Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Cobra completions for run and create #5580

Open
wants to merge 59 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
cc981e3
Share the container completions
albers Oct 22, 2024
d9766a3
Disable file completion for `--add-host`
albers Oct 23, 2024
98d5b03
Disable file completion for `--annotation`
albers Oct 23, 2024
55d4192
Add completion for `--attach`
albers Oct 23, 2024
75d8077
Disable file completion for `--blkio-weight*`
albers Oct 23, 2024
fe1db01
Disable file completion for `--cgroup-parent`
albers Oct 23, 2024
00e3b7c
Add completion for `--cgroupns`
albers Oct 23, 2024
4450f48
Disable file completion for `--cpu*`
albers Oct 23, 2024
285d0e3
Disable file completion for `--device-*`
albers Oct 23, 2024
14038a9
Disable file completion for `--dns*`
albers Oct 24, 2024
85ab121
Disable file completion for `--domainname`
albers Oct 24, 2024
9906847
Disable file completion for `--entrypoint`
albers Oct 23, 2024
6aba94e
Disable file completion for `--expose`
albers Oct 24, 2024
a5aa7c9
Disable file completion for `--gpus`
albers Oct 24, 2024
d963cfc
Disable file completion for `--group-add`
albers Oct 23, 2024
3ab5af7
Disable file completion for `--health-*`
albers Oct 23, 2024
e08a007
Disable file completion for `--hostname`
albers Oct 23, 2024
784ad51
Disable file completion for `--ip6*`
albers Oct 23, 2024
b9c7f6c
Add completion for `--ipc`
albers Oct 24, 2024
77fd7d7
Disable file completion for `--isolation`
albers Oct 24, 2024
33d870a
Disable file completion for `--kernel-memory`
albers Oct 24, 2024
846439c
Disable file completion for `--label`
albers Oct 24, 2024
0e4b312
Add completion for `--link`
albers Oct 24, 2024
93af729
Disable file completion for `--link-local-ip`
albers Oct 24, 2024
6a801bc
Disable file completion for `--log-*`
albers Oct 24, 2024
e27ff3b
Disable file completion for `--mac-address`
albers Oct 24, 2024
0135d7e
Disable file completion for `--memory*`
albers Oct 24, 2024
31f1f43
Disable file completion for `--mount`
albers Oct 24, 2024
b3703fa
Disable file completion for `--name`
albers Oct 24, 2024
39cd60b
Disable file completion for `--network-alias`
albers Oct 24, 2024
9817e32
Disable file completion for `--oom-score-adj`
albers Oct 24, 2024
5c96388
Add completion for `--pid`
albers Oct 24, 2024
968d758
Disable file completion for `--pids-limit`
albers Oct 24, 2024
39028ce
Disable file completion for `--publish`
albers Oct 24, 2024
a40ed99
Disable file completion for `--runtime`
albers Oct 24, 2024
ff9c684
Disable file completion for `--security-opt`
albers Oct 24, 2024
396d9e0
Disable file completion for `--shm-size`
albers Oct 24, 2024
09701fe
Disable file completion for `--stop-timeout`
albers Oct 24, 2024
a490538
Add completion for `--storage-opt`
albers Oct 24, 2024
0d65aca
Disable file completion for `--sysctl`
albers Oct 24, 2024
fd1d55a
Add completion for `--ulimit`
albers Oct 24, 2024
20538ac
Disable file completion for `--user`
albers Oct 24, 2024
b878f5e
Add completion for `--userns`
albers Oct 24, 2024
46db741
Disable file completion for `--uts`
albers Oct 24, 2024
8afd8dd
Add completion for `--volume-driver`
albers Oct 24, 2024
61e803c
Disable file completion for `--workdir`
albers Oct 24, 2024
fc12d09
Add completion for `--detach-keys`
albers Oct 24, 2024
eca980c
Improve completion for `--security-opt`
albers Oct 24, 2024
bdae758
Improve completion for `--log-driver` and `--log-opt`
albers Oct 26, 2024
32d7ff8
Fix function signatures (dockerCLI completion.APIClientProvider)
albers Oct 30, 2024
afda9f3
Improve completion for "--uts"
albers Oct 30, 2024
5624d2c
Add comments to linter exceptions
albers Oct 30, 2024
8362a23
Use constants in completion of `--cgroupns`
albers Oct 30, 2024
adcce2b
Improve completion for "--volume-driver"
albers Oct 30, 2024
a40ea78
Improve completion for "--log-driver"
albers Oct 30, 2024
3bc177b
Use constants in completion of IPCModes
albers Oct 30, 2024
33d3246
fixup! Add completion for `--pid`
albers Oct 30, 2024
ef6cca9
Use subtests
albers Oct 30, 2024
4378ab4
Handle null completions with a default callback
albers Oct 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
259 changes: 259 additions & 0 deletions cli/command/container/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,65 @@ var allLinuxCapabilities = sync.OnceValue(func() []string {
return out
})

