From 5dea9d881cd878d8bbe3d1376be37b86b6c6395d Mon Sep 17 00:00:00 2001 From: Dan Wallis Date: Wed, 3 Jul 2024 16:42:29 +0100 Subject: [PATCH 01/14] Enable completion for 'image' sub commands Signed-off-by: Dan Wallis (cherry picked from commit c7d46aa7a1b71b9e8a6920812de72c5b680a3f7c) Signed-off-by: Sebastiaan van Stijn --- cli/command/image/history.go | 2 ++ cli/command/image/inspect.go | 2 ++ cli/command/image/remove.go | 2 ++ 3 files changed, 6 insertions(+) diff --git a/cli/command/image/history.go b/cli/command/image/history.go index e36b9bc03c..1c0ae40981 100644 --- a/cli/command/image/history.go +++ b/cli/command/image/history.go @@ -5,6 +5,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/formatter" flagsHelper "github.com/docker/cli/cli/flags" "github.com/spf13/cobra" @@ -31,6 +32,7 @@ func NewHistoryCommand(dockerCli command.Cli) *cobra.Command { opts.image = args[0] return runHistory(cmd.Context(), dockerCli, opts) }, + ValidArgsFunction: completion.ImageNames(dockerCli), Annotations: map[string]string{ "aliases": "docker image history, docker history", }, diff --git a/cli/command/image/inspect.go b/cli/command/image/inspect.go index 96b3bb8f4c..90fc670400 100644 --- a/cli/command/image/inspect.go +++ b/cli/command/image/inspect.go @@ -8,6 +8,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/spf13/cobra" @@ -30,6 +31,7 @@ func newInspectCommand(dockerCli command.Cli) *cobra.Command { opts.refs = args return runInspect(cmd.Context(), dockerCli, opts) }, + ValidArgsFunction: completion.ImageNames(dockerCli), } flags := cmd.Flags() diff --git a/cli/command/image/remove.go b/cli/command/image/remove.go index e0983f377f..3fb0af5c03 100644 --- a/cli/command/image/remove.go +++ b/cli/command/image/remove.go @@ -7,6 +7,7 @@ import ( "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/command/completion" "github.com/docker/docker/api/types/image" "github.com/docker/docker/errdefs" "github.com/pkg/errors" @@ -29,6 +30,7 @@ func NewRemoveCommand(dockerCli command.Cli) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return runRemove(cmd.Context(), dockerCli, opts, args) }, + ValidArgsFunction: completion.ImageNames(dockerCli), Annotations: map[string]string{ "aliases": "docker image rm, docker image remove, docker rmi", }, From 338e7a604c7b94d7cc9e696cb2edcb544bd58a80 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 4 Jul 2024 21:07:31 +0200 Subject: [PATCH 02/14] Dockerfile.dev: install bash-completion in dev container It's not initialized, because there's no `docker` command installed by default, but at least this makes sure that the basics are present for testing. Signed-off-by: Sebastiaan van Stijn (cherry picked from commit 3d80b7b0a74645feb7e3a044b74c04e806e35a3d) Signed-off-by: Sebastiaan van Stijn --- dockerfiles/Dockerfile.dev | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dockerfiles/Dockerfile.dev b/dockerfiles/Dockerfile.dev index 365c5e4216..f387763e46 100644 --- a/dockerfiles/Dockerfile.dev +++ b/dockerfiles/Dockerfile.dev @@ -35,6 +35,7 @@ RUN --mount=type=cache,target=/root/.cache/go-build \ FROM golang AS dev RUN apk add --no-cache \ bash \ + bash-completion \ build-base \ ca-certificates \ coreutils \ @@ -44,7 +45,8 @@ RUN apk add --no-cache \ nano RUN echo -e "\nYou are now in a development container. Run '\e\033[1mmake help\e\033[0m' to learn about\navailable make targets.\n" > /etc/motd \ - && echo -e "cat /etc/motd\nPS1=\"\e[0;32m\u@docker-cli-dev\\$ \e[0m\"" >> /root/.bashrc + && echo -e "cat /etc/motd\nPS1=\"\e[0;32m\u@docker-cli-dev\\$ \e[0m\"" >> /root/.bashrc \ + && echo -e "source /etc/bash/bash_completion.sh" >> /root/.bashrc CMD bash ENV DISABLE_WARN_OUTSIDE_CONTAINER=1 ENV PATH=$PATH:/go/src/github.com/docker/cli/build From 6cd1f6f26dc36b3dcf3baa69b385da9a70d11294 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 4 Jul 2024 21:18:04 +0200 Subject: [PATCH 03/14] Makefile: add completion target Add a "completion" target to install the generated completion scripts inside the dev-container. As generating this script depends on the docker binary, it calls "make binary" first. Signed-off-by: Sebastiaan van Stijn (cherry picked from commit 3f3ecb94c55266d97749907b948b5db1391d2b25) Signed-off-by: Sebastiaan van Stijn --- Makefile | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Makefile b/Makefile index 8469c60318..6b529faef0 100644 --- a/Makefile +++ b/Makefile @@ -86,6 +86,16 @@ mod-outdated: ## check outdated dependencies authors: ## generate AUTHORS file from git history scripts/docs/generate-authors.sh +.PHONY: completion +completion: binary +completion: /etc/bash_completion.d/docker +completion: ## generate and install the completion scripts + +.PHONY: /etc/bash_completion.d/docker +/etc/bash_completion.d/docker: ## generate and install the bash-completion script + mkdir -p /etc/bash_completion.d + docker completion bash > /etc/bash_completion.d/docker + .PHONY: manpages manpages: ## generate man pages from go source and markdown scripts/docs/generate-man.sh From d60252954befb37e8cafba76895f4764e03f8a64 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 4 Jul 2024 21:05:07 +0200 Subject: [PATCH 04/14] cli/command/container: NewRunCommand: slight cleanup of completion - explicitly suppress unhandled errors - remove names for unused arguments Signed-off-by: Sebastiaan van Stijn (cherry picked from commit eed0e5b02a7580173cc595905f71d30354559b14) 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 562e802920..14322ef3d2 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 68fe829c968917232529a8f23a533339622b4fde Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 5 Jul 2024 13:42:46 +0200 Subject: [PATCH 05/14] 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 (cherry picked from commit 9207ff104681c56a8940ecb32c5080fa4c024444) 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 6026ef7692..034e78852e 100644 --- a/cli/command/completion/functions.go +++ b/cli/command/completion/functions.go @@ -106,6 +106,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 1c0a741263..cfb3e4baad 100644 --- a/cli/command/container/exec.go +++ b/cli/command/container/exec.go @@ -82,9 +82,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 14322ef3d2..bdf8eff5be 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 d914a3f97e14f2f2f948d7e904c3445264d155de Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 5 Jul 2024 14:01:07 +0200 Subject: [PATCH 06/14] 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 (cherry picked from commit e3427f341b9dec5c7556024f8a5e57869e461299) 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 034e78852e..1e9e062c06 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" @@ -106,6 +107,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 cfb3e4baad..8244734a5c 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" @@ -79,9 +78,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 bdf8eff5be..5fb44baef3 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 7a4062a4a94943292b7f43186c2672d5b855a640 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 5 Jul 2024 14:15:29 +0200 Subject: [PATCH 07/14] 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 (cherry picked from commit 5e7bcbeac6e368a9f90167215168f699a0ee197b) 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 1e9e062c06..c25330a4b6 100644 --- a/cli/command/completion/functions.go +++ b/cli/command/completion/functions.go @@ -130,6 +130,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 5fb44baef3..b06156f05d 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 582a2051e3..afa87d7008 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 4473f4884717b603c1a67b8175bb5e11ffb8cda0 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 5 Jul 2024 14:18:38 +0200 Subject: [PATCH 08/14] 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 (cherry picked from commit 162d9748b97c90c58414bff4db3962d2460c463d) 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 3193ccf200..208ad6c77b 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 787caf2fe40c52ae05481c6cc80410ba71c99b20 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 5 Jul 2024 14:57:19 +0200 Subject: [PATCH 09/14] 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 (cherry picked from commit 42b68a3ed72b0e6e8b26e48a15dd1b2a99f4b8bf) 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 afa87d7008..95ee737b42 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 7825c7d354..6e28a99ebc 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -102,7 +102,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 3926ed6b248d4999d75e04533c0eb3a9a866ef6d Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 5 Jul 2024 15:01:58 +0200 Subject: [PATCH 10/14] 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 (cherry picked from commit e4dd8b1898792ee26d8bb6ed95d5624594da66b7) 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 44e9477fb0..066b5769d7 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 ad61036dbe..c785b2e015 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 36802176a74390c7418041dffe38e2964e394190 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 5 Jul 2024 15:54:09 +0200 Subject: [PATCH 11/14] 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 (cherry picked from commit f30158dbf873555e314b51d67ec2b18d6128f25d) 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 0000000000..b65b5ffbd9 --- /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 208ad6c77b..a65d91e8db 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 b06156f05d..f413a2f08b 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 d11e73d6e6153e1261a85d919e72adb20c50fe1d Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sat, 6 Jul 2024 00:48:51 +0200 Subject: [PATCH 12/14] 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 (cherry picked from commit 7fe7223c2c9056776c8117a807e221cbde4aebbc) 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 b65b5ffbd9..d26401e65f 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 a65d91e8db..d080ae5ea5 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 f413a2f08b..3520d2e4e1 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 7ea668830b..5720f39bce 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 ab8a2c07162680234290a2b8cbc52bf02de88c48 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sat, 6 Jul 2024 00:58:35 +0200 Subject: [PATCH 13/14] 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 (cherry picked from commit d6f78cdbb1ef15023d13a95feeb4ec527e89938e) 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 d080ae5ea5..a14520fb1a 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 3520d2e4e1..905da1d8b7 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 66ec7142aee4e2ab15485b70c677af87f9b57e9d Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sat, 6 Jul 2024 01:46:47 +0200 Subject: [PATCH 14/14] 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 (cherry picked from commit b1c0ddca02c0cd86cffad5ce0bfdf17cb9b97f72) 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 d26401e65f..24c046a829 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 a14520fb1a..0e449bb68a 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 96ce22e5ae..0095198b5a 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 0ac4fa985e..8e7c0d4a02 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 905da1d8b7..ef0986cba2 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 c221fb4448..c99a7c456d 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 }