Skip to content

Commit b4a1f61

Browse files
committed
feat(ps): add ENGINE column gated on label presence
Show an ENGINE column in `compose ps` default table output when the com.docker.compose.engine label is present. Expose {{.Engine}} for custom --format templates. Rename api.EngineLabel to api.ContainerEngineLabel to avoid the name collision with desktop.EngineLabel (com.docker.desktop.address). Signed-off-by: Nick Sieger <nick@nicksieger.com>
1 parent 2e0368f commit b4a1f61

4 files changed

Lines changed: 100 additions & 2 deletions

File tree

cmd/compose/ps.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,13 @@ func runPs(ctx context.Context, dockerCli command.Cli, backendOptions *BackendOp
156156
opts.Format = dockerCli.ConfigFile().PsFormat
157157
}
158158

159+
showEngine := slices.ContainsFunc(containers, func(c api.ContainerSummary) bool {
160+
return c.Labels[api.ContainerEngineLabel] != ""
161+
})
162+
159163
containerCtx := cliformatter.Context{
160164
Output: dockerCli.Out(),
161-
Format: formatter.NewContainerFormat(opts.Format, opts.Quiet, false),
165+
Format: formatter.NewContainerFormat(opts.Format, opts.Quiet, false, showEngine),
162166
Trunc: !opts.noTrunc,
163167
}
164168
return formatter.ContainerWrite(containerCtx, containers)

cmd/formatter/container.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,20 @@ const (
4242
mountsHeader = "MOUNTS"
4343
localVolumes = "LOCAL VOLUMES"
4444
networksHeader = "NETWORKS"
45+
engineHeader = "ENGINE"
4546
)
4647

4748
// NewContainerFormat returns a Format for rendering using a Context
48-
func NewContainerFormat(source string, quiet bool, size bool) formatter.Format {
49+
func NewContainerFormat(source string, quiet bool, size bool, engine bool) formatter.Format {
4950
switch source {
5051
case formatter.TableFormatKey, "": // table formatting is the default if none is set.
5152
if quiet {
5253
return formatter.DefaultQuietFormat
5354
}
5455
format := defaultContainerTableFormat
56+
if engine {
57+
format += `\t{{.Engine}}`
58+
}
5559
if size {
5660
format += `\t{{.Size}}`
5761
}
@@ -126,6 +130,7 @@ func NewContainerContext() *ContainerContext {
126130
"Status": formatter.StatusHeader,
127131
"Size": formatter.SizeHeader,
128132
"Labels": formatter.LabelsHeader,
133+
"Engine": engineHeader,
129134
}
130135
return &containerCtx
131136
}
@@ -245,6 +250,15 @@ func (c *ContainerContext) Labels() string {
245250
return strings.Join(joinLabels, ",")
246251
}
247252

253+
// Engine returns the name of the engine that runs the container, as stored in
254+
// the com.docker.compose.engine label, or an empty string if unset.
255+
func (c *ContainerContext) Engine() string {
256+
if c.c.Labels == nil {
257+
return ""
258+
}
259+
return c.c.Labels[api.ContainerEngineLabel]
260+
}
261+
248262
// Label returns the value of the label with the given name or an empty string
249263
// if the given label does not exist.
250264
func (c *ContainerContext) Label(name string) string {

cmd/formatter/container_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
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 formatter
18+
19+
import (
20+
"bytes"
21+
"strings"
22+
"testing"
23+
24+
"github.com/docker/cli/cli/command/formatter"
25+
"gotest.tools/v3/assert"
26+
27+
"github.com/docker/compose/v5/pkg/api"
28+
)
29+
30+
func TestContainerContextEngine(t *testing.T) {
31+
withLabel := ContainerContext{c: api.ContainerSummary{
32+
Labels: map[string]string{api.ContainerEngineLabel: "moby"},
33+
}}
34+
assert.Equal(t, withLabel.Engine(), "moby")
35+
36+
noLabels := ContainerContext{c: api.ContainerSummary{}}
37+
assert.Equal(t, noLabels.Engine(), "")
38+
39+
otherLabel := ContainerContext{c: api.ContainerSummary{
40+
Labels: map[string]string{api.ProjectLabel: "test"},
41+
}}
42+
assert.Equal(t, otherLabel.Engine(), "")
43+
}
44+
45+
func TestContainerWriteEngineColumn(t *testing.T) {
46+
containers := []api.ContainerSummary{
47+
{
48+
Name: "with-engine",
49+
Service: "svc1",
50+
Labels: map[string]string{api.ContainerEngineLabel: "moby"},
51+
},
52+
{
53+
Name: "without-engine",
54+
Service: "svc2",
55+
},
56+
}
57+
58+
t.Run("engine column shown when label present", func(t *testing.T) {
59+
var out bytes.Buffer
60+
ctx := formatter.Context{
61+
Output: &out,
62+
Format: NewContainerFormat("table", false, false, true),
63+
}
64+
assert.NilError(t, ContainerWrite(ctx, containers))
65+
assert.Assert(t, strings.Contains(out.String(), engineHeader), out.String())
66+
assert.Assert(t, strings.Contains(out.String(), "moby"), out.String())
67+
})
68+
69+
t.Run("engine column hidden when not requested", func(t *testing.T) {
70+
var out bytes.Buffer
71+
ctx := formatter.Context{
72+
Output: &out,
73+
Format: NewContainerFormat("table", false, false, false),
74+
}
75+
assert.NilError(t, ContainerWrite(ctx, containers))
76+
assert.Assert(t, !strings.Contains(out.String(), engineHeader), out.String())
77+
})
78+
}

pkg/api/labels.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ const (
6060
ImageBuilderLabel = "com.docker.compose.image.builder"
6161
// ContainerReplaceLabel is set when container is created to replace another container (recreated)
6262
ContainerReplaceLabel = "com.docker.compose.replace"
63+
// ContainerEngineLabel stores the name of the engine that runs the container
64+
ContainerEngineLabel = "com.docker.compose.engine"
6365
)
6466

6567
// ComposeVersion is the compose tool version as declared by label VersionLabel

0 commit comments

Comments
 (0)