// logDriverOptions provides the options for each built-in logging driver.
var logDriverOptions = map[string][]string{
"awslogs": {
"max-buffer-size", "mode", "awslogs-create-group", "awslogs-credentials-endpoint", "awslogs-datetime-format",
"awslogs-group", "awslogs-multiline-pattern", "awslogs-region", "awslogs-stream", "tag",
},
"fluentd": {
"max-buffer-size", "mode", "env", "env-regex", "labels", "fluentd-address", "fluentd-async",
"fluentd-buffer-limit", "fluentd-request-ack", "fluentd-retry-wait", "fluentd-max-retries",
"fluentd-sub-second-precision", "tag",
},
"gcplogs": {
"max-buffer-size", "mode", "env", "env-regex", "labels", "gcp-log-cmd", "gcp-meta-id", "gcp-meta-name",
"gcp-meta-zone", "gcp-project",
},
"gelf": {
"max-buffer-size", "mode", "env", "env-regex", "labels", "gelf-address", "gelf-compression-level",
"gelf-compression-type", "gelf-tcp-max-reconnect", "gelf-tcp-reconnect-delay", "tag",
},
"journald": {"max-buffer-size", "mode", "env", "env-regex", "labels", "tag"},
"json-file": {"max-buffer-size", "mode", "env", "env-regex", "labels", "compress", "max-file", "max-size"},
"local": {"max-buffer-size", "mode", "compress", "max-file", "max-size"},
"none": {},
"splunk": {
"max-buffer-size", "mode", "env", "env-regex", "labels", "splunk-caname", "splunk-capath", "splunk-format",
"splunk-gzip", "splunk-gzip-level", "splunk-index", "splunk-insecureskipverify", "splunk-source",
"splunk-sourcetype", "splunk-token", "splunk-url", "splunk-verify-connection", "tag",
},
"syslog": {
"max-buffer-size", "mode", "env", "env-regex", "labels", "syslog-address", "syslog-facility", "syslog-format",
"syslog-tls-ca-cert", "syslog-tls-cert", "syslog-tls-key", "syslog-tls-skip-verify", "tag",
},
}

// builtInLogDrivers provides a list of the built-in logging drivers.
var builtInLogDrivers = sync.OnceValue(func() []string {
drivers := make([]string, 0, len(logDriverOptions))
for driver := range logDriverOptions {
drivers = append(drivers, driver)
}
return drivers
})

// allLogDriverOptions provides all options of the built-in logging drivers.
// The list does not contain duplicates.
var allLogDriverOptions = sync.OnceValue(func() []string {
var result []string
seen := make(map[string]bool)
for driver := range logDriverOptions {
for _, opt := range logDriverOptions[driver] {
if !seen[opt] {
seen[opt] = true
result = append(result, opt)
}
}
}
return result
})

// 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")
Expand All @@ -54,6 +113,206 @@ var restartPolicies = []string{
string(container.RestartPolicyUnlessStopped),
}

