Skip to content

Commit a2a1376

Browse files
authored
Merge pull request docker#6052 from thaJeztah/inspect_completion
inspect: add shell completion, improve flag-description for `--type` and improve validation
2 parents 12c0c13 + 52752f3 commit a2a1376

File tree

3 files changed

+113
-22
lines changed

3 files changed

+113
-22
lines changed

cli/command/system/inspect.go

Lines changed: 64 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"github.com/docker/cli/cli"
1313
"github.com/docker/cli/cli/command"
14+
"github.com/docker/cli/cli/command/completion"
1415
"github.com/docker/cli/cli/command/inspect"
1516
flagsHelper "github.com/docker/cli/cli/flags"
1617
"github.com/docker/docker/api/types"
@@ -20,13 +21,42 @@ import (
2021
"github.com/docker/docker/errdefs"
2122
"github.com/pkg/errors"
2223
"github.com/spf13/cobra"
24+
"github.com/spf13/pflag"
2325
)
2426

27+
type objectType = string
28+
29+
const (
30+
typeConfig objectType = "config"
31+
typeContainer objectType = "container"
32+
typeImage objectType = "image"
33+
typeNetwork objectType = "network"
34+
typeNode objectType = "node"
35+
typePlugin objectType = "plugin"
36+
typeSecret objectType = "secret"
37+
typeService objectType = "service"
38+
typeTask objectType = "task"
39+
typeVolume objectType = "volume"
40+
)
41+
42+
var allTypes = []objectType{
43+
typeConfig,
44+
typeContainer,
45+
typeImage,
46+
typeNetwork,
47+
typeNode,
48+
typePlugin,
49+
typeSecret,
50+
typeService,
51+
typeTask,
52+
typeVolume,
53+
}
54+
2555
type inspectOptions struct {
26-
format string
27-
inspectType string
28-
size bool
29-
ids []string
56+
format string
57+
objectType objectType
58+
size bool
59+
ids []string
3060
}
3161

3262
// NewInspectCommand creates a new cobra.Command for `docker inspect`
@@ -39,25 +69,38 @@ func NewInspectCommand(dockerCli command.Cli) *cobra.Command {
3969
Args: cli.RequiresMinArgs(1),
4070
RunE: func(cmd *cobra.Command, args []string) error {
4171
opts.ids = args
72+
if cmd.Flags().Changed("type") && opts.objectType == "" {
73+
return fmt.Errorf(`type is empty: must be one of "%s"`, strings.Join(allTypes, `", "`))
74+
}
4275
return runInspect(cmd.Context(), dockerCli, opts)
4376
},
77+
// TODO(thaJeztah): should we consider adding completion for common object-types? (images, containers?)
78+
ValidArgsFunction: completion.NoComplete,
4479
}
4580

4681
flags := cmd.Flags()
4782
flags.StringVarP(&opts.format, "format", "f", "", flagsHelper.InspectFormatHelp)
48-
flags.StringVar(&opts.inspectType, "type", "", "Return JSON for specified type")
83+
flags.StringVar(&opts.objectType, "type", "", "Only inspect objects of the given type")
4984
flags.BoolVarP(&opts.size, "size", "s", false, "Display total file sizes if the type is container")
5085

86+
_ = cmd.RegisterFlagCompletionFunc("type", completion.FromList(allTypes...))
87+
flags.VisitAll(func(flag *pflag.Flag) {
88+
// Set a default completion function if none was set. We don't look
89+
// up if it does already have one set, because Cobra does this for
90+
// us, and returns an error (which we ignore for this reason).
91+
_ = cmd.RegisterFlagCompletionFunc(flag.Name, completion.NoComplete)
92+
})
5193
return cmd
5294
}
5395

5496
func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions) error {
5597
var elementSearcher inspect.GetRefFunc
56-
switch opts.inspectType {
57-
case "", "config", "container", "image", "network", "node", "plugin", "secret", "service", "task", "volume":
58-
elementSearcher = inspectAll(ctx, dockerCli, opts.size, opts.inspectType)
98+
switch opts.objectType {
99+
case "", typeConfig, typeContainer, typeImage, typeNetwork, typeNode,
100+
typePlugin, typeSecret, typeService, typeTask, typeVolume:
101+
elementSearcher = inspectAll(ctx, dockerCli, opts.size, opts.objectType)
59102
default:
60-
return errors.Errorf("%q is not a valid value for --type", opts.inspectType)
103+
return errors.Errorf(`unknown type: %q: must be one of "%s"`, opts.objectType, strings.Join(allTypes, `", "`))
61104
}
62105
return inspect.Inspect(dockerCli.Out(), opts.ids, opts.format, elementSearcher)
63106
}
@@ -128,56 +171,56 @@ func inspectConfig(ctx context.Context, dockerCLI command.Cli) inspect.GetRefFun
128171
}
129172
}
130173

