Skip to content

Commit 41682ac

Browse files
committed
add support for attributes exposed by docker ps
Signed-off-by: Nicolas De Loof <[email protected]>
1 parent 1054792 commit 41682ac

File tree

5 files changed

+181
-34
lines changed

5 files changed

+181
-34
lines changed

cmd/formatter/container.go

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
package formatter
1818

1919
import (
20+
"fmt"
21+
"strconv"
22+
"strings"
2023
"time"
2124

2225
"github.com/docker/cli/cli/command/formatter"
@@ -141,6 +144,22 @@ func (c *ContainerContext) Name() string {
141144
return c.c.Name
142145
}
143146

147+
// Names returns a comma-separated string of the container's names, with their
148+
// slash (/) prefix stripped. Additional names for the container (related to the
149+
// legacy `--link` feature) are omitted.
150+
func (c *ContainerContext) Names() string {
151+
names := formatter.StripNamePrefix(c.c.Names)
152+
if c.trunc {
153+
for _, name := range names {
154+
if len(strings.Split(name, "/")) == 1 {
155+
names = []string{name}
156+
break
157+
}
158+
}
159+
}
160+
return strings.Join(names, ",")
161+
}
162+
144163
func (c *ContainerContext) Service() string {
145164
return c.c.Service
146165
}
@@ -150,7 +169,11 @@ func (c *ContainerContext) Image() string {
150169
}
151170

152171
func (c *ContainerContext) Command() string {
153-
return c.c.Command
172+
command := c.c.Command
173+
if c.trunc {
174+
command = formatter.Ellipsis(command, 20)
175+
}
176+
return strconv.Quote(command)
154177
}
155178

156179
func (c *ContainerContext) CreatedAt() string {
@@ -194,3 +217,65 @@ func (c *ContainerContext) Ports() string {
194217
}
195218
return formatter.DisplayablePorts(ports)
196219
}
220+
221+
// Labels returns a comma-separated string of labels present on the container.
222+
func (c *ContainerContext) Labels() string {
223+
if c.c.Labels == nil {
224+
return ""
225+
}
226+
227+
var joinLabels []string
228+
for k, v := range c.c.Labels {
229+
joinLabels = append(joinLabels, fmt.Sprintf("%s=%s", k, v))
230+
}
231+
return strings.Join(joinLabels, ",")
232+
}
233+
234+
// Label returns the value of the label with the given name or an empty string
235+
// if the given label does not exist.
236+
func (c *ContainerContext) Label(name string) string {
237+
if c.c.Labels == nil {
238+
return ""
239+
}
240+
return c.c.Labels[name]
241+
}
242+
243+
// Mounts returns a comma-separated string of mount names present on the container.
244+
// If the trunc option is set, names can be truncated (ellipsized).
245+
func (c *ContainerContext) Mounts() string {
246+
var mounts []string
247+
for _, name := range c.c.Mounts {
248+
if c.trunc {
249+
name = formatter.Ellipsis(name, 15)
250+
}
251+
mounts = append(mounts, name)
252+
}
253+
return strings.Join(mounts, ",")
254+
}
255+
256+
// LocalVolumes returns the number of volumes using the "local" volume driver.
257+
func (c *ContainerContext) LocalVolumes() string {
258+
return fmt.Sprintf("%d", c.c.LocalVolumes)
259+
}
260+
261+
// Networks returns a comma-separated string of networks that the container is
262+
// attached to.
263+
func (c *ContainerContext) Networks() string {
264+
return strings.Join(c.c.Networks, ",")
265+
}
266+
267+
// Size returns the container's size and virtual size (e.g. "2B (virtual 21.5MB)")
268+
func (c *ContainerContext) Size() string {
269+
if c.FieldsUsed == nil {
270+
c.FieldsUsed = map[string]interface{}{}
271+
}
272+
c.FieldsUsed["Size"] = struct{}{}
273+
srw := units.HumanSizeWithPrecision(float64(c.c.SizeRw), 3)
274+
sv := units.HumanSizeWithPrecision(float64(c.c.SizeRootFs), 3)
275+
276+
sf := srw
277+
if c.c.SizeRootFs > 0 {
278+
sf = fmt.Sprintf("%s (virtual %s)", srw, sv)
279+
}
280+
return sf
281+
}

pkg/api/api.go

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -390,18 +390,25 @@ type PortPublisher struct {
390390

391391
// ContainerSummary hold high-level description of a container
392392
type ContainerSummary struct {
393-
ID string
394-
Name string
395-
Image string
396-
Command string
397-
Project string
398-
Service string
399-
Created int64
400-
State string
401-
Status string
402-
Health string
403-
ExitCode int
404-
Publishers PortPublishers
393+
ID string
394+
Name string
395+
Names []string
396+
Image string
397+
Command string
398+
Project string
399+
Service string
400+
Created int64
401+
State string
402+
Status string
403+
Health string
404+
ExitCode int
405+
Publishers PortPublishers
406+
Labels map[string]string
407+
SizeRw int64 `json:",omitempty"`
408+
SizeRootFs int64 `json:",omitempty"`
409+
Mounts []string
410+
Networks []string
411+
LocalVolumes int
405412
}
406413

407414
// PortPublishers is a slice of PortPublisher

pkg/compose/ps.go

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -78,19 +78,48 @@ func (s *composeService) Ps(ctx context.Context, projectName string, options api
7878
}
7979
}
8080

81+
var (
82+
local int
83+
mounts []string
84+
)
85+
for _, m := range container.Mounts {
86+
name := m.Name
87+
if name == "" {
88+
name = m.Source
89+
}
90+
if m.Driver == "local" {
91+
local++
92+
}
93+
mounts = append(mounts, name)
94+
}
95+
96+
var networks []string
97+
if container.NetworkSettings != nil {
98+
for k := range container.NetworkSettings.Networks {
99+
networks = append(networks, k)
100+
}
101+
}
102+
81103
summary[i] = api.ContainerSummary{
82-
ID: container.ID,
83-
Name: getCanonicalContainerName(container),
84-
Image: container.Image,
85-
Project: container.Labels[api.ProjectLabel],
86-
Service: container.Labels[api.ServiceLabel],
87-
Command: container.Command,
88-
State: container.State,
89-
Status: container.Status,
90-
Created: container.Created,
91-
Health: health,
92-
ExitCode: exitCode,
93-
Publishers: publishers,
104+
ID: container.ID,
105+
Name: getCanonicalContainerName(container),
106+
Names: container.Names,
107+
Image: container.Image,
108+
Project: container.Labels[api.ProjectLabel],
109+
Service: container.Labels[api.ServiceLabel],
110+
Command: container.Command,
111+
State: container.State,
112+
Status: container.Status,
113+
Created: container.Created,
114+
Labels: container.Labels,
115+
SizeRw: container.SizeRw,
116+
SizeRootFs: container.SizeRootFs,
117+
Mounts: mounts,
118+
LocalVolumes: local,
119+
Networks: networks,
120+
Health: health,
121+
ExitCode: exitCode,
122+
Publishers: publishers,
94123
}
95124
return nil
96125
})