// addCompletions adds the completions that `run` and `create` have in common.
func addCompletions(cmd *cobra.Command, dockerCLI completion.APIClientProvider) {
_ = cmd.RegisterFlagCompletionFunc("attach", completion.FromList("stderr", "stdin", "stdout"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh; I was working on this one, when I discovered completion doesn't work for specifying --attach multiple times.

I started looking "why???" because it does work for some other flags we define multiple times, which is when I discovered the reason; Cobra has some "magic" values for flag "types"; if the flag-type ends with Slice or Array it considers that it can be used multiple times. If not, it considers that the flag can only be set once and it removes it from the suggestions;

if !flag.Changed ||
strings.Contains(flag.Value.Type(), "Slice") ||
strings.Contains(flag.Value.Type(), "Array") {
// If the flag is not already present, or if it can be specified multiple times (Array or Slice)
// we suggest it as a completion
completions = append(completions, getFlagNameCompletions(flag, toComplete)...)
}

In our case, some types use list as name, which isn't matched. I'm considering contributing a patch to Cobra to make it somewhat smarter;

  • make the matching case-insensitive
  • possibly add list to the magic strings

But also to have it match SliceValue, which is an interface defined by the pflag module used by Cobra for flags. I think it's an omission that it doesn't consider that interface;

type SliceValue interface {
// Append adds the specified value to the end of the flag value list.
Append(string) error
// Replace will fully overwrite any data currently in the flag value list.
Replace([]string) error
// GetSlice returns the flag value list as an array of strings.
GetSlice() []string
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, now I understand.
I was also wondering why the events --filter completion only works once.

_ = cmd.RegisterFlagCompletionFunc("cap-add", completeLinuxCapabilityNames)
_ = cmd.RegisterFlagCompletionFunc("cap-drop", completeLinuxCapabilityNames)
_ = cmd.RegisterFlagCompletionFunc("cgroupns", completeCgroupns())
_ = cmd.RegisterFlagCompletionFunc("env", completion.EnvVarNames)
_ = cmd.RegisterFlagCompletionFunc("env-file", completion.FileNames)
_ = cmd.RegisterFlagCompletionFunc("ipc", completeIpc(dockerCLI))
_ = cmd.RegisterFlagCompletionFunc("link", completeLink(dockerCLI))
_ = cmd.RegisterFlagCompletionFunc("log-driver", completeLogDriver(dockerCLI))
_ = cmd.RegisterFlagCompletionFunc("log-opt", completeLogOpt)
_ = 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)
_ = cmd.RegisterFlagCompletionFunc("security-opt", completeSecurityOpt)
_ = cmd.RegisterFlagCompletionFunc("stop-signal", completeSignals)
_ = cmd.RegisterFlagCompletionFunc("storage-opt", completeStorageOpt)
_ = cmd.RegisterFlagCompletionFunc("ulimit", completeUlimit)
_ = cmd.RegisterFlagCompletionFunc("userns", completion.FromList("host"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self; looks like we don't define an enum/consts for this (there's only a single option, so maybe not really needed, but we do define a type for this;

// UsernsMode represents userns mode in the container.
type UsernsMode string

_ = cmd.RegisterFlagCompletionFunc("uts", completion.FromList("host"))
_ = cmd.RegisterFlagCompletionFunc("volume-driver", completeVolumeDriver(dockerCLI))
_ = cmd.RegisterFlagCompletionFunc("volumes-from", completion.ContainerNames(dockerCLI, true))
}

func completeCgroupns() completion.ValidArgsFn {
return completion.FromList(string(container.CgroupnsModeHost), string(container.CgroupnsModePrivate))
}

// completeDetachKeys implements shell completion for the `--detach-keys` option of `run` and `create`.
func completeDetachKeys(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{"ctrl-"}, cobra.ShellCompDirectiveNoSpace
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self; I need to lookup if there's other special ones we define (meta-, alt-, possibly others)

}

// completeIpc implements shell completion for the `--ipc` option of `run` and `create`.
// The completion is partly composite.
func completeIpc(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{
string(container.IPCModeContainer + ":"),
string(container.IPCModeHost),
string(container.IPCModeNone),
string(container.IPCModePrivate),
string(container.IPCModeShareable),
}, cobra.ShellCompDirectiveNoFileComp
}
}

// completeLink implements shell completion for the `--link` option of `run` and `create`.
func completeLink(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) {
return postfixWith(":", containerNames(dockerCLI, cmd, args, toComplete)), cobra.ShellCompDirectiveNoSpace
}
}

// completeLogDriver implements shell completion for the `--log-driver` option of `run` and `create`.
// The log drivers are collected from a call to the Info endpoint with a fallback to a hard-coded list
// of the build-in log drivers.
func completeLogDriver(dockerCLI completion.APIClientProvider) completion.ValidArgsFn {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
info, err := dockerCLI.Client().Info(cmd.Context())
if err != nil {
return builtInLogDrivers(), cobra.ShellCompDirectiveNoFileComp
}
drivers := info.Plugins.Log
return drivers, cobra.ShellCompDirectiveNoFileComp
}
}

// completeLogOpt implements shell completion for the `--log-opt` option of `run` and `create`.
// If the user supplied a log-driver, only options for that driver are returned.
func completeLogOpt(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
driver, _ := cmd.Flags().GetString("log-driver")
if options, exists := logDriverOptions[driver]; exists {
return postfixWith("=", options), cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp
}
return postfixWith("=", allLogDriverOptions()), cobra.ShellCompDirectiveNoSpace
}

// 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self; looks like (unlike IPCMode) we don't define consts for this yet.

}
}

