diff --git a/cli/command/container/completion.go b/cli/command/container/completion.go index cffa714003..91593c5b32 100644 --- a/cli/command/container/completion.go +++ b/cli/command/container/completion.go @@ -64,6 +64,7 @@ func addCompletions(cmd *cobra.Command, dockerCLI completion.APIClientProvider) _ = cmd.RegisterFlagCompletionFunc("ipc", completeIpc(dockerCLI)) _ = cmd.RegisterFlagCompletionFunc("link", completeLink(dockerCLI)) _ = cmd.RegisterFlagCompletionFunc("network", completion.NetworkNames(dockerCLI)) + _ = cmd.RegisterFlagCompletionFunc("pid", completePid(dockerCLI)) _ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms) _ = cmd.RegisterFlagCompletionFunc("pull", completion.FromList(PullImageAlways, PullImageMissing, PullImageNever)) _ = cmd.RegisterFlagCompletionFunc("restart", completeRestartPolicies) @@ -99,6 +100,20 @@ func completeLink(dockerCLI completion.APIClientProvider) func(cmd *cobra.Comman } } +// completePid implements shell completion for the `--pid` option of `run` and `create`. +func completePid(dockerCLI completion.APIClientProvider) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(toComplete) > 0 && strings.HasPrefix("container", toComplete) { //nolint:gocritic // not swapped, matches partly typed "container" + return []string{"container:"}, cobra.ShellCompDirectiveNoSpace + } + if strings.HasPrefix(toComplete, "container:") { + names, _ := completion.ContainerNames(dockerCLI, true)(cmd, args, toComplete) + return prefixWith("container:", names), cobra.ShellCompDirectiveNoFileComp + } + return []string{"container:", "host"}, cobra.ShellCompDirectiveNoFileComp + } +} + // 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 { diff --git a/cli/command/container/completion_test.go b/cli/command/container/completion_test.go index b85aae32d5..537735722f 100644 --- a/cli/command/container/completion_test.go +++ b/cli/command/container/completion_test.go @@ -4,6 +4,10 @@ import ( "strings" "testing" + "github.com/docker/cli/internal/test" + "github.com/docker/cli/internal/test/builders" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" "github.com/moby/sys/signal" "github.com/spf13/cobra" "gotest.tools/v3/assert" @@ -21,6 +25,48 @@ func TestCompleteLinuxCapabilityNames(t *testing.T) { } } +func TestCompletePid(t *testing.T) { + tests := []struct { + containerListFunc func(container.ListOptions) ([]types.Container, error) + toComplete string + expectedCompletions []string + expectedDirective cobra.ShellCompDirective + }{ + { + toComplete: "", + expectedCompletions: []string{"container:", "host"}, + expectedDirective: cobra.ShellCompDirectiveNoFileComp, + }, + { + toComplete: "c", + expectedCompletions: []string{"container:"}, + expectedDirective: cobra.ShellCompDirectiveNoSpace, + }, + { + containerListFunc: func(container.ListOptions) ([]types.Container, error) { + return []types.Container{ + *builders.Container("c1"), + *builders.Container("c2"), + }, nil + }, + toComplete: "container:", + expectedCompletions: []string{"container:c1", "container:c2"}, + expectedDirective: cobra.ShellCompDirectiveNoFileComp, + }, + } + + for _, tc := range tests { + t.Run(tc.toComplete, func(t *testing.T) { + cli := test.NewFakeCli(&fakeClient{ + containerListFunc: tc.containerListFunc, + }) + completions, directive := completePid(cli)(NewRunCommand(cli), nil, tc.toComplete) + assert.Check(t, is.DeepEqual(completions, tc.expectedCompletions)) + assert.Check(t, is.Equal(directive, tc.expectedDirective)) + }) + } +} + func TestCompleteRestartPolicies(t *testing.T) { values, directives := completeRestartPolicies(nil, nil, "") assert.Check(t, is.Equal(directives&cobra.ShellCompDirectiveNoFileComp, cobra.ShellCompDirectiveNoFileComp), "Should not perform file completion")