cli/command/system: add shell completion for "docker inspect"

The "docker inspect" command can inspect any type of object, which would
require all possible endpoints to be contacted. By default, we don't
provide completion, but if a `--type` is passed, we provide completion
for the given type.

For example, `docker inspect --type container` will complete container
names, `docker inspect --type volume` will complete volume names and
so on.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2025-09-10 00:25:24 +02:00
parent f10041c724
commit 8bb5595f28
2 changed files with 121 additions and 1 deletions

View File

@ -1,10 +1,14 @@
package system
import (
"fmt"
"strings"
"github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/cli/command/idresolver"
"github.com/moby/moby/api/types/events"
"github.com/moby/moby/api/types/filters"
"github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@ -157,6 +161,20 @@ func validEventNames() []string {
return names
}
// configNames contacts the API to get a list of config names.
// In case of an error, an empty list is returned.
func configNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []string {
list, err := dockerCLI.Client().ConfigList(cmd.Context(), client.ConfigListOptions{})
if err != nil {
return []string{}
}
names := make([]string, 0, len(list))
for _, v := range list {
names = append(names, v.Spec.Name)
}
return names
}
// containerNames contacts the API to get names and optionally IDs of containers.
// In case of an error, an empty list is returned.
func containerNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command, args []string, toComplete string) []string {
@ -219,6 +237,72 @@ func nodeNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []str
return names
}
// pluginNames contacts the API to get a list of plugin names.
// In case of an error, an empty list is returned.
func pluginNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []string {
list, err := dockerCLI.Client().PluginList(cmd.Context(), filters.Args{})
if err != nil {
return []string{}
}
names := make([]string, 0, len(list))
for _, v := range list {
names = append(names, v.Name)
}
return names
}
// secretNames contacts the API to get a list of secret names.
// In case of an error, an empty list is returned.
func secretNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []string {
list, err := dockerCLI.Client().SecretList(cmd.Context(), client.SecretListOptions{})
if err != nil {
return []string{}
}
names := make([]string, 0, len(list))
for _, v := range list {
names = append(names, v.Spec.Name)
}
return names
}
// serviceNames contacts the API to get a list of service names.
// In case of an error, an empty list is returned.
func serviceNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []string {
list, err := dockerCLI.Client().ServiceList(cmd.Context(), client.ServiceListOptions{})
if err != nil {
return []string{}
}
names := make([]string, 0, len(list))
for _, v := range list {
names = append(names, v.Spec.Name)
}
return names
}
// taskNames contacts the API to get a list of service names.
// In case of an error, an empty list is returned.
func taskNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []string {
list, err := dockerCLI.Client().TaskList(cmd.Context(), client.TaskListOptions{})
if err != nil || len(list) == 0 {
return []string{}
}
resolver := idresolver.New(dockerCLI.Client(), false)
names := make([]string, 0, len(list))
for _, task := range list {
serviceName, err := resolver.Resolve(cmd.Context(), swarm.Service{}, task.ServiceID)
if err != nil {
continue
}
if task.Slot != 0 {
names = append(names, fmt.Sprintf("%v.%v", serviceName, task.Slot))
} else {
names = append(names, fmt.Sprintf("%v.%v", serviceName, task.NodeID))
}
}
return names
}
// volumeNames contacts the API to get a list of volume names.
// In case of an error, an empty list is returned.
func volumeNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []string {
@ -232,3 +316,39 @@ func volumeNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []s
}
return names
}
// completeObjectNames completes names of objects based on the "--type" flag
//
// TODO(thaJeztah): completion functions in this package don't remove names that have already been completed
// this causes completion to continue even if a given name was already completed.
func completeObjectNames(dockerCLI completion.APIClientProvider) cobra.CompletionFunc {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if f := cmd.Flags().Lookup("type"); f != nil && f.Changed {
switch f.Value.String() {
case typeConfig:
return configNames(dockerCLI, cmd), cobra.ShellCompDirectiveNoFileComp
case typeContainer:
return containerNames(dockerCLI, cmd, args, toComplete), cobra.ShellCompDirectiveNoFileComp
case typeImage:
return imageNames(dockerCLI, cmd), cobra.ShellCompDirectiveNoFileComp
case typeNetwork:
return networkNames(dockerCLI, cmd), cobra.ShellCompDirectiveNoFileComp
case typeNode:
return nodeNames(dockerCLI, cmd), cobra.ShellCompDirectiveNoFileComp
case typePlugin:
return pluginNames(dockerCLI, cmd), cobra.ShellCompDirectiveNoFileComp
case typeSecret:
return secretNames(dockerCLI, cmd), cobra.ShellCompDirectiveNoFileComp
case typeService:
return serviceNames(dockerCLI, cmd), cobra.ShellCompDirectiveNoFileComp
case typeTask:
return taskNames(dockerCLI, cmd), cobra.ShellCompDirectiveNoFileComp
case typeVolume:
return volumeNames(dockerCLI, cmd), cobra.ShellCompDirectiveNoFileComp
default:
return nil, cobra.ShellCompDirectiveNoFileComp
}
}
return nil, cobra.ShellCompDirectiveNoFileComp
}
}

View File

@ -73,7 +73,7 @@ func newInspectCommand(dockerCLI command.Cli) *cobra.Command {
return runInspect(cmd.Context(), dockerCLI, opts)
},
// TODO(thaJeztah): should we consider adding completion for common object-types? (images, containers?)
ValidArgsFunction: cobra.NoFileCompletions,
ValidArgsFunction: completeObjectNames(dockerCLI),
DisableFlagsInUseLine: true,
}