// completeSecurityOpt implements shell completion for the `--security-opt` option of `run` and `create`.
// The completion is partly composite.
func completeSecurityOpt(_ *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(toComplete) > 0 && strings.HasPrefix("apparmor=", toComplete) { //nolint:gocritic // not swapped, matches partly typed "apparmor="
return []string{"apparmor="}, cobra.ShellCompDirectiveNoSpace
}
if len(toComplete) > 0 && strings.HasPrefix("label", toComplete) { //nolint:gocritic // not swapped, matches partly typed "label"
return []string{"label="}, cobra.ShellCompDirectiveNoSpace
}
if strings.HasPrefix(toComplete, "label=") {
if strings.HasPrefix(toComplete, "label=d") {
return []string{"label=disable"}, cobra.ShellCompDirectiveNoFileComp
}
labels := []string{"disable", "level:", "role:", "type:", "user:"}
return prefixWith("label=", labels), cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp
}
// length must be > 1 here so that completion of "s" falls through.
if len(toComplete) > 1 && strings.HasPrefix("seccomp", toComplete) { //nolint:gocritic // not swapped, matches partly typed "seccomp"
return []string{"seccomp="}, cobra.ShellCompDirectiveNoSpace
}
if strings.HasPrefix(toComplete, "seccomp=") {
return []string{"seccomp=unconfined"}, cobra.ShellCompDirectiveNoFileComp
}
return []string{"apparmor=", "label=", "no-new-privileges", "seccomp=", "systempaths=unconfined"}, cobra.ShellCompDirectiveNoFileComp
}

// completeStorageOpt implements shell completion for the `--storage-opt` option of `run` and `create`.
func completeStorageOpt(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{"size="}, cobra.ShellCompDirectiveNoSpace
}

// completeUlimit implements shell completion for the `--ulimit` option of `run` and `create`.
func completeUlimit(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
limits := []string{
"as",
"chroot",
"core",
"cpu",
"data",
"fsize",
"locks",
"maxlogins",
"maxsyslogins",
"memlock",
"msgqueue",
"nice",
"nofile",
"nproc",
"priority",
"rss",
"rtprio",
"sigpending",
"stack",
}
return postfixWith("=", limits), cobra.ShellCompDirectiveNoSpace
}

