From eed0e5b02a7580173cc595905f71d30354559b14 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 4 Jul 2024 21:05:07 +0200 Subject: [PATCH 01/11] cli/command/container: NewRunCommand: slight cleanup of completion - explicitly suppress unhandled errors - remove names for unused arguments Signed-off-by: Sebastiaan van Stijn --- cli/command/container/run.go | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/cli/command/container/run.go b/cli/command/container/run.go index 562e8029208b..14322ef3d2bd 100644 --- a/cli/command/container/run.go +++ b/cli/command/container/run.go @@ -70,22 +70,13 @@ func NewRunCommand(dockerCli command.Cli) *cobra.Command { command.AddTrustVerificationFlags(flags, &options.untrusted, dockerCli.ContentTrustEnabled()) copts = addFlags(flags) - cmd.RegisterFlagCompletionFunc( - "env", - func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return os.Environ(), cobra.ShellCompDirectiveNoFileComp - }, - ) - cmd.RegisterFlagCompletionFunc( - "env-file", - func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return nil, cobra.ShellCompDirectiveDefault - }, - ) - cmd.RegisterFlagCompletionFunc( - "network", - completion.NetworkNames(dockerCli), - ) + _ = cmd.RegisterFlagCompletionFunc("env", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { + return os.Environ(), cobra.ShellCompDirectiveNoFileComp + }) + _ = cmd.RegisterFlagCompletionFunc("env-file", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { + return nil, cobra.ShellCompDirectiveDefault + }) + _ = cmd.RegisterFlagCompletionFunc("network", completion.NetworkNames(dockerCli)) return cmd } From 9207ff104681c56a8940ecb32c5080fa4c024444 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 5 Jul 2024 13:42:46 +0200 Subject: [PATCH 02/11] cli/command/completion: add FileNames utility This is just a convenience function to allow defining completion to use the default (complete with filenames and directories). Signed-off-by: Sebastiaan van Stijn --- cli/command/completion/functions.go | 7 +++++++ cli/command/container/exec.go | 4 +--- cli/command/container/run.go | 4 +--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/cli/command/completion/functions.go b/cli/command/completion/functions.go index ac2335d9ac81..7777cb84278c 100644 --- a/cli/command/completion/functions.go +++ b/cli/command/completion/functions.go @@ -105,6 +105,13 @@ func NetworkNames(dockerCLI APIClientProvider) ValidArgsFn { } } +// FileNames is a convenience function to use [cobra.ShellCompDirectiveDefault], +// which indicates to let the shell perform its default behavior after +// completions have been provided. +func FileNames(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { + return nil, cobra.ShellCompDirectiveDefault +} + // NoComplete is used for commands where there's no relevant completion func NoComplete(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { return nil, cobra.ShellCompDirectiveNoFileComp diff --git a/cli/command/container/exec.go b/cli/command/container/exec.go index a459c01388cd..1f5506ae2a7d 100644 --- a/cli/command/container/exec.go +++ b/cli/command/container/exec.go @@ -81,9 +81,7 @@ func NewExecCommand(dockerCli command.Cli) *cobra.Command { _ = cmd.RegisterFlagCompletionFunc("env", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { return os.Environ(), cobra.ShellCompDirectiveNoFileComp }) - _ = cmd.RegisterFlagCompletionFunc("env-file", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { - return nil, cobra.ShellCompDirectiveDefault // _filedir - }) + _ = cmd.RegisterFlagCompletionFunc("env-file", completion.FileNames) return cmd } diff --git a/cli/command/container/run.go b/cli/command/container/run.go index 14322ef3d2bd..bdf8eff5bef4 100644 --- a/cli/command/container/run.go +++ b/cli/command/container/run.go @@ -73,9 +73,7 @@ func NewRunCommand(dockerCli command.Cli) *cobra.Command { _ = cmd.RegisterFlagCompletionFunc("env", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { return os.Environ(), cobra.ShellCompDirectiveNoFileComp }) - _ = cmd.RegisterFlagCompletionFunc("env-file", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { - return nil, cobra.ShellCompDirectiveDefault - }) + _ = cmd.RegisterFlagCompletionFunc("env-file", completion.FileNames) _ = cmd.RegisterFlagCompletionFunc("network", completion.NetworkNames(dockerCli)) return cmd } From e3427f341b9dec5c7556024f8a5e57869e461299 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 5 Jul 2024 14:01:07 +0200 Subject: [PATCH 03/11] cli/command/completion: add EnvVarNames utility EnvVarNames offers completion for environment-variable names. This completion can be used for "--env" and "--build-arg" flags, which allow obtaining the value of the given environment-variable if present in the local environment, so we only should complete the names of the environment variables, and not their value. This also prevents the completion script from printing values of environment variables containing sensitive values. For example; export MY_VAR=hello docker run --rm --env MY_VAR alpine printenv MY_VAR hello Before this patch: docker run --env GO GO111MODULE=auto GOLANG_VERSION=1.21.12 GOPATH=/go GOTOOLCHAIN=local With this patch: docker run --env GO GO111MODULE GOLANG_VERSION GOPATH GOTOOLCHAIN Signed-off-by: Sebastiaan van Stijn --- cli/command/completion/functions.go | 24 ++++++++++++++++++++++++ cli/command/container/exec.go | 5 +---- cli/command/container/run.go | 5 +---- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/cli/command/completion/functions.go b/cli/command/completion/functions.go index 7777cb84278c..b395850b45b9 100644 --- a/cli/command/completion/functions.go +++ b/cli/command/completion/functions.go @@ -2,6 +2,7 @@ package completion import ( "os" + "strings" "github.com/docker/cli/cli/command/formatter" "github.com/docker/docker/api/types/container" @@ -105,6 +106,29 @@ func NetworkNames(dockerCLI APIClientProvider) ValidArgsFn { } } +// EnvVarNames offers completion for environment-variable names. This +// completion can be used for "--env" and "--build-arg" flags, which +// allow obtaining the value of the given environment-variable if present +// in the local environment, so we only should complete the names of the +// environment variables, and not their value. This also prevents the +// completion script from printing values of environment variables +// containing sensitive values. +// +// For example; +// +// export MY_VAR=hello +// docker run --rm --env MY_VAR alpine printenv MY_VAR +// hello +func EnvVarNames(_ *cobra.Command, _ []string, _ string) (names []string, _ cobra.ShellCompDirective) { + envs := os.Environ() + names = make([]string, 0, len(envs)) + for _, env := range envs { + name, _, _ := strings.Cut(env, "=") + names = append(names, name) + } + return names, cobra.ShellCompDirectiveNoFileComp +} + // FileNames is a convenience function to use [cobra.ShellCompDirectiveDefault], // which indicates to let the shell perform its default behavior after // completions have been provided. diff --git a/cli/command/container/exec.go b/cli/command/container/exec.go index 1f5506ae2a7d..c2a1d447998b 100644 --- a/cli/command/container/exec.go +++ b/cli/command/container/exec.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "io" - "os" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -78,9 +77,7 @@ func NewExecCommand(dockerCli command.Cli) *cobra.Command { flags.StringVarP(&options.Workdir, "workdir", "w", "", "Working directory inside the container") flags.SetAnnotation("workdir", "version", []string{"1.35"}) - _ = cmd.RegisterFlagCompletionFunc("env", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { - return os.Environ(), cobra.ShellCompDirectiveNoFileComp - }) + _ = cmd.RegisterFlagCompletionFunc("env", completion.EnvVarNames) _ = cmd.RegisterFlagCompletionFunc("env-file", completion.FileNames) return cmd diff --git a/cli/command/container/run.go b/cli/command/container/run.go index bdf8eff5bef4..5fb44baef33e 100644 --- a/cli/command/container/run.go +++ b/cli/command/container/run.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "io" - "os" "strings" "syscall" @@ -70,9 +69,7 @@ func NewRunCommand(dockerCli command.Cli) *cobra.Command { command.AddTrustVerificationFlags(flags, &options.untrusted, dockerCli.ContentTrustEnabled()) copts = addFlags(flags) - _ = cmd.RegisterFlagCompletionFunc("env", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { - return os.Environ(), cobra.ShellCompDirectiveNoFileComp - }) + _ = cmd.RegisterFlagCompletionFunc("env", completion.EnvVarNames) _ = cmd.RegisterFlagCompletionFunc("env-file", completion.FileNames) _ = cmd.RegisterFlagCompletionFunc("network", completion.NetworkNames(dockerCli)) return cmd From 5e7bcbeac6e368a9f90167215168f699a0ee197b Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 5 Jul 2024 14:15:29 +0200 Subject: [PATCH 04/11] cli/command/completion: add FromList utility It's an alias for cobra.FixedCompletions but takes a variadic list of strings, so that it's not needed to construct an array for this. Signed-off-by: Sebastiaan van Stijn --- cli/command/completion/functions.go | 5 +++++ cli/command/container/run.go | 1 + cmd/docker/completions.go | 9 ++------- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/cli/command/completion/functions.go b/cli/command/completion/functions.go index b395850b45b9..30acf1e96eb0 100644 --- a/cli/command/completion/functions.go +++ b/cli/command/completion/functions.go @@ -129,6 +129,11 @@ func EnvVarNames(_ *cobra.Command, _ []string, _ string) (names []string, _ cobr return names, cobra.ShellCompDirectiveNoFileComp } +// FromList offers completion for the given list of options. +func FromList(options ...string) ValidArgsFn { + return cobra.FixedCompletions(options, cobra.ShellCompDirectiveNoFileComp) +} + // FileNames is a convenience function to use [cobra.ShellCompDirectiveDefault], // which indicates to let the shell perform its default behavior after // completions have been provided. diff --git a/cli/command/container/run.go b/cli/command/container/run.go index 5fb44baef33e..b06156f05d05 100644 --- a/cli/command/container/run.go +++ b/cli/command/container/run.go @@ -72,6 +72,7 @@ func NewRunCommand(dockerCli command.Cli) *cobra.Command { _ = cmd.RegisterFlagCompletionFunc("env", completion.EnvVarNames) _ = cmd.RegisterFlagCompletionFunc("env-file", completion.FileNames) _ = cmd.RegisterFlagCompletionFunc("network", completion.NetworkNames(dockerCli)) + _ = cmd.RegisterFlagCompletionFunc("pull", completion.FromList(PullImageAlways, PullImageMissing, PullImageNever)) return cmd } diff --git a/cmd/docker/completions.go b/cmd/docker/completions.go index 582a2051e33d..afa87d700875 100644 --- a/cmd/docker/completions.go +++ b/cmd/docker/completions.go @@ -1,6 +1,7 @@ package main import ( + "github.com/docker/cli/cli/command/completion" "github.com/docker/cli/cli/context/store" "github.com/spf13/cobra" ) @@ -19,13 +20,7 @@ func registerCompletionFuncForGlobalFlags(contextStore store.Store, cmd *cobra.C if err != nil { return err } - err = cmd.RegisterFlagCompletionFunc( - "log-level", - func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - values := []string{"debug", "info", "warn", "error", "fatal"} - return values, cobra.ShellCompDirectiveNoFileComp - }, - ) + err = cmd.RegisterFlagCompletionFunc("log-level", completion.FromList("debug", "info", "warn", "error", "fatal")) if err != nil { return err } From 162d9748b97c90c58414bff4db3962d2460c463d Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 5 Jul 2024 14:18:38 +0200 Subject: [PATCH 05/11] cli/command/container: provide flag-completion for "docker create" "docker run" and "docker create" are mostly identical, so we can copy the same completion functions, We could possibly create a utility for this (similar to `addFlags()` which configures both commands with the flags they share). I considered combining his with `addFlags()`, but that utility is also used in various tests, in which we don't need this feature, so keeping that for a future exercise. Signed-off-by: Sebastiaan van Stijn --- cli/command/container/create.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cli/command/container/create.go b/cli/command/container/create.go index 3193ccf20072..208ad6c77bc2 100644 --- a/cli/command/container/create.go +++ b/cli/command/container/create.go @@ -77,6 +77,11 @@ func NewCreateCommand(dockerCli command.Cli) *cobra.Command { command.AddPlatformFlag(flags, &options.platform) command.AddTrustVerificationFlags(flags, &options.untrusted, dockerCli.ContentTrustEnabled()) copts = addFlags(flags) + + _ = cmd.RegisterFlagCompletionFunc("env", completion.EnvVarNames) + _ = cmd.RegisterFlagCompletionFunc("env-file", completion.FileNames) + _ = cmd.RegisterFlagCompletionFunc("network", completion.NetworkNames(dockerCli)) + _ = cmd.RegisterFlagCompletionFunc("pull", completion.FromList(PullImageAlways, PullImageMissing, PullImageNever)) return cmd } From 42b68a3ed72b0e6e8b26e48a15dd1b2a99f4b8bf Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 5 Jul 2024 14:57:19 +0200 Subject: [PATCH 06/11] cmd/docker: fix completion for --context registerCompletionFuncForGlobalFlags was called from newDockerCommand, at which time no context-store is initialized yet, so it would return a nil value, probably resulting in `store.Names` to panic, but these errors are not shown when running the completion. As a result, the flag completion would fall back to completing from filenames. This patch changes the function to dynamically get the context-store; this fixes the problem mentioned above, because at the time the completion function is _invoked_, the CLI is fully initialized, and does have a context-store available. A (non-exported) interface is defined to allow the function to accept alternative implementations (not requiring a full command.DockerCLI). Before this patch: docker context create one docker context create two docker --context .DS_Store .idea/ Makefile .dockerignore .mailmap build/ ... With this patch: docker context create one docker context create two docker --context default one two Signed-off-by: Sebastiaan van Stijn --- cmd/docker/completions.go | 20 +++++++++----------- cmd/docker/docker.go | 2 +- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/cmd/docker/completions.go b/cmd/docker/completions.go index afa87d700875..95ee737b4276 100644 --- a/cmd/docker/completions.go +++ b/cmd/docker/completions.go @@ -6,17 +6,15 @@ import ( "github.com/spf13/cobra" ) -func registerCompletionFuncForGlobalFlags(contextStore store.Store, cmd *cobra.Command) error { - err := cmd.RegisterFlagCompletionFunc( - "context", - func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - names, err := store.Names(contextStore) - if err != nil { - return nil, cobra.ShellCompDirectiveError - } - return names, cobra.ShellCompDirectiveNoFileComp - }, - ) +type contextStoreProvider interface { + ContextStore() store.Store +} + +func registerCompletionFuncForGlobalFlags(dockerCLI contextStoreProvider, cmd *cobra.Command) error { + err := cmd.RegisterFlagCompletionFunc("context", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { + names, _ := store.Names(dockerCLI.ContextStore()) + return names, cobra.ShellCompDirectiveNoFileComp + }) if err != nil { return err } diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index 99f28db80de3..4df04923ebf9 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -100,7 +100,7 @@ func newDockerCommand(dockerCli *command.DockerCli) *cli.TopLevelCommand { cmd.SetErr(dockerCli.Err()) opts, helpCmd = cli.SetupRootCommand(cmd) - _ = registerCompletionFuncForGlobalFlags(dockerCli.ContextStore(), cmd) + _ = registerCompletionFuncForGlobalFlags(dockerCli, cmd) cmd.Flags().BoolP("version", "v", false, "Print version information and quit") setFlagErrorFunc(dockerCli, cmd) From e4dd8b1898792ee26d8bb6ed95d5624594da66b7 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 5 Jul 2024 15:01:58 +0200 Subject: [PATCH 07/11] cli/context/store: Names(): fix panic when called with nil-interface Before this, it would panic when a nil-interface was passed. Signed-off-by: Sebastiaan van Stijn --- cli/context/store/store.go | 3 +++ cli/context/store/store_test.go | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/cli/context/store/store.go b/cli/context/store/store.go index 44e9477fb014..066b5769d728 100644 --- a/cli/context/store/store.go +++ b/cli/context/store/store.go @@ -124,6 +124,9 @@ func (s *ContextStore) List() ([]Metadata, error) { // Names return Metadata names for a Lister func Names(s Lister) ([]string, error) { + if s == nil { + return nil, errors.New("nil lister") + } list, err := s.List() if err != nil { return nil, err diff --git a/cli/context/store/store_test.go b/cli/context/store/store_test.go index ad61036dbe52..c785b2e01524 100644 --- a/cli/context/store/store_test.go +++ b/cli/context/store/store_test.go @@ -260,3 +260,9 @@ func TestCorruptMetadata(t *testing.T) { _, err = s.GetMetadata("source") assert.ErrorContains(t, err, fmt.Sprintf("parsing %s: unexpected end of JSON input", contextFile)) } + +func TestNames(t *testing.T) { + names, err := Names(nil) + assert.Check(t, is.Error(err, "nil lister")) + assert.Check(t, is.Len(names, 0)) +} From f30158dbf873555e314b51d67ec2b18d6128f25d Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 5 Jul 2024 15:54:09 +0200 Subject: [PATCH 08/11] cli/command/container: add completion for --cap-add, --cap-drop With this patch: docker run --cap-add ALL CAP_KILL CAP_SETUID CAP_AUDIT_CONTROL CAP_LEASE CAP_SYSLOG CAP_AUDIT_READ CAP_LINUX_IMMUTABLE CAP_SYS_ADMIN CAP_AUDIT_WRITE CAP_MAC_ADMIN CAP_SYS_BOOT CAP_BLOCK_SUSPEND CAP_MAC_OVERRIDE CAP_SYS_CHROOT CAP_BPF CAP_MKNOD CAP_SYS_MODULE CAP_CHECKPOINT_RESTORE CAP_NET_ADMIN CAP_SYS_NICE CAP_CHOWN CAP_NET_BIND_SERVICE CAP_SYS_PACCT CAP_DAC_OVERRIDE CAP_NET_BROADCAST CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_NET_RAW CAP_SYS_RAWIO CAP_FOWNER CAP_PERFMON CAP_SYS_RESOURCE CAP_FSETID CAP_SETFCAP CAP_SYS_TIME CAP_IPC_LOCK CAP_SETGID CAP_SYS_TTY_CONFIG CAP_IPC_OWNER CAP_SETPCAP CAP_WAKE_ALARM Signed-off-by: Sebastiaan van Stijn --- cli/command/container/completion.go | 69 +++++++++++++++++++++++++++++ cli/command/container/create.go | 2 + cli/command/container/run.go | 2 + 3 files changed, 73 insertions(+) create mode 100644 cli/command/container/completion.go diff --git a/cli/command/container/completion.go b/cli/command/container/completion.go new file mode 100644 index 000000000000..b65b5ffbd98d --- /dev/null +++ b/cli/command/container/completion.go @@ -0,0 +1,69 @@ +package container + +import ( + "github.com/docker/cli/cli/command/completion" + "github.com/spf13/cobra" +) + +// allLinuxCapabilities is a list of all known Linux capabilities. +// +// This list was based on the containerd pkg/cap package; +// https://github.com/containerd/containerd/blob/v1.7.19/pkg/cap/cap_linux.go#L133-L181 +// +// TODO(thaJeztah): add descriptions, and enable descriptions for our completion scripts (cobra.CompletionOptions.DisableDescriptions is currently set to "true") +var allLinuxCapabilities = []string{ + "ALL", // magic value for "all capabilities" + + // caps35 is the caps of kernel 3.5 (37 entries) + "CAP_CHOWN", // 2.2 + "CAP_DAC_OVERRIDE", // 2.2 + "CAP_DAC_READ_SEARCH", // 2.2 + "CAP_FOWNER", // 2.2 + "CAP_FSETID", // 2.2 + "CAP_KILL", // 2.2 + "CAP_SETGID", // 2.2 + "CAP_SETUID", // 2.2 + "CAP_SETPCAP", // 2.2 + "CAP_LINUX_IMMUTABLE", // 2.2 + "CAP_NET_BIND_SERVICE", // 2.2 + "CAP_NET_BROADCAST", // 2.2 + "CAP_NET_ADMIN", // 2.2 + "CAP_NET_RAW", // 2.2 + "CAP_IPC_LOCK", // 2.2 + "CAP_IPC_OWNER", // 2.2 + "CAP_SYS_MODULE", // 2.2 + "CAP_SYS_RAWIO", // 2.2 + "CAP_SYS_CHROOT", // 2.2 + "CAP_SYS_PTRACE", // 2.2 + "CAP_SYS_PACCT", // 2.2 + "CAP_SYS_ADMIN", // 2.2 + "CAP_SYS_BOOT", // 2.2 + "CAP_SYS_NICE", // 2.2 + "CAP_SYS_RESOURCE", // 2.2 + "CAP_SYS_TIME", // 2.2 + "CAP_SYS_TTY_CONFIG", // 2.2 + "CAP_MKNOD", // 2.4 + "CAP_LEASE", // 2.4 + "CAP_AUDIT_WRITE", // 2.6.11 + "CAP_AUDIT_CONTROL", // 2.6.11 + "CAP_SETFCAP", // 2.6.24 + "CAP_MAC_OVERRIDE", // 2.6.25 + "CAP_MAC_ADMIN", // 2.6.25 + "CAP_SYSLOG", // 2.6.37 + "CAP_WAKE_ALARM", // 3.0 + "CAP_BLOCK_SUSPEND", // 3.5 + + // caps316 is the caps of kernel 3.16 (38 entries) + "CAP_AUDIT_READ", + + // caps58 is the caps of kernel 5.8 (40 entries) + "CAP_PERFMON", + "CAP_BPF", + + // caps59 is the caps of kernel 5.9 (41 entries) + "CAP_CHECKPOINT_RESTORE", +} + +func completeLinuxCapabilityNames(cmd *cobra.Command, args []string, toComplete string) (names []string, _ cobra.ShellCompDirective) { + return completion.FromList(allLinuxCapabilities...)(cmd, args, toComplete) +} diff --git a/cli/command/container/create.go b/cli/command/container/create.go index 208ad6c77bc2..a65d91e8db56 100644 --- a/cli/command/container/create.go +++ b/cli/command/container/create.go @@ -78,6 +78,8 @@ func NewCreateCommand(dockerCli command.Cli) *cobra.Command { command.AddTrustVerificationFlags(flags, &options.untrusted, dockerCli.ContentTrustEnabled()) copts = addFlags(flags) + _ = cmd.RegisterFlagCompletionFunc("cap-add", completeLinuxCapabilityNames) + _ = cmd.RegisterFlagCompletionFunc("cap-drop", completeLinuxCapabilityNames) _ = cmd.RegisterFlagCompletionFunc("env", completion.EnvVarNames) _ = cmd.RegisterFlagCompletionFunc("env-file", completion.FileNames) _ = cmd.RegisterFlagCompletionFunc("network", completion.NetworkNames(dockerCli)) diff --git a/cli/command/container/run.go b/cli/command/container/run.go index b06156f05d05..f413a2f08b06 100644 --- a/cli/command/container/run.go +++ b/cli/command/container/run.go @@ -69,6 +69,8 @@ func NewRunCommand(dockerCli command.Cli) *cobra.Command { command.AddTrustVerificationFlags(flags, &options.untrusted, dockerCli.ContentTrustEnabled()) copts = addFlags(flags) + _ = cmd.RegisterFlagCompletionFunc("cap-add", completeLinuxCapabilityNames) + _ = cmd.RegisterFlagCompletionFunc("cap-drop", completeLinuxCapabilityNames) _ = cmd.RegisterFlagCompletionFunc("env", completion.EnvVarNames) _ = cmd.RegisterFlagCompletionFunc("env-file", completion.FileNames) _ = cmd.RegisterFlagCompletionFunc("network", completion.NetworkNames(dockerCli)) From 7fe7223c2c9056776c8117a807e221cbde4aebbc Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sat, 6 Jul 2024 00:48:51 +0200 Subject: [PATCH 09/11] cli/command/container: add completion for --restart With this patch: docker run --restart always no on-failure unless-stopped Signed-off-by: Sebastiaan van Stijn --- cli/command/container/completion.go | 15 +++++++++++++++ cli/command/container/create.go | 1 + cli/command/container/run.go | 1 + cli/command/container/update.go | 2 ++ 4 files changed, 19 insertions(+) diff --git a/cli/command/container/completion.go b/cli/command/container/completion.go index b65b5ffbd98d..d26401e65fb6 100644 --- a/cli/command/container/completion.go +++ b/cli/command/container/completion.go @@ -2,6 +2,7 @@ package container import ( "github.com/docker/cli/cli/command/completion" + "github.com/docker/docker/api/types/container" "github.com/spf13/cobra" ) @@ -64,6 +65,20 @@ var allLinuxCapabilities = []string{ "CAP_CHECKPOINT_RESTORE", } +// restartPolicies is a list of all valid restart-policies.. +// +// TODO(thaJeztah): add descriptions, and enable descriptions for our completion scripts (cobra.CompletionOptions.DisableDescriptions is currently set to "true") +var restartPolicies = []string{ + string(container.RestartPolicyDisabled), + string(container.RestartPolicyAlways), + string(container.RestartPolicyOnFailure), + string(container.RestartPolicyUnlessStopped), +} + func completeLinuxCapabilityNames(cmd *cobra.Command, args []string, toComplete string) (names []string, _ cobra.ShellCompDirective) { return completion.FromList(allLinuxCapabilities...)(cmd, args, toComplete) } + +func completeRestartPolicies(cmd *cobra.Command, args []string, toComplete string) (names []string, _ cobra.ShellCompDirective) { + return completion.FromList(restartPolicies...)(cmd, args, toComplete) +} diff --git a/cli/command/container/create.go b/cli/command/container/create.go index a65d91e8db56..d080ae5ea558 100644 --- a/cli/command/container/create.go +++ b/cli/command/container/create.go @@ -84,6 +84,7 @@ func NewCreateCommand(dockerCli command.Cli) *cobra.Command { _ = cmd.RegisterFlagCompletionFunc("env-file", completion.FileNames) _ = cmd.RegisterFlagCompletionFunc("network", completion.NetworkNames(dockerCli)) _ = cmd.RegisterFlagCompletionFunc("pull", completion.FromList(PullImageAlways, PullImageMissing, PullImageNever)) + _ = cmd.RegisterFlagCompletionFunc("restart", completeRestartPolicies) return cmd } diff --git a/cli/command/container/run.go b/cli/command/container/run.go index f413a2f08b06..3520d2e4e1ca 100644 --- a/cli/command/container/run.go +++ b/cli/command/container/run.go @@ -75,6 +75,7 @@ func NewRunCommand(dockerCli command.Cli) *cobra.Command { _ = cmd.RegisterFlagCompletionFunc("env-file", completion.FileNames) _ = cmd.RegisterFlagCompletionFunc("network", completion.NetworkNames(dockerCli)) _ = cmd.RegisterFlagCompletionFunc("pull", completion.FromList(PullImageAlways, PullImageMissing, PullImageNever)) + _ = cmd.RegisterFlagCompletionFunc("restart", completeRestartPolicies) return cmd } diff --git a/cli/command/container/update.go b/cli/command/container/update.go index 997d61b354d7..275ec5f64634 100644 --- a/cli/command/container/update.go +++ b/cli/command/container/update.go @@ -83,6 +83,8 @@ func NewUpdateCommand(dockerCli command.Cli) *cobra.Command { flags.Var(&options.cpus, "cpus", "Number of CPUs") flags.SetAnnotation("cpus", "version", []string{"1.29"}) + _ = cmd.RegisterFlagCompletionFunc("restart", completeRestartPolicies) + return cmd } From d6f78cdbb1ef15023d13a95feeb4ec527e89938e Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sat, 6 Jul 2024 00:58:35 +0200 Subject: [PATCH 10/11] cli/command/container: add completion for --volumes-from With this patch: docker run --volumes-from amazing_nobel amazing_cannon boring_wozniak determined_banzai elegant_solomon reverent_booth amazing_nobel Signed-off-by: Sebastiaan van Stijn --- cli/command/container/create.go | 1 + cli/command/container/run.go | 1 + 2 files changed, 2 insertions(+) diff --git a/cli/command/container/create.go b/cli/command/container/create.go index d080ae5ea558..a14520fb1a30 100644 --- a/cli/command/container/create.go +++ b/cli/command/container/create.go @@ -85,6 +85,7 @@ func NewCreateCommand(dockerCli command.Cli) *cobra.Command { _ = cmd.RegisterFlagCompletionFunc("network", completion.NetworkNames(dockerCli)) _ = cmd.RegisterFlagCompletionFunc("pull", completion.FromList(PullImageAlways, PullImageMissing, PullImageNever)) _ = cmd.RegisterFlagCompletionFunc("restart", completeRestartPolicies) + _ = cmd.RegisterFlagCompletionFunc("volumes-from", completion.ContainerNames(dockerCli, true)) return cmd } diff --git a/cli/command/container/run.go b/cli/command/container/run.go index 3520d2e4e1ca..905da1d8b784 100644 --- a/cli/command/container/run.go +++ b/cli/command/container/run.go @@ -76,6 +76,7 @@ func NewRunCommand(dockerCli command.Cli) *cobra.Command { _ = cmd.RegisterFlagCompletionFunc("network", completion.NetworkNames(dockerCli)) _ = cmd.RegisterFlagCompletionFunc("pull", completion.FromList(PullImageAlways, PullImageMissing, PullImageNever)) _ = cmd.RegisterFlagCompletionFunc("restart", completeRestartPolicies) + _ = cmd.RegisterFlagCompletionFunc("volumes-from", completion.ContainerNames(dockerCli, true)) return cmd } From b1c0ddca02c0cd86cffad5ce0bfdf17cb9b97f72 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sat, 6 Jul 2024 01:46:47 +0200 Subject: [PATCH 11/11] cli/command/container: add completion for --stop-signal With this patch: docker run --stop-signal ABRT IOT RTMAX-4 RTMIN RTMIN+11 TSTP ALRM KILL RTMAX-5 RTMIN+1 RTMIN+12 TTIN BUS PIPE RTMAX-6 RTMIN+2 RTMIN+13 TTOU CHLD POLL RTMAX-7 RTMIN+3 RTMIN+14 URG CLD PROF RTMAX-8 RTMIN+4 RTMIN+15 USR1 CONT PWR RTMAX-9 RTMIN+5 SEGV USR2 FPE QUIT RTMAX-10 RTMIN+6 STKFLT VTALRM HUP RTMAX RTMAX-11 RTMIN+7 STOP WINCH ILL RTMAX-1 RTMAX-12 RTMIN+8 SYS XCPU INT RTMAX-2 RTMAX-13 RTMIN+9 TERM XFSZ IO RTMAX-3 RTMAX-14 RTMIN+10 TRAP Signed-off-by: Sebastiaan van Stijn --- cli/command/container/completion.go | 10 ++++++++++ cli/command/container/create.go | 1 + cli/command/container/kill.go | 5 ++++- cli/command/container/restart.go | 3 +++ cli/command/container/run.go | 1 + cli/command/container/stop.go | 3 +++ 6 files changed, 22 insertions(+), 1 deletion(-) diff --git a/cli/command/container/completion.go b/cli/command/container/completion.go index d26401e65fb6..24c046a8292e 100644 --- a/cli/command/container/completion.go +++ b/cli/command/container/completion.go @@ -3,6 +3,7 @@ package container import ( "github.com/docker/cli/cli/command/completion" "github.com/docker/docker/api/types/container" + "github.com/moby/sys/signal" "github.com/spf13/cobra" ) @@ -82,3 +83,12 @@ func completeLinuxCapabilityNames(cmd *cobra.Command, args []string, toComplete func completeRestartPolicies(cmd *cobra.Command, args []string, toComplete string) (names []string, _ cobra.ShellCompDirective) { return completion.FromList(restartPolicies...)(cmd, args, toComplete) } + +func completeSignals(cmd *cobra.Command, args []string, toComplete string) (names []string, _ cobra.ShellCompDirective) { + // TODO(thaJeztah): do we want to provide the full list here, or a subset? + signalNames := make([]string, 0, len(signal.SignalMap)) + for k := range signal.SignalMap { + signalNames = append(signalNames, k) + } + return completion.FromList(signalNames...)(cmd, args, toComplete) +} diff --git a/cli/command/container/create.go b/cli/command/container/create.go index a14520fb1a30..0e449bb68a6e 100644 --- a/cli/command/container/create.go +++ b/cli/command/container/create.go @@ -85,6 +85,7 @@ func NewCreateCommand(dockerCli command.Cli) *cobra.Command { _ = cmd.RegisterFlagCompletionFunc("network", completion.NetworkNames(dockerCli)) _ = cmd.RegisterFlagCompletionFunc("pull", completion.FromList(PullImageAlways, PullImageMissing, PullImageNever)) _ = cmd.RegisterFlagCompletionFunc("restart", completeRestartPolicies) + _ = cmd.RegisterFlagCompletionFunc("stop-signal", completeSignals) _ = cmd.RegisterFlagCompletionFunc("volumes-from", completion.ContainerNames(dockerCli, true)) return cmd } diff --git a/cli/command/container/kill.go b/cli/command/container/kill.go index 96ce22e5ae85..0095198b5aab 100644 --- a/cli/command/container/kill.go +++ b/cli/command/container/kill.go @@ -38,6 +38,9 @@ func NewKillCommand(dockerCli command.Cli) *cobra.Command { flags := cmd.Flags() flags.StringVarP(&opts.signal, "signal", "s", "", "Signal to send to the container") + + _ = cmd.RegisterFlagCompletionFunc("signal", completeSignals) + return cmd } @@ -50,7 +53,7 @@ func runKill(ctx context.Context, dockerCli command.Cli, opts *killOptions) erro if err := <-errChan; err != nil { errs = append(errs, err.Error()) } else { - fmt.Fprintln(dockerCli.Out(), name) + _, _ = fmt.Fprintln(dockerCli.Out(), name) } } if len(errs) > 0 { diff --git a/cli/command/container/restart.go b/cli/command/container/restart.go index 0ac4fa985ef7..8e7c0d4a026f 100644 --- a/cli/command/container/restart.go +++ b/cli/command/container/restart.go @@ -43,6 +43,9 @@ func NewRestartCommand(dockerCli command.Cli) *cobra.Command { flags := cmd.Flags() flags.StringVarP(&opts.signal, "signal", "s", "", "Signal to send to the container") flags.IntVarP(&opts.timeout, "time", "t", 0, "Seconds to wait before killing the container") + + _ = cmd.RegisterFlagCompletionFunc("signal", completeSignals) + return cmd } diff --git a/cli/command/container/run.go b/cli/command/container/run.go index 905da1d8b784..ef0986cba281 100644 --- a/cli/command/container/run.go +++ b/cli/command/container/run.go @@ -76,6 +76,7 @@ func NewRunCommand(dockerCli command.Cli) *cobra.Command { _ = cmd.RegisterFlagCompletionFunc("network", completion.NetworkNames(dockerCli)) _ = cmd.RegisterFlagCompletionFunc("pull", completion.FromList(PullImageAlways, PullImageMissing, PullImageNever)) _ = cmd.RegisterFlagCompletionFunc("restart", completeRestartPolicies) + _ = cmd.RegisterFlagCompletionFunc("stop-signal", completeSignals) _ = cmd.RegisterFlagCompletionFunc("volumes-from", completion.ContainerNames(dockerCli, true)) return cmd } diff --git a/cli/command/container/stop.go b/cli/command/container/stop.go index c221fb4448ac..c99a7c456dbc 100644 --- a/cli/command/container/stop.go +++ b/cli/command/container/stop.go @@ -43,6 +43,9 @@ func NewStopCommand(dockerCli command.Cli) *cobra.Command { flags := cmd.Flags() flags.StringVarP(&opts.signal, "signal", "s", "", "Signal to send to the container") flags.IntVarP(&opts.timeout, "time", "t", 0, "Seconds to wait before killing the container") + + _ = cmd.RegisterFlagCompletionFunc("signal", completeSignals) + return cmd }