131-
func inspectAll(ctx context.Context, dockerCLI command.Cli, getSize bool, typeConstraint string) inspect.GetRefFunc {
174+
func inspectAll(ctx context.Context, dockerCLI command.Cli, getSize bool, typeConstraint objectType) inspect.GetRefFunc {
132175
inspectAutodetect := []struct {
133-
objectType string
176+
objectType objectType
134177
isSizeSupported bool
135178
isSwarmObject bool
136179
objectInspector func(string) (any, []byte, error)
137180
}{
138181
{
139-
objectType: "container",
182+
objectType: typeContainer,
140183
isSizeSupported: true,
141184
objectInspector: inspectContainers(ctx, dockerCLI, getSize),
142185
},
143186
{
144-
objectType: "image",
187+
objectType: typeImage,
145188
objectInspector: inspectImages(ctx, dockerCLI),
146189
},
147190
{
148-
objectType: "network",
191+
objectType: typeNetwork,
149192
objectInspector: inspectNetwork(ctx, dockerCLI),
150193
},
151194
{
152-
objectType: "volume",
195+
objectType: typeVolume,
153196
objectInspector: inspectVolume(ctx, dockerCLI),
154197
},
155198
{
156-
objectType: "service",
199+
objectType: typeService,
157200
isSwarmObject: true,
158201
objectInspector: inspectService(ctx, dockerCLI),
159202
},
160203
{
161-
objectType: "task",
204+
objectType: typeTask,
162205
isSwarmObject: true,
163206
objectInspector: inspectTasks(ctx, dockerCLI),
164207
},
165208
{
166-
objectType: "node",
209+
objectType: typeNode,
167210
isSwarmObject: true,
168211
objectInspector: inspectNode(ctx, dockerCLI),
169212
},
170213
{
171-
objectType: "plugin",
214+
objectType: typePlugin,
172215
objectInspector: inspectPlugin(ctx, dockerCLI),
173216
},
174217
{
175-
objectType: "secret",
218+
objectType: typeSecret,
176219
isSwarmObject: true,
177220
objectInspector: inspectSecret(ctx, dockerCLI),
178221
},
179222
{
180-
objectType: "config",
223+
objectType: typeConfig,
181224
isSwarmObject: true,
182225
objectInspector: inspectConfig(ctx, dockerCLI),
183226
},

cli/command/system/inspect_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package system
2+
3+
import (
4+
"io"
5+
"testing"
6+
7+
"github.com/docker/cli/internal/test"
8+
"gotest.tools/v3/assert"
9+
is "gotest.tools/v3/assert/cmp"
10+
)
11+
12+
func TestInspectValidateFlagsAndArgs(t *testing.T) {
13+
for _, tc := range []struct {
14+
name string
15+
args []string
16+
expectedErr string
17+
}{
18+
{
19+
name: "empty type",
20+
args: []string{"--type", "", "something"},
21+
expectedErr: `type is empty: must be one of "config", "container", "image", "network", "node", "plugin", "secret", "service", "task", "volume"`,
22+
},
23+
{
24+
name: "unknown type",
25+
args: []string{"--type", "unknown", "something"},
26+
expectedErr: `unknown type: "unknown": must be one of "config", "container", "image", "network", "node", "plugin", "secret", "service", "task", "volume"`,
27+
},
28+
{
29+
name: "no arg",
30+
args: []string{},
31+
expectedErr: `inspect: 'inspect' requires at least 1 argument`,
32+
},
33+
} {
34+
t.Run(tc.name, func(t *testing.T) {
35+
cmd := NewInspectCommand(test.NewFakeCli(&fakeClient{}))
36+
cmd.SetOut(io.Discard)
37+
cmd.SetErr(io.Discard)
38+
cmd.SetArgs(tc.args)
39+
40+
err := cmd.Execute()
41+
if tc.expectedErr != "" {
42+
assert.Check(t, is.ErrorContains(err, tc.expectedErr))
43+
} else {
44+
assert.Check(t, is.Nil(err))
45+
}
46+
})
47+
}
48+
}

docs/reference/commandline/inspect.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Return low-level information on Docker objects
99
|:---------------------------------------|:---------|:--------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
1010
| [`-f`](#format), [`--format`](#format) | `string` | | Format output using a custom template:<br>'json': Print in JSON format<br>'TEMPLATE': Print output using the given Go template.<br>Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates |
1111
| [`-s`](#size), [`--size`](#size) | `bool` | | Display total file sizes if the type is container |
12-
| [`--type`](#type) | `string` | | Return JSON for specified type |
12+
| [`--type`](#type) | `string` | | Only inspect objects of the given type |
1313

1414

1515
<!---MARKER_GEN_END-->

0 commit comments

Comments
 (0)