diff --git a/cli/command/system/inspect.go b/cli/command/system/inspect.go
index 68e41969cb..9cbca4c421 100644
--- a/cli/command/system/inspect.go
+++ b/cli/command/system/inspect.go
@@ -11,6 +11,7 @@ import (
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
+ "github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/cli/command/inspect"
flagsHelper "github.com/docker/cli/cli/flags"
"github.com/docker/docker/api/types"
@@ -20,13 +21,42 @@ import (
"github.com/docker/docker/errdefs"
"github.com/pkg/errors"
"github.com/spf13/cobra"
+ "github.com/spf13/pflag"
)
+type objectType = string
+
+const (
+ typeConfig objectType = "config"
+ typeContainer objectType = "container"
+ typeImage objectType = "image"
+ typeNetwork objectType = "network"
+ typeNode objectType = "node"
+ typePlugin objectType = "plugin"
+ typeSecret objectType = "secret"
+ typeService objectType = "service"
+ typeTask objectType = "task"
+ typeVolume objectType = "volume"
+)
+
+var allTypes = []objectType{
+ typeConfig,
+ typeContainer,
+ typeImage,
+ typeNetwork,
+ typeNode,
+ typePlugin,
+ typeSecret,
+ typeService,
+ typeTask,
+ typeVolume,
+}
+
type inspectOptions struct {
- format string
- inspectType string
- size bool
- ids []string
+ format string
+ objectType objectType
+ size bool
+ ids []string
}
// NewInspectCommand creates a new cobra.Command for `docker inspect`
@@ -39,25 +69,38 @@ func NewInspectCommand(dockerCli command.Cli) *cobra.Command {
Args: cli.RequiresMinArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
opts.ids = args
+ if cmd.Flags().Changed("type") && opts.objectType == "" {
+ return fmt.Errorf(`type is empty: must be one of "%s"`, strings.Join(allTypes, `", "`))
+ }
return runInspect(cmd.Context(), dockerCli, opts)
},
+ // TODO(thaJeztah): should we consider adding completion for common object-types? (images, containers?)
+ ValidArgsFunction: completion.NoComplete,
}
flags := cmd.Flags()
flags.StringVarP(&opts.format, "format", "f", "", flagsHelper.InspectFormatHelp)
- flags.StringVar(&opts.inspectType, "type", "", "Return JSON for specified type")
+ flags.StringVar(&opts.objectType, "type", "", "Only inspect objects of the given type")
flags.BoolVarP(&opts.size, "size", "s", false, "Display total file sizes if the type is container")
+ _ = cmd.RegisterFlagCompletionFunc("type", completion.FromList(allTypes...))
+ flags.VisitAll(func(flag *pflag.Flag) {
+ // Set a default completion function if none was set. We don't look
+ // up if it does already have one set, because Cobra does this for
+ // us, and returns an error (which we ignore for this reason).
+ _ = cmd.RegisterFlagCompletionFunc(flag.Name, completion.NoComplete)
+ })
return cmd
}
func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions) error {
var elementSearcher inspect.GetRefFunc
- switch opts.inspectType {
- case "", "config", "container", "image", "network", "node", "plugin", "secret", "service", "task", "volume":
- elementSearcher = inspectAll(ctx, dockerCli, opts.size, opts.inspectType)
+ switch opts.objectType {
+ case "", typeConfig, typeContainer, typeImage, typeNetwork, typeNode,
+ typePlugin, typeSecret, typeService, typeTask, typeVolume:
+ elementSearcher = inspectAll(ctx, dockerCli, opts.size, opts.objectType)
default:
- return errors.Errorf("%q is not a valid value for --type", opts.inspectType)
+ return errors.Errorf(`unknown type: %q: must be one of "%s"`, opts.objectType, strings.Join(allTypes, `", "`))
}
return inspect.Inspect(dockerCli.Out(), opts.ids, opts.format, elementSearcher)
}
@@ -128,56 +171,56 @@ func inspectConfig(ctx context.Context, dockerCLI command.Cli) inspect.GetRefFun
}
}
-func inspectAll(ctx context.Context, dockerCLI command.Cli, getSize bool, typeConstraint string) inspect.GetRefFunc {
+func inspectAll(ctx context.Context, dockerCLI command.Cli, getSize bool, typeConstraint objectType) inspect.GetRefFunc {
inspectAutodetect := []struct {
- objectType string
+ objectType objectType
isSizeSupported bool
isSwarmObject bool
objectInspector func(string) (any, []byte, error)
}{
{
- objectType: "container",
+ objectType: typeContainer,
isSizeSupported: true,
objectInspector: inspectContainers(ctx, dockerCLI, getSize),
},
{
- objectType: "image",
+ objectType: typeImage,
objectInspector: inspectImages(ctx, dockerCLI),
},
{
- objectType: "network",
+ objectType: typeNetwork,
objectInspector: inspectNetwork(ctx, dockerCLI),
},
{
- objectType: "volume",
+ objectType: typeVolume,
objectInspector: inspectVolume(ctx, dockerCLI),
},
{
- objectType: "service",
+ objectType: typeService,
isSwarmObject: true,
objectInspector: inspectService(ctx, dockerCLI),
},
{
- objectType: "task",
+ objectType: typeTask,
isSwarmObject: true,
objectInspector: inspectTasks(ctx, dockerCLI),
},
{
- objectType: "node",
+ objectType: typeNode,
isSwarmObject: true,
objectInspector: inspectNode(ctx, dockerCLI),
},
{
- objectType: "plugin",
+ objectType: typePlugin,
objectInspector: inspectPlugin(ctx, dockerCLI),
},
{
- objectType: "secret",
+ objectType: typeSecret,
isSwarmObject: true,
objectInspector: inspectSecret(ctx, dockerCLI),
},
{
- objectType: "config",
+ objectType: typeConfig,
isSwarmObject: true,
objectInspector: inspectConfig(ctx, dockerCLI),
},
diff --git a/cli/command/system/inspect_test.go b/cli/command/system/inspect_test.go
new file mode 100644
index 0000000000..56ad5fd5b5
--- /dev/null
+++ b/cli/command/system/inspect_test.go
@@ -0,0 +1,48 @@
+package system
+
+import (
+ "io"
+ "testing"
+
+ "github.com/docker/cli/internal/test"
+ "gotest.tools/v3/assert"
+ is "gotest.tools/v3/assert/cmp"
+)
+
+func TestInspectValidateFlagsAndArgs(t *testing.T) {
+ for _, tc := range []struct {
+ name string
+ args []string
+ expectedErr string
+ }{
+ {
+ name: "empty type",
+ args: []string{"--type", "", "something"},
+ expectedErr: `type is empty: must be one of "config", "container", "image", "network", "node", "plugin", "secret", "service", "task", "volume"`,
+ },
+ {
+ name: "unknown type",
+ args: []string{"--type", "unknown", "something"},
+ expectedErr: `unknown type: "unknown": must be one of "config", "container", "image", "network", "node", "plugin", "secret", "service", "task", "volume"`,
+ },
+ {
+ name: "no arg",
+ args: []string{},
+ expectedErr: `inspect: 'inspect' requires at least 1 argument`,
+ },
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ cmd := NewInspectCommand(test.NewFakeCli(&fakeClient{}))
+ cmd.SetOut(io.Discard)
+ cmd.SetErr(io.Discard)
+ cmd.SetArgs(tc.args)
+
+ err := cmd.Execute()
+ if tc.expectedErr != "" {
+ assert.Check(t, is.ErrorContains(err, tc.expectedErr))
+ } else {
+ assert.Check(t, is.Nil(err))
+ }
+ })
+ }
+}
diff --git a/docs/reference/commandline/inspect.md b/docs/reference/commandline/inspect.md
index ce25827e85..c41cc05576 100644
--- a/docs/reference/commandline/inspect.md
+++ b/docs/reference/commandline/inspect.md
@@ -9,7 +9,7 @@ Return low-level information on Docker objects
|:---------------------------------------|:---------|:--------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`-f`](#format), [`--format`](#format) | `string` | | Format output using a custom template:
'json': Print in JSON format
'TEMPLATE': Print output using the given Go template.
Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates |
| [`-s`](#size), [`--size`](#size) | `bool` | | Display total file sizes if the type is container |
-| [`--type`](#type) | `string` | | Return JSON for specified type |
+| [`--type`](#type) | `string` | | Only inspect objects of the given type |