diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach.go b/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach.go index 48975f6847c..2c41a1c08ee 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach.go @@ -105,7 +105,7 @@ func NewCmdAttach(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra Short: i18n.T("Attach to a running container"), Long: i18n.T("Attach to a process that is already running inside an existing container."), Example: attachExample, - ValidArgsFunction: util.ResourceNameCompletionFunc(f, "pod"), + ValidArgsFunction: util.PodResourceNameCompletionFunc(f), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.Validate()) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/exec/exec.go b/staging/src/k8s.io/kubectl/pkg/cmd/exec/exec.go index 436e547d380..ac6d278125f 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/exec/exec.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/exec/exec.go @@ -89,7 +89,7 @@ func NewCmdExec(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.C Short: i18n.T("Execute a command in a container"), Long: i18n.T("Execute a command in a container."), Example: execExample, - ValidArgsFunction: util.ResourceNameCompletionFunc(f, "pod"), + ValidArgsFunction: util.PodResourceNameCompletionFunc(f), Run: func(cmd *cobra.Command, args []string) { argsLenAtDash := cmd.ArgsLenAtDash() cmdutil.CheckErr(options.Complete(f, cmd, args, argsLenAtDash)) @@ -101,6 +101,8 @@ func NewCmdExec(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.C cmdutil.AddJsonFilenameFlag(cmd.Flags(), &options.FilenameOptions.Filenames, "to use to exec into the resource") // TODO support UID cmdutil.AddContainerVarFlags(cmd, &options.ContainerName, options.ContainerName) + cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc("container", util.ContainerCompletionFunc(f))) + cmd.Flags().BoolVarP(&options.Stdin, "stdin", "i", options.Stdin, "Pass stdin to the container") cmd.Flags().BoolVarP(&options.TTY, "tty", "t", options.TTY, "Stdin is a TTY") cmd.Flags().BoolVarP(&options.Quiet, "quiet", "q", options.Quiet, "Only print output from the remote session") diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/get/get.go b/staging/src/k8s.io/kubectl/pkg/cmd/get/get.go index 3a0d2481179..6a2b31ceeeb 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/get/get.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/get/get.go @@ -163,18 +163,7 @@ func NewCmdGet(parent string, f cmdutil.Factory, streams genericclioptions.IOStr Short: i18n.T("Display one or many resources"), Long: getLong + "\n\n" + cmdutil.SuggestAPIResources(parent), Example: getExample, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - comps = apiresources.CompGetResourceList(f, cmd, toComplete) - } else { - comps = CompGetResource(f, cmd, args[0], toComplete) - if len(args) > 1 { - comps = cmdutil.Difference(comps, args[1:]) - } - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: ResourceTypeAndNameCompletionFunc(f, nil, true), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.Validate(cmd)) @@ -920,3 +909,82 @@ func CompGetFromTemplate(template *string, f cmdutil.Factory, namespace string, } return comps } + +// ResourceTypeAndNameCompletionFunc Returns a completion function that completes resource types +// and resource names that match the toComplete prefix. It supports the / form. +func ResourceTypeAndNameCompletionFunc(f cmdutil.Factory, allowedTypes []string, allowRepeat bool) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { + return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + directive := cobra.ShellCompDirectiveNoFileComp + + if len(args) > 0 && !strings.Contains(args[0], "/") { + // The first argument is of the form (e.g., pods) + // All following arguments should be a resource name. + if allowRepeat || len(args) == 1 { + comps = CompGetResource(f, cmd, args[0], toComplete) + + // Remove choices already on the command-line + if len(args) > 1 { + comps = cmdutil.Difference(comps, args[1:]) + } + } + } else { + slashIdx := strings.Index(toComplete, "/") + if slashIdx == -1 { + if len(args) == 0 { + // We are completing the first argument. We default to the normal + // form (not the form /). + // So we suggest resource types and let the shell add a space after + // the completion. + if len(allowedTypes) == 0 { + comps = apiresources.CompGetResourceList(f, cmd, toComplete) + } else { + for _, c := range allowedTypes { + if strings.HasPrefix(c, toComplete) { + comps = append(comps, c) + } + } + } + } else { + // Here we know the first argument contains a / (/). + // All other arguments must also use that form. + if allowRepeat { + // Since toComplete does not already contain a / we know we are completing a + // resource type. Disable adding a space after the completion, and add the / + directive |= cobra.ShellCompDirectiveNoSpace + + if len(allowedTypes) == 0 { + typeComps := apiresources.CompGetResourceList(f, cmd, toComplete) + for _, c := range typeComps { + comps = append(comps, fmt.Sprintf("%s/", c)) + } + } else { + for _, c := range allowedTypes { + if strings.HasPrefix(c, toComplete) { + comps = append(comps, fmt.Sprintf("%s/", c)) + } + } + } + } + } + } else { + // We are completing an argument of the form / + // and since the / is already present, we are completing the resource name. + if allowRepeat || len(args) == 0 { + resourceType := toComplete[:slashIdx] + toComplete = toComplete[slashIdx+1:] + nameComps := CompGetResource(f, cmd, resourceType, toComplete) + for _, c := range nameComps { + comps = append(comps, fmt.Sprintf("%s/%s", resourceType, c)) + } + + // Remove choices already on the command-line. + if len(args) > 0 { + comps = cmdutil.Difference(comps, args[0:]) + } + } + } + } + return comps, directive + } +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/portforward/portforward.go b/staging/src/k8s.io/kubectl/pkg/cmd/portforward/portforward.go index 6c9fdba5c86..4f7c771ef88 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/portforward/portforward.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/portforward/portforward.go @@ -109,7 +109,7 @@ func NewCmdPortForward(f cmdutil.Factory, streams genericclioptions.IOStreams) * Short: i18n.T("Forward one or more local ports to a pod"), Long: portforwardLong, Example: portforwardExample, - ValidArgsFunction: util.ResourceNameCompletionFunc(f, "pod"), + ValidArgsFunction: util.PodResourceNameCompletionFunc(f), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(opts.Complete(f, cmd, args)) cmdutil.CheckErr(opts.Validate()) diff --git a/staging/src/k8s.io/kubectl/pkg/util/completion.go b/staging/src/k8s.io/kubectl/pkg/util/completion.go index 5ccd16306a6..f9c0e628b83 100644 --- a/staging/src/k8s.io/kubectl/pkg/util/completion.go +++ b/staging/src/k8s.io/kubectl/pkg/util/completion.go @@ -17,12 +17,15 @@ limitations under the License. package util import ( + "fmt" "strings" + "time" "github.com/spf13/cobra" - "k8s.io/kubectl/pkg/cmd/apiresources" "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/kubectl/pkg/polymorphichelpers" + "k8s.io/kubectl/pkg/scheme" ) var factory cmdutil.Factory @@ -33,61 +36,32 @@ func SetFactoryForCompletion(f cmdutil.Factory) { factory = f } -// ResourceTypeAndNameCompletionFunc Returns a completion function that completes as a first argument -// the resource types that match the toComplete prefix, and all following arguments as resource names that match -// the toComplete prefix. +// ResourceTypeAndNameCompletionFunc Returns a completion function that completes resource types +// and resource names that match the toComplete prefix. It supports the / form. func ResourceTypeAndNameCompletionFunc(f cmdutil.Factory) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { - return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - comps = apiresources.CompGetResourceList(f, cmd, toComplete) - } else { - comps = get.CompGetResource(f, cmd, args[0], toComplete) - if len(args) > 1 { - comps = cmdutil.Difference(comps, args[1:]) - } - } - return comps, cobra.ShellCompDirectiveNoFileComp - } + // The logic is in the 'get' package because the 'get' command must be able to call it also + return get.ResourceTypeAndNameCompletionFunc(f, nil, true) } -// SpecifiedResourceTypeAndNameCompletionFunc Returns a completion function that completes as a first -// argument the resource types that match the toComplete prefix and are limited to the allowedTypes, -// and all following arguments as resource names that match the toComplete prefix. +// SpecifiedResourceTypeAndNameCompletionFunc Returns a completion function that completes resource +// types limited to the specified allowedTypes, and resource names that match the toComplete prefix. +// It allows for multiple resources. It supports the / form. func SpecifiedResourceTypeAndNameCompletionFunc(f cmdutil.Factory, allowedTypes []string) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { - return doSpecifiedResourceTypeAndNameComp(f, allowedTypes, true) + return get.ResourceTypeAndNameCompletionFunc(f, allowedTypes, true) } -// SpecifiedResourceTypeAndNameNoRepeatCompletionFunc Returns a completion function that completes as a first -// argument the resource types that match the toComplete prefix and are limited to the allowedTypes, and as -// a second argument a resource name that match the toComplete prefix. +// SpecifiedResourceTypeAndNameNoRepeatCompletionFunc Returns a completion function that completes resource +// types limited to the specified allowedTypes, and resource names that match the toComplete prefix. +// It only allows for one resource. It supports the / form. func SpecifiedResourceTypeAndNameNoRepeatCompletionFunc(f cmdutil.Factory, allowedTypes []string) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { - return doSpecifiedResourceTypeAndNameComp(f, allowedTypes, false) -} - -func doSpecifiedResourceTypeAndNameComp(f cmdutil.Factory, allowedTypes []string, repeat bool) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { - return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - for _, comp := range allowedTypes { - if strings.HasPrefix(comp, toComplete) { - comps = append(comps, comp) - } - } - } else { - if repeat || len(args) == 1 { - comps = get.CompGetResource(f, cmd, args[0], toComplete) - if repeat && len(args) > 1 { - comps = cmdutil.Difference(comps, args[1:]) - } - } - } - return comps, cobra.ShellCompDirectiveNoFileComp - } + return get.ResourceTypeAndNameCompletionFunc(f, allowedTypes, false) } // ResourceNameCompletionFunc Returns a completion function that completes as a first argument // the resource names specified by the resourceType parameter, and which match the toComplete prefix. +// This function does NOT support the / form: it is meant to be used by commands +// that don't support that form. For commands that apply to pods and that support the / +// form, please use PodResourceNameCompletionFunc() func ResourceNameCompletionFunc(f cmdutil.Factory, resourceType string) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { var comps []string @@ -98,16 +72,49 @@ func ResourceNameCompletionFunc(f cmdutil.Factory, resourceType string) func(*co } } -// PodResourceNameAndContainerCompletionFunc Returns a completion function that completes as a first -// argument pod names that match the toComplete prefix, and as a second argument the containers -// within the specified pod. +// PodResourceNameCompletionFunc Returns a completion function that completes: +// 1- pod names that match the toComplete prefix +// 2- resource types containing pods which match the toComplete prefix +func PodResourceNameCompletionFunc(f cmdutil.Factory) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { + return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + directive := cobra.ShellCompDirectiveNoFileComp + if len(args) == 0 { + comps, directive = doPodResourceCompletion(f, cmd, toComplete) + } + return comps, directive + } +} + +// PodResourceNameAndContainerCompletionFunc Returns a completion function that completes, as a first argument: +// 1- pod names that match the toComplete prefix +// 2- resource types containing pods which match the toComplete prefix +// and as a second argument the containers within the specified pod. func PodResourceNameAndContainerCompletionFunc(f cmdutil.Factory) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { var comps []string + directive := cobra.ShellCompDirectiveNoFileComp if len(args) == 0 { - comps = get.CompGetResource(f, cmd, "pod", toComplete) + comps, directive = doPodResourceCompletion(f, cmd, toComplete) } else if len(args) == 1 { - comps = get.CompGetContainers(f, cmd, args[0], toComplete) + podName := convertResourceNameToPodName(f, args[0]) + comps = get.CompGetContainers(f, cmd, podName, toComplete) + } + return comps, directive + } +} + +// ContainerCompletionFunc Returns a completion function that completes the containers within the +// pod specified by the first argument. The resource containing the pod can be specified in +// the / form. +func ContainerCompletionFunc(f cmdutil.Factory) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { + return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + // We need the pod name to be able to complete the container names, it must be in args[0]. + // That first argument can also be of the form / so we need to convert it. + if len(args) > 0 { + podName := convertResourceNameToPodName(f, args[0]) + comps = get.CompGetContainers(f, cmd, podName, toComplete) } return comps, cobra.ShellCompDirectiveNoFileComp } @@ -184,3 +191,85 @@ func ListUsersInConfig(toComplete string) []string { } return ret } + +// doPodResourceCompletion Returns completions of: +// 1- pod names that match the toComplete prefix +// 2- resource types containing pods which match the toComplete prefix +func doPodResourceCompletion(f cmdutil.Factory, cmd *cobra.Command, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + directive := cobra.ShellCompDirectiveNoFileComp + slashIdx := strings.Index(toComplete, "/") + if slashIdx == -1 { + // Standard case, complete pod names + comps = get.CompGetResource(f, cmd, "pod", toComplete) + + // Also include resource choices for the / form, + // but only for resources that contain pods + resourcesWithPods := []string{ + "daemonsets", + "deployments", + "pods", + "jobs", + "replicasets", + "replicationcontrollers", + "services", + "statefulsets"} + + if len(comps) == 0 { + // If there are no pods to complete, we will only be completing + // /. We should disable adding a space after the /. + directive |= cobra.ShellCompDirectiveNoSpace + } + + for _, resource := range resourcesWithPods { + if strings.HasPrefix(resource, toComplete) { + comps = append(comps, fmt.Sprintf("%s/", resource)) + } + } + } else { + // Dealing with the / form, use the specified resource type + resourceType := toComplete[:slashIdx] + toComplete = toComplete[slashIdx+1:] + nameComps := get.CompGetResource(f, cmd, resourceType, toComplete) + for _, c := range nameComps { + comps = append(comps, fmt.Sprintf("%s/%s", resourceType, c)) + } + } + return comps, directive +} + +// convertResourceNameToPodName Converts a resource name to a pod name. +// If the resource name is of the form /, we use +// polymorphichelpers.AttachablePodForObjectFn(), if not, the resource name +// is already a pod name. +func convertResourceNameToPodName(f cmdutil.Factory, resourceName string) string { + var podName string + if !strings.Contains(resourceName, "/") { + // When we don't have the / form, the resource name is the pod name + podName = resourceName + } else { + // if the resource name is of the form /, we need to convert it to a pod name + ns, _, err := f.ToRawKubeConfigLoader().Namespace() + if err != nil { + return "" + } + + resourceWithPod, err := f.NewBuilder(). + WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...). + ContinueOnError(). + NamespaceParam(ns).DefaultNamespace(). + ResourceNames("pods", resourceName). + Do().Object() + if err != nil { + return "" + } + + // For shell completion, use a short timeout + forwardablePod, err := polymorphichelpers.AttachablePodForObjectFn(f, resourceWithPod, 100*time.Millisecond) + if err != nil { + return "" + } + podName = forwardablePod.Name + } + return podName +} diff --git a/staging/src/k8s.io/kubectl/pkg/util/completion_test.go b/staging/src/k8s.io/kubectl/pkg/util/completion_test.go index 21e95eedc24..aff36504110 100644 --- a/staging/src/k8s.io/kubectl/pkg/util/completion_test.go +++ b/staging/src/k8s.io/kubectl/pkg/util/completion_test.go @@ -127,6 +127,25 @@ func TestResourceTypeAndNameCompletionFuncRepeating(t *testing.T) { checkCompletion(t, comps, []string{"foo"}, directive, cobra.ShellCompDirectiveNoFileComp) } +func TestResourceTypeAndNameCompletionFuncJointForm(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := ResourceTypeAndNameCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{}, "pod/b") + checkCompletion(t, comps, []string{"pod/bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestResourceTypeAndNameCompletionFuncJointFormRepeating(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := ResourceTypeAndNameCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{"pod/bar"}, "pod/") + // The other pods should be completed, but not the already specified ones + checkCompletion(t, comps, []string{"pod/foo"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + func TestSpecifiedResourceTypeAndNameCompletionFuncNoArgs(t *testing.T) { tf, cmd := prepareCompletionTest() addPodsToFactory(tf) @@ -155,6 +174,24 @@ func TestSpecifiedResourceTypeAndNameCompletionFuncRepeating(t *testing.T) { checkCompletion(t, comps, []string{"foo"}, directive, cobra.ShellCompDirectiveNoFileComp) } +func TestSpecifiedResourceTypeAndNameCompletionFuncJointFormOneArg(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := SpecifiedResourceTypeAndNameCompletionFunc(tf, []string{"pod"}) + comps, directive := compFunc(cmd, []string{}, "pod/b") + checkCompletion(t, comps, []string{"pod/bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestSpecifiedResourceTypeAndNameCompletionFuncJointFormRepeating(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := SpecifiedResourceTypeAndNameCompletionFunc(tf, []string{"pod"}) + comps, directive := compFunc(cmd, []string{"pod/bar"}, "pod/") + // The other pods should be completed, but not the already specified ones + checkCompletion(t, comps, []string{"pod/foo"}, directive, cobra.ShellCompDirectiveNoFileComp) +} func TestSpecifiedResourceTypeAndNameCompletionNoRepeatFuncOneArg(t *testing.T) { tf, cmd := prepareCompletionTest() addPodsToFactory(tf) @@ -174,6 +211,25 @@ func TestSpecifiedResourceTypeAndNameCompletionNoRepeatFuncMultiArg(t *testing.T checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) } +func TestSpecifiedResourceTypeAndNameCompletionNoRepeatFuncJointFormOneArg(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := SpecifiedResourceTypeAndNameNoRepeatCompletionFunc(tf, []string{"pod"}) + comps, directive := compFunc(cmd, []string{}, "pod/b") + checkCompletion(t, comps, []string{"pod/bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestSpecifiedResourceTypeAndNameCompletionNoRepeatFuncJointFormMultiArg(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := SpecifiedResourceTypeAndNameNoRepeatCompletionFunc(tf, []string{"pod"}) + comps, directive := compFunc(cmd, []string{"pod/bar"}, "pod/") + // There should not be any more pods shown as this function should not repeat the completion + checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) +} + func TestResourceNameCompletionFuncNoArgs(t *testing.T) { tf, cmd := prepareCompletionTest() addPodsToFactory(tf) @@ -192,7 +248,65 @@ func TestResourceNameCompletionFuncTooManyArgs(t *testing.T) { checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) } -func TestPodResourceNameAndContainerCompletionFuncNoArgs(t *testing.T) { +func TestResourceNameCompletionFuncJointFormNoArgs(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := ResourceNameCompletionFunc(tf, "pod") + comps, directive := compFunc(cmd, []string{}, "pod/b") + // The / should NOT be supported by this function + checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestPodResourceNameCompletionFuncNoArgsPodName(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := PodResourceNameCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{}, "b") + checkCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestPodResourceNameCompletionFuncNoArgsResources(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := PodResourceNameCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{}, "d") + checkCompletion( + t, comps, []string{"daemonsets/", "deployments/"}, + directive, cobra.ShellCompDirectiveNoFileComp|cobra.ShellCompDirectiveNoSpace) +} + +func TestPodResourceNameCompletionFuncTooManyArgs(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := PodResourceNameCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{"pod-name"}, "") + checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestPodResourceNameCompletionFuncJointFormNoArgs(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := PodResourceNameCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{}, "pod/b") + // The / SHOULD be supported by this function + checkCompletion(t, comps, []string{"pod/bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestPodResourceNameCompletionFuncJointFormTooManyArgs(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := PodResourceNameCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{"pod/name"}, "pod/b") + checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestPodResourceNameAndContainerCompletionFuncNoArgsPodName(t *testing.T) { tf, cmd := prepareCompletionTest() addPodsToFactory(tf) @@ -201,6 +315,18 @@ func TestPodResourceNameAndContainerCompletionFuncNoArgs(t *testing.T) { checkCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) } +func TestPodResourceNameAndContainerCompletionFuncNoArgsResources(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := PodResourceNameAndContainerCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{}, "s") + checkCompletion( + t, comps, []string{"services/", "statefulsets/"}, + directive, cobra.ShellCompDirectiveNoFileComp|cobra.ShellCompDirectiveNoSpace) + +} + func TestPodResourceNameAndContainerCompletionFuncTooManyArgs(t *testing.T) { tf, cmd := prepareCompletionTest() addPodsToFactory(tf) @@ -210,6 +336,24 @@ func TestPodResourceNameAndContainerCompletionFuncTooManyArgs(t *testing.T) { checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) } +func TestPodResourceNameAndContainerCompletionFuncJointFormNoArgs(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := PodResourceNameAndContainerCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{}, "pod/b") + checkCompletion(t, comps, []string{"pod/bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestPodResourceNameAndContainerCompletionFuncJointFormTooManyArgs(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := PodResourceNameAndContainerCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{"pod/pod-name", "container-name"}, "") + checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) +} + func setMockFactory(config api.Config) { clientConfig := clientcmd.NewDefaultClientConfig(config, nil) testFactory := cmdtesting.NewTestFactory().WithClientConfig(clientConfig)