pkg/compose/ps_test.go

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,34 @@ func TestPs(t *testing.T) {
5454
containers, err := tested.Ps(ctx, strings.ToLower(testProject), compose.PsOptions{})
5555

5656
expected := []compose.ContainerSummary{
57-
{ID: "123", Name: "123", Image: "foo", Project: strings.ToLower(testProject), Service: "service1",
58-
State: "running", Health: "healthy", Publishers: nil},
59-
{ID: "456", Name: "456", Image: "foo", Project: strings.ToLower(testProject), Service: "service1",
57+
{ID: "123", Name: "123", Names: []string{"/123"}, Image: "foo", Project: strings.ToLower(testProject), Service: "service1",
58+
State: "running", Health: "healthy", Publishers: nil,
59+
Labels: map[string]string{
60+
compose.ProjectLabel: strings.ToLower(testProject),
61+
compose.ConfigFilesLabel: "/src/pkg/compose/testdata/compose.yaml",
62+
compose.WorkingDirLabel: "/src/pkg/compose/testdata",
63+
compose.ServiceLabel: "service1",
64+
},
65+
},
66+
{ID: "456", Name: "456", Names: []string{"/456"}, Image: "foo", Project: strings.ToLower(testProject), Service: "service1",
6067
State: "running", Health: "",
61-
Publishers: []compose.PortPublisher{{URL: "localhost", TargetPort: 90, PublishedPort: 80}}},
62-
{ID: "789", Name: "789", Image: "foo", Project: strings.ToLower(testProject), Service: "service2",
63-
State: "exited", Health: "", ExitCode: 130, Publishers: nil},
68+
Publishers: []compose.PortPublisher{{URL: "localhost", TargetPort: 90, PublishedPort: 80}},
69+
Labels: map[string]string{
70+
compose.ProjectLabel: strings.ToLower(testProject),
71+
compose.ConfigFilesLabel: "/src/pkg/compose/testdata/compose.yaml",
72+
compose.WorkingDirLabel: "/src/pkg/compose/testdata",
73+
compose.ServiceLabel: "service1",
74+
},
75+
},
76+
{ID: "789", Name: "789", Names: []string{"/789"}, Image: "foo", Project: strings.ToLower(testProject), Service: "service2",
77+
State: "exited", Health: "", ExitCode: 130, Publishers: nil,
78+
Labels: map[string]string{
79+
compose.ProjectLabel: strings.ToLower(testProject),
80+
compose.ConfigFilesLabel: "/src/pkg/compose/testdata/compose.yaml",
81+
compose.WorkingDirLabel: "/src/pkg/compose/testdata",
82+
compose.ServiceLabel: "service2",
83+
},
84+
},
6485
}
6586
assert.NilError(t, err)
6687
assert.DeepEqual(t, containers, expected)

pkg/e2e/ps_test.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,15 @@ func TestPs(t *testing.T) {
6262
t.Run("json", func(t *testing.T) {
6363
res = c.RunDockerComposeCmd(t, "-f", "./fixtures/ps-test/compose.yaml", "--project-name", projectName, "ps",
6464
"--format", "json")
65-
var output []api.ContainerSummary
66-
dec := json.NewDecoder(strings.NewReader(res.Stdout()))
65+
type element struct {
66+
Name string
67+
Publishers api.PortPublishers
68+
}
69+
var output []element
70+
out := res.Stdout()
71+
dec := json.NewDecoder(strings.NewReader(out))
6772
for dec.More() {
68-
var s api.ContainerSummary
73+
var s element
6974
require.NoError(t, dec.Decode(&s), "Failed to unmarshal ps JSON output")
7075
output = append(output, s)
7176
}

0 commit comments

Comments
 (0)