// completeVolumeDriver contacts the API to get the built-in and installed volume drivers.
func completeVolumeDriver(dockerCLI completion.APIClientProvider) completion.ValidArgsFn {
return func(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
info, err := dockerCLI.Client().Info(cmd.Context())
if err != nil {
// fallback: the built-in drivers
return []string{"local"}, cobra.ShellCompDirectiveNoFileComp
}
drivers := info.Plugins.Volume
return drivers, 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 {
names, _ := completion.ContainerNames(dockerCLI, true)(cmd, args, toComplete)
if names == nil {
return []string{}
}
Comment on lines +291 to +294
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious; is this a bug in completion.ContainerNames and should we update that one?

Copy link
Collaborator Author

@albers albers Oct 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

completion.ContainerNames is consistent with the other helper functions in completion.go.
They all return nil, cobra.ShellCompDirectiveError in case of api errors, which is an appropriate way of signaling completion problems.

I just wanted to get a list of container names that I can feed into postfixWith directly without having to bother with api errors.
That's why I adapted the existing function to my use case.
I had to adapt anyway because I had to get rid of the ShellCompDirective.

From my point of view, a better solution would have been to pull out a function from completion.ContainerNames that gets the container names directly as an array (no nil).
But this would have caused naming issues: This function should obviously be named completion.ContainerNames and thus collide with the existing function.
So the existing function, and for consistency similar functions, would all have to be renamed (completion.ContainerNames -> completion.CompleteContainerNames, completion.NetworkNames -> completion.NetworkNames, ...)

return names
}

// prefixWith prefixes every element in the slice with the given prefix.
func prefixWith(prefix string, values []string) []string {
result := make([]string, len(values))
for i, v := range values {
result[i] = prefix + v
}
return result
}

// postfixWith appends postfix to every element in the slice.
func postfixWith(postfix string, values []string) []string {
result := make([]string, len(values))
for i, v := range values {
result[i] = v + postfix
}
return result
}

func completeLinuxCapabilityNames(cmd *cobra.Command, args []string, toComplete string) (names []string, _ cobra.ShellCompDirective) {
return completion.FromList(allLinuxCapabilities()...)(cmd, args, toComplete)
}
Expand Down
100 changes: 100 additions & 0 deletions cli/command/container/completion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import (
"strings"
"testing"

"github.com/docker/cli/internal/test"
"github.com/docker/cli/internal/test/builders"
"github.com/docker/docker/api/types/container"
"github.com/moby/sys/signal"
"github.com/spf13/cobra"
"gotest.tools/v3/assert"
Expand All @@ -21,13 +24,110 @@ func TestCompleteLinuxCapabilityNames(t *testing.T) {
}
}

func TestCompletePid(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you decide to change the name then the test should probably also be renamed; TestCompletePIDMode

tests := []struct {
containerListFunc func(container.ListOptions) ([]container.Summary, 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) ([]container.Summary, error) {
return []container.Summary{
*builders.Container("c1"),
*builders.Container("c2"),
}, nil
},
toComplete: "container:",
expectedCompletions: []string{"container:c1", "container:c2"},
expectedDirective: cobra.ShellCompDirectiveNoFileComp,
},
}

for _, tc := range tests {
tc := tc
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")
expected := restartPolicies
assert.Check(t, is.DeepEqual(values, expected))
}

func TestCompleteSecurityOpts(t *testing.T) {
tests := []struct {
toComplete string
expectedCompletions []string
expectedDirective cobra.ShellCompDirective
}{
{
toComplete: "",
expectedCompletions: []string{"apparmor=", "label=", "no-new-privileges", "seccomp=", "systempaths=unconfined"},
expectedDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
toComplete: "apparmor=",
expectedCompletions: []string{"apparmor="},
expectedDirective: cobra.ShellCompDirectiveNoSpace,
},
{
toComplete: "label=",
expectedCompletions: []string{"label=disable", "label=level:", "label=role:", "label=type:", "label=user:"},
expectedDirective: cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp,
},
{
toComplete: "s",
// We do not filter matching completions but delegate this task to the shell script.
expectedCompletions: []string{"apparmor=", "label=", "no-new-privileges", "seccomp=", "systempaths=unconfined"},
expectedDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
toComplete: "se",
expectedCompletions: []string{"seccomp="},
expectedDirective: cobra.ShellCompDirectiveNoSpace,
},
{
toComplete: "seccomp=",
expectedCompletions: []string{"seccomp=unconfined"},
expectedDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
toComplete: "sy",
expectedCompletions: []string{"apparmor=", "label=", "no-new-privileges", "seccomp=", "systempaths=unconfined"},
expectedDirective: cobra.ShellCompDirectiveNoFileComp,
},
}

for _, tc := range tests {
albers marked this conversation as resolved.
Show resolved Hide resolved
tc := tc
t.Run(tc.toComplete, func(t *testing.T) {
completions, directive := completeSecurityOpt(nil, nil, tc.toComplete)
assert.Check(t, is.DeepEqual(completions, tc.expectedCompletions))
assert.Check(t, is.Equal(directive, tc.expectedDirective))
})
}
}

func TestCompleteSignals(t *testing.T) {
values, directives := completeSignals(nil, nil, "")
assert.Check(t, is.Equal(directives&cobra.ShellCompDirectiveNoFileComp, cobra.ShellCompDirectiveNoFileComp), "Should not perform file completion")
Expand Down
Loading
Loading