diff --git a/cli/command/system/completion.go b/cli/command/system/completion.go index 8b3c1fc5b..544a0e029 100644 --- a/cli/command/system/completion.go +++ b/cli/command/system/completion.go @@ -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 + } +} diff --git a/cli/command/system/inspect.go b/cli/command/system/inspect.go index f248314bc..8fa11ea3f 100644 --- a/cli/command/system/inspect.go +++ b/cli/command/system/inspect.go @@ -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, }