Skip to content

Commit b2c0d25

Browse files
committed
ps: use DisplayablePorts from docker/cli
Fixes docker#9527. Signed-off-by: Nick Sieger <[email protected]>
1 parent 3599fc8 commit b2c0d25

File tree

2 files changed

+96
-63
lines changed

2 files changed

+96
-63
lines changed

cmd/compose/ps.go

Lines changed: 12 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727

2828
"github.com/docker/compose/v2/cmd/formatter"
2929
"github.com/docker/compose/v2/pkg/utils"
30+
"github.com/docker/docker/api/types"
3031

3132
formatter2 "github.com/docker/cli/cli/command/formatter"
3233
"github.com/pkg/errors"
@@ -146,7 +147,7 @@ SERVICES:
146147
func writter(containers []api.ContainerSummary) func(w io.Writer) {
147148
return func(w io.Writer) {
148149
for _, container := range containers {
149-
ports := DisplayablePorts(container)
150+
ports := displayablePorts(container)
150151
status := container.State
151152
if status == "running" && container.Health != "" {
152153
status = fmt.Sprintf("%s (%s)", container.State, container.Health)
@@ -178,72 +179,20 @@ func hasStatus(c api.ContainerSummary, statuses []string) bool {
178179
return false
179180
}
180181

181-
type portRange struct {
182-
pStart int
183-
pEnd int
184-
tStart int
185-
tEnd int
186-
IP string
187-
protocol string
188-
}
189-
190-
func (pr portRange) String() string {
191-
var (
192-
pub string
193-
tgt string
194-
)
195-
196-
if pr.pEnd > pr.pStart {
197-
pub = fmt.Sprintf("%s:%d-%d->", pr.IP, pr.pStart, pr.pEnd)
198-
} else if pr.pStart > 0 {
199-
pub = fmt.Sprintf("%s:%d->", pr.IP, pr.pStart)
200-
}
201-
if pr.tEnd > pr.tStart {
202-
tgt = fmt.Sprintf("%d-%d", pr.tStart, pr.tEnd)
203-
} else {
204-
tgt = fmt.Sprintf("%d", pr.tStart)
205-
}
206-
return fmt.Sprintf("%s%s/%s", pub, tgt, pr.protocol)
207-
}
208-
209-
// DisplayablePorts is copy pasted from https://github.com/docker/cli/pull/581/files
210-
func DisplayablePorts(c api.ContainerSummary) string {
182+
func displayablePorts(c api.ContainerSummary) string {
211183
if c.Publishers == nil {
212184
return ""
213185
}
214186

215-
sort.Sort(c.Publishers)
216-
217-
pr := portRange{}
218-
ports := []string{}
219-
for _, p := range c.Publishers {
220-
prIsRange := pr.tEnd != pr.tStart
221-
tOverlaps := p.TargetPort <= pr.tEnd
222-
223-
// Start a new port-range if:
224-
// - the protocol is different from the current port-range
225-
// - published or target port are not consecutive to the current port-range
226-
// - the current port-range is a _range_, and the target port overlaps with the current range's target-ports
227-
if p.Protocol != pr.protocol || p.URL != pr.IP || p.PublishedPort-pr.pEnd > 1 || p.TargetPort-pr.tEnd > 1 || prIsRange && tOverlaps {
228-
// start a new port-range, and print the previous port-range (if any)
229-
if pr.pStart > 0 {
230-
ports = append(ports, pr.String())
231-
}
232-
pr = portRange{
233-
pStart: p.PublishedPort,
234-
pEnd: p.PublishedPort,
235-
tStart: p.TargetPort,
236-
tEnd: p.TargetPort,
237-
protocol: p.Protocol,
238-
IP: p.URL,
239-
}
240-
continue
187+
ports := make([]types.Port, len(c.Publishers))
188+
for i, pub := range c.Publishers {
189+
ports[i] = types.Port{
190+
IP: pub.URL,
191+
PrivatePort: uint16(pub.TargetPort),
192+
PublicPort: uint16(pub.PublishedPort),
193+
Type: pub.Protocol,
241194
}
242-
pr.pEnd = p.PublishedPort
243-
pr.tEnd = p.TargetPort
244-
}
245-
if pr.tStart > 0 {
246-
ports = append(ports, pr.String())
247195
}
248-
return strings.Join(ports, ", ")
196+
197+
return formatter2.DisplayablePorts(ports)
249198
}

cmd/compose/ps_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
Copyright 2020 Docker Compose CLI authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package compose
18+
19+
import (
20+
"context"
21+
"os"
22+
"path/filepath"
23+
"testing"
24+
25+
"github.com/docker/compose/v2/pkg/api"
26+
"github.com/docker/compose/v2/pkg/mocks"
27+
"github.com/golang/mock/gomock"
28+
"github.com/stretchr/testify/assert"
29+
)
30+
31+
func TestPsPretty(t *testing.T) {
32+
ctx := context.Background()
33+
origStdout := os.Stdout
34+
t.Cleanup(func() {
35+
os.Stdout = origStdout
36+
})
37+
dir := t.TempDir()
38+
f, err := os.Create(filepath.Join(dir, "output.txt"))
39+
if err != nil {
40+
t.Fatal("could not create output file")
41+
}
42+
defer func() { _ = f.Close() }()
43+
44+
os.Stdout = f
45+
ctrl := gomock.NewController(t)
46+
defer ctrl.Finish()
47+
48+
backend := mocks.NewMockService(ctrl)
49+
backend.EXPECT().
50+
Ps(gomock.Eq(ctx), gomock.Any(), gomock.Any()).
51+
DoAndReturn(func(ctx context.Context, projectName string, options api.PsOptions) ([]api.ContainerSummary, error) {
52+
return []api.ContainerSummary{
53+
{
54+
ID: "abc123",
55+
Name: "ABC",
56+
Publishers: api.PortPublishers{
57+
{
58+
TargetPort: 8080,
59+
PublishedPort: 8080,
60+
Protocol: "tcp",
61+
},
62+
{
63+
TargetPort: 8443,
64+
PublishedPort: 8443,
65+
Protocol: "tcp",
66+
},
67+
},
68+
},
69+
}, nil
70+
}).AnyTimes()
71+
72+
opts := psOptions{projectOptions: &projectOptions{ProjectName: "test"}}
73+
err = runPs(ctx, backend, nil, opts)
74+
assert.NoError(t, err)
75+
76+
_, err = f.Seek(0, 0)
77+
assert.NoError(t, err)
78+
79+
output := make([]byte, 256)
80+
_, err = f.Read(output)
81+
assert.NoError(t, err)
82+
83+
assert.Contains(t, string(output), "8080/tcp, 8443/tcp")
84+
}

0 commit comments

Comments
 (0)