Merge pull request #6052 from thaJeztah/inspect_completion

inspect: add shell completion, improve flag-description for `--type` and improve validation
This commit is contained in:
Sebastiaan van Stijn 2025-05-06 20:29:54 +02:00 committed by GitHub
commit a2a13765f7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 113 additions and 22 deletions

View File

@ -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),
},

View File

@ -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))
}
})
}
}

View File

@ -9,7 +9,7 @@ Return low-level information on Docker objects
|:---------------------------------------|:---------|:--------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`-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 |
| [`-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 |
<!---MARKER_GEN_END-->