From 663b13e814909070e83642c42969174ce03a0e1e Mon Sep 17 00:00:00 2001 From: knight42 Date: Thu, 6 Aug 2020 23:48:33 +0800 Subject: [PATCH 1/8] refactor: implement custom completions for some flags using Go Only --context, --cluster, --user and --namespace are included. Signed-off-by: knight42 Signed-off-by: Marc Khouzam --- staging/src/k8s.io/kubectl/pkg/cmd/cmd.go | 21 +---- .../src/k8s.io/kubectl/pkg/cmd/completion.go | 46 +++++++++ .../k8s.io/kubectl/pkg/completion/utils.go | 94 +++++++++++++++++++ vendor/modules.txt | 1 + 4 files changed, 142 insertions(+), 20 deletions(-) create mode 100644 staging/src/k8s.io/kubectl/pkg/cmd/completion.go create mode 100644 staging/src/k8s.io/kubectl/pkg/completion/utils.go diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/cmd.go b/staging/src/k8s.io/kubectl/pkg/cmd/cmd.go index 1f94844720ce..aa641545399b 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/cmd.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/cmd.go @@ -325,15 +325,6 @@ __kubectl_custom_func() { const kubectlCmdHeaders = "KUBECTL_COMMAND_HEADERS" -var ( - bashCompletionFlags = map[string]string{ - "namespace": "__kubectl_get_resource_namespace", - "context": "__kubectl_config_get_contexts", - "cluster": "__kubectl_config_get_clusters", - "user": "__kubectl_config_get_users", - } -) - // NewDefaultKubectlCommand creates the `kubectl` command with default arguments func NewDefaultKubectlCommand() *cobra.Command { return NewDefaultKubectlCommandWithArgs(NewDefaultPluginHandler(plugin.ValidPluginFilenamePrefixes), os.Args, os.Stdin, os.Stdout, os.Stderr) @@ -633,17 +624,7 @@ func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command { templates.ActsAsRootCommand(cmds, filters, groups...) - for name, completion := range bashCompletionFlags { - if cmds.Flag(name) != nil { - if cmds.Flag(name).Annotations == nil { - cmds.Flag(name).Annotations = map[string][]string{} - } - cmds.Flag(name).Annotations[cobra.BashCompCustom] = append( - cmds.Flag(name).Annotations[cobra.BashCompCustom], - completion, - ) - } - } + registerCompletionFuncForGlobalFlags(cmds, f) cmds.AddCommand(alpha) cmds.AddCommand(cmdconfig.NewCmdConfig(f, clientcmd.NewDefaultPathOptions(), ioStreams)) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/completion.go b/staging/src/k8s.io/kubectl/pkg/cmd/completion.go new file mode 100644 index 000000000000..a561c07d45bb --- /dev/null +++ b/staging/src/k8s.io/kubectl/pkg/cmd/completion.go @@ -0,0 +1,46 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "github.com/spf13/cobra" + "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/kubectl/pkg/completion" +) + +func registerCompletionFuncForGlobalFlags(cmd *cobra.Command, f util.Factory) { + util.CheckErr(cmd.RegisterFlagCompletionFunc( + "namespace", + func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return completion.ListNamespaces(f, toComplete) + })) + util.CheckErr(cmd.RegisterFlagCompletionFunc( + "context", + func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return completion.ListContextsInKubeconfig(f, toComplete) + })) + util.CheckErr(cmd.RegisterFlagCompletionFunc( + "cluster", + func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return completion.ListClustersInKubeconfig(f, toComplete) + })) + util.CheckErr(cmd.RegisterFlagCompletionFunc( + "user", + func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return completion.ListUsersInKubeconfig(f, toComplete) + })) +} diff --git a/staging/src/k8s.io/kubectl/pkg/completion/utils.go b/staging/src/k8s.io/kubectl/pkg/completion/utils.go new file mode 100644 index 000000000000..bfb9f9bfe8b8 --- /dev/null +++ b/staging/src/k8s.io/kubectl/pkg/completion/utils.go @@ -0,0 +1,94 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package completion + +import ( + "context" + "strings" + "time" + + "github.com/spf13/cobra" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/kubectl/pkg/cmd/util" +) + +// ListNamespaces returns a list of namespaces which begins with `toComplete`. +func ListNamespaces(f util.Factory, toComplete string) ([]string, cobra.ShellCompDirective) { + clientSet, err := f.KubernetesClientSet() + if err != nil { + return nil, cobra.ShellCompDirectiveDefault + } + ctx, cancel := context.WithTimeout(context.TODO(), time.Second*3) + defer cancel() + namespaces, err := clientSet.CoreV1().Namespaces().List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, cobra.ShellCompDirectiveDefault + } + var ret []string + for _, ns := range namespaces.Items { + if strings.HasPrefix(ns.Name, toComplete) { + ret = append(ret, ns.Name) + } + } + return ret, cobra.ShellCompDirectiveNoFileComp +} + +// ListContextsInKubeconfig returns a list of context names which begins with `toComplete`. +func ListContextsInKubeconfig(f util.Factory, toComplete string) ([]string, cobra.ShellCompDirective) { + config, err := f.ToRawKubeConfigLoader().RawConfig() + if err != nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + var ret []string + for name := range config.Contexts { + if strings.HasPrefix(name, toComplete) { + ret = append(ret, name) + } + } + return ret, cobra.ShellCompDirectiveNoFileComp +} + +// ListClustersInKubeconfig returns a list of cluster names which begins with `toComplete`. +func ListClustersInKubeconfig(f util.Factory, toComplete string) ([]string, cobra.ShellCompDirective) { + config, err := f.ToRawKubeConfigLoader().RawConfig() + if err != nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + var ret []string + for name := range config.Clusters { + if strings.HasPrefix(name, toComplete) { + ret = append(ret, name) + } + } + return ret, cobra.ShellCompDirectiveNoFileComp +} + +// ListUsersInKubeconfig returns a list of user names which begins with `toComplete`. +func ListUsersInKubeconfig(f util.Factory, toComplete string) ([]string, cobra.ShellCompDirective) { + config, err := f.ToRawKubeConfigLoader().RawConfig() + if err != nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + var ret []string + for name := range config.AuthInfos { + if strings.HasPrefix(name, toComplete) { + ret = append(ret, name) + } + } + return ret, cobra.ShellCompDirectiveNoFileComp +} diff --git a/vendor/modules.txt b/vendor/modules.txt index ebc4e11be34f..4064ac74e0e2 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -2070,6 +2070,7 @@ k8s.io/kubectl/pkg/cmd/util/podcmd k8s.io/kubectl/pkg/cmd/util/sanity k8s.io/kubectl/pkg/cmd/version k8s.io/kubectl/pkg/cmd/wait +k8s.io/kubectl/pkg/completion k8s.io/kubectl/pkg/describe k8s.io/kubectl/pkg/drain k8s.io/kubectl/pkg/explain From 879cdc5fa9e518ce76b9e1dc7e795132457966f3 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Fri, 5 Mar 2021 15:43:31 -0500 Subject: [PATCH 2/8] Move all completion bash code to Go code Signed-off-by: Marc Khouzam --- .../kubectl/pkg/cmd/annotate/annotate.go | 11 + .../pkg/cmd/apiresources/apiresources.go | 38 +++ .../pkg/cmd/apply/apply_edit_last_applied.go | 11 + .../pkg/cmd/apply/apply_view_last_applied.go | 11 + .../k8s.io/kubectl/pkg/cmd/attach/attach.go | 8 + .../kubectl/pkg/cmd/autoscale/autoscale.go | 16 +- staging/src/k8s.io/kubectl/pkg/cmd/cmd.go | 268 ++---------------- .../src/k8s.io/kubectl/pkg/cmd/completion.go | 46 --- .../k8s.io/kubectl/pkg/cmd/config/config.go | 60 ++++ .../kubectl/pkg/cmd/config/delete_cluster.go | 6 + .../kubectl/pkg/cmd/config/delete_context.go | 6 + .../kubectl/pkg/cmd/config/rename_context.go | 6 + .../kubectl/pkg/cmd/config/use_context.go | 6 + staging/src/k8s.io/kubectl/pkg/cmd/cp/cp.go | 52 ++++ .../cmd/create/create_clusterrolebinding.go | 10 +- .../k8s.io/kubectl/pkg/cmd/delete/delete.go | 11 + .../kubectl/pkg/cmd/describe/describe.go | 11 + .../src/k8s.io/kubectl/pkg/cmd/drain/drain.go | 22 ++ .../src/k8s.io/kubectl/pkg/cmd/edit/edit.go | 11 + .../src/k8s.io/kubectl/pkg/cmd/exec/exec.go | 8 + .../k8s.io/kubectl/pkg/cmd/expose/expose.go | 15 +- staging/src/k8s.io/kubectl/pkg/cmd/get/get.go | 71 +++++ .../src/k8s.io/kubectl/pkg/cmd/label/label.go | 11 + .../src/k8s.io/kubectl/pkg/cmd/logs/logs.go | 10 + .../src/k8s.io/kubectl/pkg/cmd/patch/patch.go | 11 + .../pkg/cmd/portforward/portforward.go | 8 + .../pkg/cmd/rollout/rollout_history.go | 16 +- .../kubectl/pkg/cmd/rollout/rollout_pause.go | 16 +- .../pkg/cmd/rollout/rollout_restart.go | 16 +- .../kubectl/pkg/cmd/rollout/rollout_resume.go | 16 +- .../kubectl/pkg/cmd/rollout/rollout_status.go | 16 +- .../kubectl/pkg/cmd/rollout/rollout_undo.go | 16 +- .../src/k8s.io/kubectl/pkg/cmd/scale/scale.go | 16 +- .../src/k8s.io/kubectl/pkg/cmd/taint/taint.go | 15 +- .../k8s.io/kubectl/pkg/cmd/top/top_node.go | 8 + .../src/k8s.io/kubectl/pkg/cmd/top/top_pod.go | 8 + .../k8s.io/kubectl/pkg/completion/utils.go | 94 ------ vendor/modules.txt | 1 - 38 files changed, 585 insertions(+), 397 deletions(-) delete mode 100644 staging/src/k8s.io/kubectl/pkg/cmd/completion.go delete mode 100644 staging/src/k8s.io/kubectl/pkg/completion/utils.go diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/annotate/annotate.go b/staging/src/k8s.io/kubectl/pkg/cmd/annotate/annotate.go index 00fe8423b6d4..abb081abd651 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/annotate/annotate.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/annotate/annotate.go @@ -35,6 +35,8 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" + "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" @@ -134,6 +136,15 @@ func NewCmdAnnotate(parent string, f cmdutil.Factory, ioStreams genericclioption Short: i18n.T("Update the annotations on a resource"), Long: annotateLong + "\n\n" + cmdutil.SuggestAPIResources(parent), Example: annotateExample, + 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 if len(args) == 1 { + comps = get.CompGetResource(f, cmd, args[0], toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, 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/apiresources/apiresources.go b/staging/src/k8s.io/kubectl/pkg/cmd/apiresources/apiresources.go index b1177a119e08..b0eff26a73c6 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/apiresources/apiresources.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/apiresources/apiresources.go @@ -17,8 +17,11 @@ limitations under the License. package apiresources import ( + "bytes" "fmt" "io" + "io/ioutil" + "os" "sort" "strings" @@ -275,3 +278,38 @@ func (s sortableResource) compareValues(i, j int) (string, string) { } return s.resources[i].APIGroup, s.resources[j].APIGroup } + +// CompGetResourceList returns the list of api resources which begin with `toComplete`. +func CompGetResourceList(f cmdutil.Factory, cmd *cobra.Command, toComplete string) []string { + buf := new(bytes.Buffer) + streams := genericclioptions.IOStreams{In: os.Stdin, Out: buf, ErrOut: ioutil.Discard} + o := NewAPIResourceOptions(streams) + + // Get the list of resources + o.Output = "name" + o.Cached = true + o.Verbs = []string{"get"} + // TODO:Should set --request-timeout=5s + + // Ignore errors as the output may still be valid + o.RunAPIResources(cmd, f) + + // Resources can be a comma-separated list. The last element is then + // the one we should complete. For example if toComplete=="pods,secre" + // we should return "pods,secrets" + prefix := "" + suffix := toComplete + lastIdx := strings.LastIndex(toComplete, ",") + if lastIdx != -1 { + prefix = toComplete[0 : lastIdx+1] + suffix = toComplete[lastIdx+1:] + } + var comps []string + resources := strings.Split(buf.String(), "\n") + for _, res := range resources { + if res != "" && strings.HasPrefix(res, suffix) { + comps = append(comps, fmt.Sprintf("%s%s", prefix, res)) + } + } + return comps +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_edit_last_applied.go b/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_edit_last_applied.go index da7065313c51..ba677d002862 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_edit_last_applied.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_edit_last_applied.go @@ -20,6 +20,8 @@ import ( "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" + "k8s.io/kubectl/pkg/cmd/apiresources" + "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/cmd/util/editor" "k8s.io/kubectl/pkg/util/i18n" @@ -66,6 +68,15 @@ func NewCmdApplyEditLastApplied(f cmdutil.Factory, ioStreams genericclioptions.I Short: i18n.T("Edit latest last-applied-configuration annotations of a resource/object"), Long: applyEditLastAppliedLong, Example: applyEditLastAppliedExample, + 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 if len(args) == 1 { + comps = get.CompGetResource(f, cmd, args[0], toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, args, cmd)) cmdutil.CheckErr(o.Run()) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_view_last_applied.go b/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_view_last_applied.go index 4327ea6aef15..939b926bafc3 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_view_last_applied.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_view_last_applied.go @@ -24,6 +24,8 @@ import ( "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/resource" + "k8s.io/kubectl/pkg/cmd/apiresources" + "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" @@ -77,6 +79,15 @@ func NewCmdApplyViewLastApplied(f cmdutil.Factory, ioStreams genericclioptions.I Short: i18n.T("View latest last-applied-configuration annotations of a resource/object"), Long: applyViewLastAppliedLong, Example: applyViewLastAppliedExample, + 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 if len(args) == 1 { + comps = get.CompGetResource(f, cmd, args[0], toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(options.Complete(cmd, f, args)) cmdutil.CheckErr(options.Validate(cmd)) 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 f7cc2b833c9d..37c48776c9d6 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach.go @@ -31,6 +31,7 @@ import ( restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/remotecommand" "k8s.io/kubectl/pkg/cmd/exec" + "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/cmd/util/podcmd" "k8s.io/kubectl/pkg/polymorphichelpers" @@ -104,6 +105,13 @@ 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: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + if len(args) == 0 { + comps = get.CompGetResource(f, cmd, "pod", toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, 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/autoscale/autoscale.go b/staging/src/k8s.io/kubectl/pkg/cmd/autoscale/autoscale.go index bb09cbec91e0..bf08890d324d 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/autoscale/autoscale.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/autoscale/autoscale.go @@ -19,6 +19,7 @@ package autoscale import ( "context" "fmt" + "strings" "github.com/spf13/cobra" "k8s.io/klog/v2" @@ -31,6 +32,7 @@ import ( "k8s.io/cli-runtime/pkg/resource" autoscalingv1client "k8s.io/client-go/kubernetes/typed/autoscaling/v1" "k8s.io/client-go/scale" + "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/scheme" "k8s.io/kubectl/pkg/util" @@ -107,12 +109,24 @@ func NewCmdAutoscale(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) * Short: i18n.T("Auto-scale a Deployment, ReplicaSet, StatefulSet, or ReplicationController"), Long: autoscaleLong, Example: autoscaleExample, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + if len(args) == 0 { + for _, comp := range validArgs { + if strings.HasPrefix(comp, toComplete) { + comps = append(comps, comp) + } + } + } else if len(args) == 1 { + comps = get.CompGetResource(f, cmd, args[0], toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.Validate()) cmdutil.CheckErr(o.Run()) }, - ValidArgs: validArgs, } // bind flag structs diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/cmd.go b/staging/src/k8s.io/kubectl/pkg/cmd/cmd.go index aa641545399b..f397c334b68b 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/cmd.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/cmd.go @@ -79,250 +79,6 @@ import ( "k8s.io/kubectl/pkg/cmd/kustomize" ) -const ( - bashCompletionFunc = `# call kubectl get $1, -__kubectl_debug_out() -{ - local cmd="$1" - __kubectl_debug "${FUNCNAME[1]}: get completion by ${cmd}" - eval "${cmd} 2>/dev/null" -} - -__kubectl_override_flag_list=(--kubeconfig --cluster --user --context --namespace --server -n -s) -__kubectl_override_flags() -{ - local ${__kubectl_override_flag_list[*]##*-} two_word_of of var - for w in "${words[@]}"; do - if [ -n "${two_word_of}" ]; then - eval "${two_word_of##*-}=\"${two_word_of}=\${w}\"" - two_word_of= - continue - fi - for of in "${__kubectl_override_flag_list[@]}"; do - case "${w}" in - ${of}=*) - eval "${of##*-}=\"${w}\"" - ;; - ${of}) - two_word_of="${of}" - ;; - esac - done - done - for var in "${__kubectl_override_flag_list[@]##*-}"; do - if eval "test -n \"\$${var}\""; then - eval "echo -n \${${var}}' '" - fi - done -} - -__kubectl_config_get_contexts() -{ - __kubectl_parse_config "contexts" -} - -__kubectl_config_get_clusters() -{ - __kubectl_parse_config "clusters" -} - -__kubectl_config_get_users() -{ - __kubectl_parse_config "users" -} - -# $1 has to be "contexts", "clusters" or "users" -__kubectl_parse_config() -{ - local template kubectl_out - template="{{ range .$1 }}{{ .name }} {{ end }}" - if kubectl_out=$(__kubectl_debug_out "kubectl config $(__kubectl_override_flags) -o template --template=\"${template}\" view"); then - COMPREPLY=( $( compgen -W "${kubectl_out[*]}" -- "$cur" ) ) - fi -} - -# $1 is the name of resource (required) -# $2 is template string for kubectl get (optional) -__kubectl_parse_get() -{ - local template - template="${2:-"{{ range .items }}{{ .metadata.name }} {{ end }}"}" - local kubectl_out - if kubectl_out=$(__kubectl_debug_out "kubectl get $(__kubectl_override_flags) -o template --template=\"${template}\" \"$1\""); then - COMPREPLY+=( $( compgen -W "${kubectl_out[*]}" -- "$cur" ) ) - fi -} - -# Same as __kubectl_get_resources (with s) but allows completion for only one resource name. -__kubectl_get_resource() -{ - if [[ ${#nouns[@]} -eq 0 ]]; then - __kubectl_get_resource_helper "" "$cur" - return # the return status is that of the last command executed in the function body - fi - __kubectl_parse_get "${nouns[${#nouns[@]} -1]}" -} - -# Same as __kubectl_get_resource (without s) but allows completion for multiple, comma-separated resource names. -__kubectl_get_resources() -{ - local SEPARATOR=',' - if [[ ${#nouns[@]} -eq 0 ]]; then - local kubectl_out HEAD TAIL - HEAD="" - TAIL="$cur" - # if SEPARATOR is contained in $cur, e.g. "pod,sec" - if [[ "$cur" = *${SEPARATOR}* ]] ; then - # set HEAD to "pod," - HEAD="${cur%${SEPARATOR}*}${SEPARATOR}" - # set TAIL to "sec" - TAIL="${cur##*${SEPARATOR}}" - fi - __kubectl_get_resource_helper "$HEAD" "$TAIL" - return # the return status is that of the last command executed in the function body - fi - __kubectl_parse_get "${nouns[${#nouns[@]} -1]}" -} - -__kubectl_get_resource_helper() -{ - local kubectl_out HEAD TAIL - HEAD="$1" - TAIL="$2" - if kubectl_out=$(__kubectl_debug_out "kubectl api-resources $(__kubectl_override_flags) -o name --cached --request-timeout=5s --verbs=get"); then - COMPREPLY=( $( compgen -P "$HEAD" -W "${kubectl_out[*]}" -- "$TAIL" ) ) - return 0 - fi - return 1 -} - -__kubectl_get_resource_namespace() -{ - __kubectl_parse_get "namespace" -} - -__kubectl_get_resource_pod() -{ - __kubectl_parse_get "pod" -} - -__kubectl_get_resource_rc() -{ - __kubectl_parse_get "rc" -} - -__kubectl_get_resource_node() -{ - __kubectl_parse_get "node" -} - -__kubectl_get_resource_clusterrole() -{ - __kubectl_parse_get "clusterrole" -} - -# $1 is the name of the pod we want to get the list of containers inside -__kubectl_get_containers() -{ - local template - template="{{ range .spec.initContainers }}{{ .name }} {{end}}{{ range .spec.containers }}{{ .name }} {{ end }}" - __kubectl_debug "${FUNCNAME} nouns are ${nouns[*]}" - - local len="${#nouns[@]}" - if [[ ${len} -ne 1 ]]; then - return - fi - local last=${nouns[${len} -1]} - local kubectl_out - if kubectl_out=$(__kubectl_debug_out "kubectl get $(__kubectl_override_flags) -o template --template=\"${template}\" pods \"${last}\""); then - COMPREPLY=( $( compgen -W "${kubectl_out[*]}" -- "$cur" ) ) - fi -} - -# Require both a pod and a container to be specified -__kubectl_require_pod_and_container() -{ - if [[ ${#nouns[@]} -eq 0 ]]; then - __kubectl_parse_get pods - return 0 - fi; - __kubectl_get_containers - return 0 -} - -__kubectl_cp() -{ - if [[ $(type -t compopt) = "builtin" ]]; then - compopt -o nospace - fi - - case "$cur" in - /*|[.~]*) # looks like a path - return - ;; - *:*) # TODO: complete remote files in the pod - return - ;; - */*) # complete / - local template namespace kubectl_out - template="{{ range .items }}{{ .metadata.namespace }}/{{ .metadata.name }}: {{ end }}" - namespace="${cur%%/*}" - if kubectl_out=$(__kubectl_debug_out "kubectl get $(__kubectl_override_flags) --namespace \"${namespace}\" -o template --template=\"${template}\" pods"); then - COMPREPLY=( $(compgen -W "${kubectl_out[*]}" -- "${cur}") ) - fi - return - ;; - *) # complete namespaces, pods, and filedirs - __kubectl_parse_get "namespace" "{{ range .items }}{{ .metadata.name }}/ {{ end }}" - __kubectl_parse_get "pod" "{{ range .items }}{{ .metadata.name }}: {{ end }}" - _filedir - ;; - esac -} - -__kubectl_custom_func() { - case ${last_command} in - kubectl_get) - __kubectl_get_resources - return - ;; - kubectl_describe | kubectl_delete | kubectl_label | kubectl_edit | kubectl_patch |\ - kubectl_annotate | kubectl_expose | kubectl_scale | kubectl_autoscale | kubectl_taint | kubectl_rollout_* |\ - kubectl_apply_edit-last-applied | kubectl_apply_view-last-applied) - __kubectl_get_resource - return - ;; - kubectl_logs) - __kubectl_require_pod_and_container - return - ;; - kubectl_exec | kubectl_port-forward | kubectl_top_pod | kubectl_attach) - __kubectl_get_resource_pod - return - ;; - kubectl_cordon | kubectl_uncordon | kubectl_drain | kubectl_top_node) - __kubectl_get_resource_node - return - ;; - kubectl_config_use-context | kubectl_config_rename-context | kubectl_config_delete-context) - __kubectl_config_get_contexts - return - ;; - kubectl_config_delete-cluster) - __kubectl_config_get_clusters - return - ;; - kubectl_cp) - __kubectl_cp - return - ;; - *) - ;; - esac -} -` -) - const kubectlCmdHeaders = "KUBECTL_COMMAND_HEADERS" // NewDefaultKubectlCommand creates the `kubectl` command with default arguments @@ -496,7 +252,6 @@ func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command { } return nil }, - BashCompletionFunction: bashCompletionFunc, } flags := cmds.PersistentFlags() @@ -672,3 +427,26 @@ func addCmdHeaderHooks(cmds *cobra.Command, kubeConfigFlags *genericclioptions.C func runHelp(cmd *cobra.Command, args []string) { cmd.Help() } + +func registerCompletionFuncForGlobalFlags(cmd *cobra.Command, f cmdutil.Factory) { + cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc( + "namespace", + func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return get.CompGetResource(f, cmd, "namespace", toComplete), cobra.ShellCompDirectiveNoFileComp + })) + cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc( + "context", + func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return cmdconfig.CompListContextsInConfig(nil, toComplete), cobra.ShellCompDirectiveNoFileComp + })) + cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc( + "cluster", + func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return cmdconfig.CompListClustersInConfig(nil, toComplete), cobra.ShellCompDirectiveNoFileComp + })) + cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc( + "user", + func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return cmdconfig.CompListUsersInConfig(nil, toComplete), cobra.ShellCompDirectiveNoFileComp + })) +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/completion.go b/staging/src/k8s.io/kubectl/pkg/cmd/completion.go deleted file mode 100644 index a561c07d45bb..000000000000 --- a/staging/src/k8s.io/kubectl/pkg/cmd/completion.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright 2021 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package cmd - -import ( - "github.com/spf13/cobra" - "k8s.io/kubectl/pkg/cmd/util" - "k8s.io/kubectl/pkg/completion" -) - -func registerCompletionFuncForGlobalFlags(cmd *cobra.Command, f util.Factory) { - util.CheckErr(cmd.RegisterFlagCompletionFunc( - "namespace", - func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return completion.ListNamespaces(f, toComplete) - })) - util.CheckErr(cmd.RegisterFlagCompletionFunc( - "context", - func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return completion.ListContextsInKubeconfig(f, toComplete) - })) - util.CheckErr(cmd.RegisterFlagCompletionFunc( - "cluster", - func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return completion.ListClustersInKubeconfig(f, toComplete) - })) - util.CheckErr(cmd.RegisterFlagCompletionFunc( - "user", - func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return completion.ListUsersInKubeconfig(f, toComplete) - })) -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/config.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/config.go index 74a0e2175158..4930e0d43fe0 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/config.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/config.go @@ -20,6 +20,7 @@ import ( "fmt" "path" "strconv" + "strings" "github.com/spf13/cobra" @@ -30,8 +31,22 @@ import ( "k8s.io/kubectl/pkg/util/templates" ) +var ( + // CompListContextsInConfig returns a list of context names which begin with `toComplete` + // We allow to pass in a factory to be ready for a future improvement + CompListContextsInConfig func(f cmdutil.Factory, toComplete string) []string + // CompListClustersInConfig returns a list of cluster names which begin with `toComplete` + // We allow to pass in a factory to be ready for a future improvement + CompListClustersInConfig func(f cmdutil.Factory, toComplete string) []string + // CompListUsersInConfig returns a list of user names which begin with `toComplete` + // We allow to pass in a factory to be ready for a future improvement + CompListUsersInConfig func(f cmdutil.Factory, toComplete string) []string +) + // NewCmdConfig creates a command object for the "config" action, and adds all child commands to it. func NewCmdConfig(f cmdutil.Factory, pathOptions *clientcmd.PathOptions, streams genericclioptions.IOStreams) *cobra.Command { + initCompletionFunctions(f) + if len(pathOptions.ExplicitFileFlag) == 0 { pathOptions.ExplicitFileFlag = clientcmd.RecommendedConfigPathFlag } @@ -92,3 +107,48 @@ func helpErrorf(cmd *cobra.Command, format string, args ...interface{}) error { msg := fmt.Sprintf(format, args...) return fmt.Errorf("%s", msg) } + +// The completion function need the factory, so we initialize them once it is available +func initCompletionFunctions(f cmdutil.Factory) { + CompListContextsInConfig = func(notused cmdutil.Factory, toComplete string) []string { + config, err := f.ToRawKubeConfigLoader().RawConfig() + if err != nil { + return nil + } + var ret []string + for name := range config.Contexts { + if strings.HasPrefix(name, toComplete) { + ret = append(ret, name) + } + } + return ret + } + + CompListClustersInConfig = func(notused cmdutil.Factory, toComplete string) []string { + config, err := f.ToRawKubeConfigLoader().RawConfig() + if err != nil { + return nil + } + var ret []string + for name := range config.Clusters { + if strings.HasPrefix(name, toComplete) { + ret = append(ret, name) + } + } + return ret + } + + CompListUsersInConfig = func(notused cmdutil.Factory, toComplete string) []string { + config, err := f.ToRawKubeConfigLoader().RawConfig() + if err != nil { + return nil + } + var ret []string + for name := range config.AuthInfos { + if strings.HasPrefix(name, toComplete) { + ret = append(ret, name) + } + } + return ret + } +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_cluster.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_cluster.go index 318629cf10cf..e76b3ae4d36e 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_cluster.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_cluster.go @@ -41,6 +41,12 @@ func NewCmdConfigDeleteCluster(out io.Writer, configAccess clientcmd.ConfigAcces Short: i18n.T("Delete the specified cluster from the kubeconfig"), Long: i18n.T("Delete the specified cluster from the kubeconfig"), Example: deleteClusterExample, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) == 0 { + return CompListClustersInConfig(nil, toComplete), cobra.ShellCompDirectiveNoFileComp + } + return nil, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(runDeleteCluster(out, configAccess, cmd)) }, diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_context.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_context.go index 8221bba873df..8828d2f1ffcd 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_context.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_context.go @@ -41,6 +41,12 @@ func NewCmdConfigDeleteContext(out, errOut io.Writer, configAccess clientcmd.Con Short: i18n.T("Delete the specified context from the kubeconfig"), Long: i18n.T("Delete the specified context from the kubeconfig"), Example: deleteContextExample, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) == 0 { + return CompListContextsInConfig(nil, toComplete), cobra.ShellCompDirectiveNoFileComp + } + return nil, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(runDeleteContext(out, errOut, configAccess, cmd)) }, diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/rename_context.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/rename_context.go index 3a8022538e40..52fa7773ac84 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/rename_context.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/rename_context.go @@ -67,6 +67,12 @@ func NewCmdConfigRenameContext(out io.Writer, configAccess clientcmd.ConfigAcces Short: renameContextShort, Long: renameContextLong, Example: renameContextExample, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) == 0 { + return CompListContextsInConfig(nil, toComplete), cobra.ShellCompDirectiveNoFileComp + } + return nil, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(options.Complete(cmd, args, out)) cmdutil.CheckErr(options.Validate()) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/use_context.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/use_context.go index c87dd5f7d875..1a8678b66a1a 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/use_context.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/use_context.go @@ -52,6 +52,12 @@ func NewCmdConfigUseContext(out io.Writer, configAccess clientcmd.ConfigAccess) Aliases: []string{"use"}, Long: `Sets the current-context in a kubeconfig file`, Example: useContextExample, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) == 0 { + return CompListContextsInConfig(nil, toComplete), cobra.ShellCompDirectiveNoFileComp + } + return nil, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(options.complete(cmd)) cmdutil.CheckErr(options.run()) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/cp/cp.go b/staging/src/k8s.io/kubectl/pkg/cmd/cp/cp.go index 8282e82497fe..79d064113bc5 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/cp/cp.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/cp/cp.go @@ -35,6 +35,7 @@ import ( "k8s.io/client-go/kubernetes" restclient "k8s.io/client-go/rest" "k8s.io/kubectl/pkg/cmd/exec" + "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" @@ -104,6 +105,57 @@ func NewCmdCp(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.C Short: i18n.T("Copy files and directories to and from containers."), Long: i18n.T("Copy files and directories to and from containers."), Example: cpExample, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + if len(args) == 0 { + if strings.IndexAny(toComplete, "/.~") == 0 { + // Looks like a path, do nothing + } else if strings.Index(toComplete, ":") != -1 { + // TODO: complete remote files in the pod + } else if idx := strings.Index(toComplete, "/"); idx > 0 { + // complete / + namespace := toComplete[:idx] + template := "{{ range .items }}{{ .metadata.namespace }}/{{ .metadata.name }}: {{ end }}" + comps = get.CompGetFromTemplate(&template, f, namespace, cmd, []string{"pod"}, toComplete) + } else { + // Complete namespaces followed by a / + for _, ns := range get.CompGetResource(f, cmd, "namespace", toComplete) { + comps = append(comps, fmt.Sprintf("%s/", ns)) + } + // Complete pod names followed by a : + for _, pod := range get.CompGetResource(f, cmd, "pod", toComplete) { + comps = append(comps, fmt.Sprintf("%s:", pod)) + } + + // Finally, provide file completion if we need to. + // We only do this if: + // 1- There are other completions found (if there are no completions, + // the shell will do file completion itself) + // 2- If there is some input from the user (or else we will end up + // listing the entire content of the current directory which could + // be too many choices for the user) + if len(comps) > 0 && len(toComplete) > 0 { + if files, err := ioutil.ReadDir("."); err == nil { + for _, file := range files { + filename := file.Name() + if strings.HasPrefix(filename, toComplete) { + if file.IsDir() { + filename = fmt.Sprintf("%s/", filename) + } + // We are completing a file prefix + comps = append(comps, filename) + } + } + } + } else if len(toComplete) == 0 { + // If the user didn't provide any input to complete, + // we provide a hint that a path can also be used + comps = append(comps, "./", "/") + } + } + } + return comps, cobra.ShellCompDirectiveNoSpace + }, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd)) cmdutil.CheckErr(o.Run(args)) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/create/create_clusterrolebinding.go b/staging/src/k8s.io/kubectl/pkg/cmd/create/create_clusterrolebinding.go index e973dc9517c8..7757b2c66097 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/create/create_clusterrolebinding.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/create/create_clusterrolebinding.go @@ -29,6 +29,7 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/resource" rbacclientv1 "k8s.io/client-go/kubernetes/typed/rbac/v1" + "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/scheme" "k8s.io/kubectl/pkg/util" @@ -99,11 +100,18 @@ func NewCmdCreateClusterRoleBinding(f cmdutil.Factory, ioStreams genericclioptio cmdutil.AddDryRunFlag(cmd) cmd.Flags().StringVar(&o.ClusterRole, "clusterrole", "", i18n.T("ClusterRole this ClusterRoleBinding should reference")) cmd.MarkFlagRequired("clusterrole") - cmd.MarkFlagCustom("clusterrole", "__kubectl_get_resource_clusterrole") cmd.Flags().StringArrayVar(&o.Users, "user", o.Users, "Usernames to bind to the clusterrole") cmd.Flags().StringArrayVar(&o.Groups, "group", o.Groups, "Groups to bind to the clusterrole") cmd.Flags().StringArrayVar(&o.ServiceAccounts, "serviceaccount", o.ServiceAccounts, "Service accounts to bind to the clusterrole, in the format :") cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create") + + // Completion for relevant flags + cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc( + "clusterrole", + func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return get.CompGetResource(f, cmd, "clusterrole", toComplete), cobra.ShellCompDirectiveNoFileComp + })) + return cmd } diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/delete/delete.go b/staging/src/k8s.io/kubectl/pkg/cmd/delete/delete.go index 022c99171812..234ae3b6714f 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/delete/delete.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/delete/delete.go @@ -33,6 +33,8 @@ import ( "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" "k8s.io/client-go/dynamic" + "k8s.io/kubectl/pkg/cmd/apiresources" + "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" cmdwait "k8s.io/kubectl/pkg/cmd/wait" "k8s.io/kubectl/pkg/rawhttp" @@ -135,6 +137,15 @@ func NewCmdDelete(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra Short: i18n.T("Delete resources by filenames, stdin, resources and names, or by resources and label selector"), Long: deleteLong, Example: deleteExample, + 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 if len(args) == 1 { + comps = get.CompGetResource(f, cmd, args[0], toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { o, err := deleteFlags.ToOptions(nil, streams) cmdutil.CheckErr(err) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/describe/describe.go b/staging/src/k8s.io/kubectl/pkg/cmd/describe/describe.go index dfd2c1e873f7..7199b2b932a2 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/describe/describe.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/describe/describe.go @@ -28,6 +28,8 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/resource" + "k8s.io/kubectl/pkg/cmd/apiresources" + "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/describe" "k8s.io/kubectl/pkg/util/i18n" @@ -106,6 +108,15 @@ func NewCmdDescribe(parent string, f cmdutil.Factory, streams genericclioptions. Short: i18n.T("Show details of a specific resource or group of resources"), Long: describeLong + "\n\n" + cmdutil.SuggestAPIResources(parent), Example: describeExample, + 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 if len(args) == 1 { + comps = get.CompGetResource(f, cmd, args[0], toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.Run()) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go b/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go index c2a13a078e84..e81488016e60 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go @@ -31,6 +31,7 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" + "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/drain" "k8s.io/kubectl/pkg/scheme" @@ -68,6 +69,13 @@ func NewCmdCordon(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cob Short: i18n.T("Mark node as unschedulable"), Long: cordonLong, Example: cordonExample, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + if len(args) == 0 { + comps = get.CompGetResource(f, cmd, "node", toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.RunCordonOrUncordon(true)) @@ -96,6 +104,13 @@ func NewCmdUncordon(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *c Short: i18n.T("Mark node as schedulable"), Long: uncordonLong, Example: uncordonExample, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + if len(args) == 0 { + comps = get.CompGetResource(f, cmd, "node", toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.RunCordonOrUncordon(false)) @@ -181,6 +196,13 @@ func NewCmdDrain(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobr Short: i18n.T("Drain node in preparation for maintenance"), Long: drainLong, Example: drainExample, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + if len(args) == 0 { + comps = get.CompGetResource(f, cmd, "node", toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.RunDrain()) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/edit/edit.go b/staging/src/k8s.io/kubectl/pkg/cmd/edit/edit.go index d466fbc1efd0..faa25f15e8e7 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/edit/edit.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/edit/edit.go @@ -20,6 +20,8 @@ import ( "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" + "k8s.io/kubectl/pkg/cmd/apiresources" + "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/cmd/util/editor" "k8s.io/kubectl/pkg/util/i18n" @@ -76,6 +78,15 @@ func NewCmdEdit(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra Short: i18n.T("Edit a resource on the server"), Long: editLong, Example: editExample, + 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 if len(args) == 1 { + comps = get.CompGetResource(f, cmd, args[0], toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, args, cmd)) cmdutil.CheckErr(o.Run()) 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 1ff45f26fd52..7354f9af77e6 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/exec/exec.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/exec/exec.go @@ -33,6 +33,7 @@ import ( restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/remotecommand" + "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/cmd/util/podcmd" "k8s.io/kubectl/pkg/polymorphichelpers" @@ -88,6 +89,13 @@ 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: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + if len(args) == 0 { + comps = get.CompGetResource(f, cmd, "pod", toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { argsLenAtDash := cmd.ArgsLenAtDash() cmdutil.CheckErr(options.Complete(f, cmd, args, argsLenAtDash)) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/expose/expose.go b/staging/src/k8s.io/kubectl/pkg/cmd/expose/expose.go index 008dcb32c34a..a63f4a41fb82 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/expose/expose.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/expose/expose.go @@ -31,6 +31,7 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" + "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/generate" generateversioned "k8s.io/kubectl/pkg/generate/versioned" @@ -134,11 +135,23 @@ func NewCmdExposeService(f cmdutil.Factory, streams genericclioptions.IOStreams) Short: i18n.T("Take a replication controller, service, deployment or pod and expose it as a new Kubernetes Service"), Long: exposeLong, Example: exposeExample, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + if len(args) == 0 { + for _, comp := range validArgs { + if strings.HasPrefix(comp, toComplete) { + comps = append(comps, comp) + } + } + } else if len(args) == 1 { + comps = get.CompGetResource(f, cmd, args[0], toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd)) cmdutil.CheckErr(o.RunExpose(cmd, args)) }, - ValidArgs: validArgs, } o.RecordFlags.AddFlags(cmd) 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 4f1f5a8c4ee0..3252bb14f047 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/get/get.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/get/get.go @@ -17,11 +17,14 @@ limitations under the License. package get import ( + "bytes" "context" "encoding/json" "fmt" "io" + "io/ioutil" "net/url" + "os" "strings" "github.com/spf13/cobra" @@ -42,6 +45,7 @@ import ( kubernetesscheme "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" watchtools "k8s.io/client-go/tools/watch" + "k8s.io/kubectl/pkg/cmd/apiresources" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/rawhttp" "k8s.io/kubectl/pkg/scheme" @@ -161,6 +165,15 @@ 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 if len(args) == 1 { + comps = CompGetResource(f, cmd, args[0], toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.Validate(cmd)) @@ -849,3 +862,61 @@ func multipleGVKsRequested(infos []*resource.Info) bool { } return false } + +// CompGetResource gets the list of the resource specified which begin with `toComplete`. +func CompGetResource(f cmdutil.Factory, cmd *cobra.Command, resourceName string, toComplete string) []string { + template := "{{ range .items }}{{ .metadata.name }} {{ end }}" + return CompGetFromTemplate(&template, f, "", cmd, []string{resourceName}, toComplete) +} + +// CompGetContainers gets the list of containers of the specified pod which begin with `toComplete`. +func CompGetContainers(f cmdutil.Factory, cmd *cobra.Command, podName string, toComplete string) []string { + template := "{{ range .spec.initContainers }}{{ .name }} {{end}}{{ range .spec.containers }}{{ .name }} {{ end }}" + return CompGetFromTemplate(&template, f, "", cmd, []string{"pod", podName}, toComplete) +} + +// CompGetFromTemplate executes a Get operation using the specified template and args and returns the results +// which begin with `toComplete`. +func CompGetFromTemplate(template *string, f cmdutil.Factory, namespace string, cmd *cobra.Command, args []string, toComplete string) []string { + buf := new(bytes.Buffer) + streams := genericclioptions.IOStreams{In: os.Stdin, Out: buf, ErrOut: ioutil.Discard} + o := NewGetOptions("kubectl", streams) + + // Get the list of names of the specified resource + o.PrintFlags.TemplateFlags.GoTemplatePrintFlags.TemplateArgument = template + format := "go-template" + o.PrintFlags.OutputFormat = &format + + // Do the steps Complete() would have done. + // We cannot actually call Complete() or Validate() as these function check for + // the presence of flags, which, in our case won't be there + if namespace != "" { + o.Namespace = namespace + o.ExplicitNamespace = true + } else { + var err error + o.Namespace, o.ExplicitNamespace, err = f.ToRawKubeConfigLoader().Namespace() + if err != nil { + return nil + } + } + + o.ToPrinter = func(mapping *meta.RESTMapping, outputObjects *bool, withNamespace bool, withKind bool) (printers.ResourcePrinterFunc, error) { + printer, err := o.PrintFlags.ToPrinter() + if err != nil { + return nil, err + } + return printer.PrintObj, nil + } + + o.Run(f, cmd, args) + + var comps []string + resources := strings.Split(buf.String(), " ") + for _, res := range resources { + if res != "" && strings.HasPrefix(res, toComplete) { + comps = append(comps, res) + } + } + return comps +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/label/label.go b/staging/src/k8s.io/kubectl/pkg/cmd/label/label.go index 430ac7b5ff21..faaf67e21122 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/label/label.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/label/label.go @@ -37,6 +37,8 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" + "k8s.io/kubectl/pkg/cmd/apiresources" + "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/scheme" "k8s.io/kubectl/pkg/util/i18n" @@ -132,6 +134,15 @@ func NewCmdLabel(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobr Short: i18n.T("Update the labels on a resource"), Long: fmt.Sprintf(labelLong, validation.LabelValueMaxLength), Example: labelExample, + 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 if len(args) == 1 { + comps = get.CompGetResource(f, cmd, args[0], toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, 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/logs/logs.go b/staging/src/k8s.io/kubectl/pkg/cmd/logs/logs.go index 4b6c0fc373c8..ecd4994b2dbb 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/logs/logs.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/logs/logs.go @@ -33,6 +33,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/client-go/rest" + "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/polymorphichelpers" "k8s.io/kubectl/pkg/scheme" @@ -152,6 +153,15 @@ func NewCmdLogs(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.C Short: i18n.T("Print the logs for a container in a pod"), Long: logsLong, Example: logsExample, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + if len(args) == 0 { + comps = get.CompGetResource(f, cmd, "pod", toComplete) + } else if len(args) == 1 { + comps = get.CompGetContainers(f, cmd, args[0], toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, 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/patch/patch.go b/staging/src/k8s.io/kubectl/pkg/cmd/patch/patch.go index fe563ecbc3fd..59151f7c1f89 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/patch/patch.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/patch/patch.go @@ -37,6 +37,8 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" + "k8s.io/kubectl/pkg/cmd/apiresources" + "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/scheme" "k8s.io/kubectl/pkg/util/i18n" @@ -114,6 +116,15 @@ func NewCmdPatch(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobr Short: i18n.T("Update field(s) of a resource"), Long: patchLong, Example: patchExample, + 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 if len(args) == 1 { + comps = get.CompGetResource(f, cmd, args[0], toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, 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/portforward/portforward.go b/staging/src/k8s.io/kubectl/pkg/cmd/portforward/portforward.go index 71922bb9d237..d55545593e7a 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/portforward/portforward.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/portforward/portforward.go @@ -38,6 +38,7 @@ import ( restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/portforward" "k8s.io/client-go/transport/spdy" + "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/polymorphichelpers" "k8s.io/kubectl/pkg/util" @@ -109,6 +110,13 @@ 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: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + if len(args) == 0 { + comps = get.CompGetResource(f, cmd, "pod", toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, 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/cmd/rollout/rollout_history.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_history.go index 9cf3639218d9..c0aaee29c188 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_history.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_history.go @@ -18,12 +18,14 @@ package rollout import ( "fmt" + "strings" "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" + "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/polymorphichelpers" "k8s.io/kubectl/pkg/scheme" @@ -82,12 +84,24 @@ func NewCmdRolloutHistory(f cmdutil.Factory, streams genericclioptions.IOStreams Short: i18n.T("View rollout history"), Long: historyLong, Example: historyExample, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + if len(args) == 0 { + for _, comp := range validArgs { + if strings.HasPrefix(comp, toComplete) { + comps = append(comps, comp) + } + } + } else if len(args) == 1 { + comps = get.CompGetResource(f, cmd, args[0], toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.Validate()) cmdutil.CheckErr(o.Run()) }, - ValidArgs: validArgs, } cmd.Flags().Int64Var(&o.Revision, "revision", o.Revision, "See the details, including podTemplate of the revision specified") diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_pause.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_pause.go index 2470c004927a..59b53748fb1d 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_pause.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_pause.go @@ -18,6 +18,7 @@ package rollout import ( "fmt" + "strings" "github.com/spf13/cobra" @@ -26,6 +27,7 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" + "k8s.io/kubectl/pkg/cmd/get" "k8s.io/kubectl/pkg/cmd/set" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/polymorphichelpers" @@ -82,12 +84,24 @@ func NewCmdRolloutPause(f cmdutil.Factory, streams genericclioptions.IOStreams) Short: i18n.T("Mark the provided resource as paused"), Long: pauseLong, Example: pauseExample, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + if len(args) == 0 { + for _, comp := range validArgs { + if strings.HasPrefix(comp, toComplete) { + comps = append(comps, comp) + } + } + } else if len(args) == 1 { + comps = get.CompGetResource(f, cmd, args[0], toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.Validate()) cmdutil.CheckErr(o.RunPause()) }, - ValidArgs: validArgs, } o.PrintFlags.AddFlags(cmd) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_restart.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_restart.go index 0a980cf8f5d8..e8cc3ed10085 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_restart.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_restart.go @@ -18,6 +18,7 @@ package rollout import ( "fmt" + "strings" "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/types" @@ -25,6 +26,7 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" + "k8s.io/kubectl/pkg/cmd/get" "k8s.io/kubectl/pkg/cmd/set" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/polymorphichelpers" @@ -86,12 +88,24 @@ func NewCmdRolloutRestart(f cmdutil.Factory, streams genericclioptions.IOStreams Short: i18n.T("Restart a resource"), Long: restartLong, Example: restartExample, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + if len(args) == 0 { + for _, comp := range validArgs { + if strings.HasPrefix(comp, toComplete) { + comps = append(comps, comp) + } + } + } else if len(args) == 1 { + comps = get.CompGetResource(f, cmd, args[0], toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.Validate()) cmdutil.CheckErr(o.RunRestart()) }, - ValidArgs: validArgs, } usage := "identifying the resource to get from a server." diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_resume.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_resume.go index 915c779e5655..c31d9ff7831a 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_resume.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_resume.go @@ -18,6 +18,7 @@ package rollout import ( "fmt" + "strings" "github.com/spf13/cobra" @@ -26,6 +27,7 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" + "k8s.io/kubectl/pkg/cmd/get" "k8s.io/kubectl/pkg/cmd/set" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/polymorphichelpers" @@ -86,12 +88,24 @@ func NewCmdRolloutResume(f cmdutil.Factory, streams genericclioptions.IOStreams) Short: i18n.T("Resume a paused resource"), Long: resumeLong, Example: resumeExample, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + if len(args) == 0 { + for _, comp := range validArgs { + if strings.HasPrefix(comp, toComplete) { + comps = append(comps, comp) + } + } + } else if len(args) == 1 { + comps = get.CompGetResource(f, cmd, args[0], toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.Validate()) cmdutil.CheckErr(o.RunResume()) }, - ValidArgs: validArgs, } usage := "identifying the resource to get from a server." diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_status.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_status.go index 4abee4ba084e..b6e5a0decb73 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_status.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_status.go @@ -19,6 +19,7 @@ package rollout import ( "context" "fmt" + "strings" "time" "github.com/spf13/cobra" @@ -34,6 +35,7 @@ import ( "k8s.io/client-go/dynamic" "k8s.io/client-go/tools/cache" watchtools "k8s.io/client-go/tools/watch" + "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/polymorphichelpers" "k8s.io/kubectl/pkg/scheme" @@ -101,12 +103,24 @@ func NewCmdRolloutStatus(f cmdutil.Factory, streams genericclioptions.IOStreams) Short: i18n.T("Show the status of the rollout"), Long: statusLong, Example: statusExample, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + if len(args) == 0 { + for _, comp := range validArgs { + if strings.HasPrefix(comp, toComplete) { + comps = append(comps, comp) + } + } + } else if len(args) == 1 { + comps = get.CompGetResource(f, cmd, args[0], toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, args)) cmdutil.CheckErr(o.Validate()) cmdutil.CheckErr(o.Run()) }, - ValidArgs: validArgs, } usage := "identifying the resource to get from a server." diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_undo.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_undo.go index 783ace37952d..4debce3b62f0 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_undo.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_undo.go @@ -18,12 +18,14 @@ package rollout import ( "fmt" + "strings" "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" + "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/polymorphichelpers" "k8s.io/kubectl/pkg/scheme" @@ -86,12 +88,24 @@ func NewCmdRolloutUndo(f cmdutil.Factory, streams genericclioptions.IOStreams) * Short: i18n.T("Undo a previous rollout"), Long: undoLong, Example: undoExample, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + if len(args) == 0 { + for _, comp := range validArgs { + if strings.HasPrefix(comp, toComplete) { + comps = append(comps, comp) + } + } + } else if len(args) == 1 { + comps = get.CompGetResource(f, cmd, args[0], toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.Validate()) cmdutil.CheckErr(o.RunUndo()) }, - ValidArgs: validArgs, } cmd.Flags().Int64Var(&o.ToRevision, "to-revision", o.ToRevision, "The revision to rollback to. Default to 0 (last revision).") diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/scale/scale.go b/staging/src/k8s.io/kubectl/pkg/cmd/scale/scale.go index 1607ef3ff59f..8978e7c54fd4 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/scale/scale.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/scale/scale.go @@ -18,6 +18,7 @@ package scale import ( "fmt" + "strings" "time" "github.com/spf13/cobra" @@ -29,6 +30,7 @@ import ( "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" "k8s.io/client-go/kubernetes" + "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/scale" "k8s.io/kubectl/pkg/util/i18n" @@ -113,12 +115,24 @@ func NewCmdScale(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobr Short: i18n.T("Set a new size for a Deployment, ReplicaSet or Replication Controller"), Long: scaleLong, Example: scaleExample, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + if len(args) == 0 { + for _, comp := range validArgs { + if strings.HasPrefix(comp, toComplete) { + comps = append(comps, comp) + } + } + } else if len(args) == 1 { + comps = get.CompGetResource(f, cmd, args[0], toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.Validate(cmd)) cmdutil.CheckErr(o.RunScale()) }, - ValidArgs: validArgs, } o.RecordFlags.AddFlags(cmd) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/taint/taint.go b/staging/src/k8s.io/kubectl/pkg/cmd/taint/taint.go index 9430028e628c..4f6c7409f986 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/taint/taint.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/taint/taint.go @@ -35,6 +35,7 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" + "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/scheme" "k8s.io/kubectl/pkg/util/i18n" @@ -108,12 +109,24 @@ func NewCmdTaint(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra. Short: i18n.T("Update the taints on one or more nodes"), Long: fmt.Sprintf(taintLong, validation.DNS1123SubdomainMaxLength, validation.LabelValueMaxLength), Example: taintExample, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + if len(args) == 0 { + for _, comp := range validArgs { + if strings.HasPrefix(comp, toComplete) { + comps = append(comps, comp) + } + } + } else if len(args) == 1 { + comps = get.CompGetResource(f, cmd, args[0], toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(options.Complete(f, cmd, args)) cmdutil.CheckErr(options.Validate()) cmdutil.CheckErr(options.RunTaint()) }, - ValidArgs: validArgs, } options.PrintFlags.AddFlags(cmd) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/top/top_node.go b/staging/src/k8s.io/kubectl/pkg/cmd/top/top_node.go index 2f530d78f515..2561323444c7 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/top/top_node.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/top/top_node.go @@ -27,6 +27,7 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/client-go/discovery" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/metricsutil" "k8s.io/kubectl/pkg/util/i18n" @@ -80,6 +81,13 @@ func NewCmdTopNode(f cmdutil.Factory, o *TopNodeOptions, streams genericclioptio Short: i18n.T("Display Resource (CPU/Memory) usage of nodes"), Long: topNodeLong, Example: topNodeExample, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + if len(args) == 0 { + comps = get.CompGetResource(f, cmd, "node", toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, 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/top/top_pod.go b/staging/src/k8s.io/kubectl/pkg/cmd/top/top_pod.go index 5d9649e707f5..eeb1981a2a6b 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/top/top_pod.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/top/top_pod.go @@ -27,6 +27,7 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/discovery" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/metricsutil" "k8s.io/kubectl/pkg/util/i18n" @@ -97,6 +98,13 @@ func NewCmdTopPod(f cmdutil.Factory, o *TopPodOptions, streams genericclioptions Short: i18n.T("Display Resource (CPU/Memory) usage of pods"), Long: topPodLong, Example: topPodExample, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + if len(args) == 0 { + comps = get.CompGetResource(f, cmd, "pod", toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + }, 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/completion/utils.go b/staging/src/k8s.io/kubectl/pkg/completion/utils.go deleted file mode 100644 index bfb9f9bfe8b8..000000000000 --- a/staging/src/k8s.io/kubectl/pkg/completion/utils.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright 2021 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package completion - -import ( - "context" - "strings" - "time" - - "github.com/spf13/cobra" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/kubectl/pkg/cmd/util" -) - -// ListNamespaces returns a list of namespaces which begins with `toComplete`. -func ListNamespaces(f util.Factory, toComplete string) ([]string, cobra.ShellCompDirective) { - clientSet, err := f.KubernetesClientSet() - if err != nil { - return nil, cobra.ShellCompDirectiveDefault - } - ctx, cancel := context.WithTimeout(context.TODO(), time.Second*3) - defer cancel() - namespaces, err := clientSet.CoreV1().Namespaces().List(ctx, metav1.ListOptions{}) - if err != nil { - return nil, cobra.ShellCompDirectiveDefault - } - var ret []string - for _, ns := range namespaces.Items { - if strings.HasPrefix(ns.Name, toComplete) { - ret = append(ret, ns.Name) - } - } - return ret, cobra.ShellCompDirectiveNoFileComp -} - -// ListContextsInKubeconfig returns a list of context names which begins with `toComplete`. -func ListContextsInKubeconfig(f util.Factory, toComplete string) ([]string, cobra.ShellCompDirective) { - config, err := f.ToRawKubeConfigLoader().RawConfig() - if err != nil { - return nil, cobra.ShellCompDirectiveNoFileComp - } - var ret []string - for name := range config.Contexts { - if strings.HasPrefix(name, toComplete) { - ret = append(ret, name) - } - } - return ret, cobra.ShellCompDirectiveNoFileComp -} - -// ListClustersInKubeconfig returns a list of cluster names which begins with `toComplete`. -func ListClustersInKubeconfig(f util.Factory, toComplete string) ([]string, cobra.ShellCompDirective) { - config, err := f.ToRawKubeConfigLoader().RawConfig() - if err != nil { - return nil, cobra.ShellCompDirectiveNoFileComp - } - var ret []string - for name := range config.Clusters { - if strings.HasPrefix(name, toComplete) { - ret = append(ret, name) - } - } - return ret, cobra.ShellCompDirectiveNoFileComp -} - -// ListUsersInKubeconfig returns a list of user names which begins with `toComplete`. -func ListUsersInKubeconfig(f util.Factory, toComplete string) ([]string, cobra.ShellCompDirective) { - config, err := f.ToRawKubeConfigLoader().RawConfig() - if err != nil { - return nil, cobra.ShellCompDirectiveNoFileComp - } - var ret []string - for name := range config.AuthInfos { - if strings.HasPrefix(name, toComplete) { - ret = append(ret, name) - } - } - return ret, cobra.ShellCompDirectiveNoFileComp -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 4064ac74e0e2..ebc4e11be34f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -2070,7 +2070,6 @@ k8s.io/kubectl/pkg/cmd/util/podcmd k8s.io/kubectl/pkg/cmd/util/sanity k8s.io/kubectl/pkg/cmd/version k8s.io/kubectl/pkg/cmd/wait -k8s.io/kubectl/pkg/completion k8s.io/kubectl/pkg/describe k8s.io/kubectl/pkg/drain k8s.io/kubectl/pkg/explain From c0b3a698fa15265eecdec0bd8776658c40d8d151 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Fri, 5 Mar 2021 15:44:30 -0500 Subject: [PATCH 3/8] Add Go tests for custom completions Signed-off-by: Marc Khouzam --- .../kubectl/pkg/cmd/annotate/annotate_test.go | 8 + .../cmd/apply/apply_edit_last_applied_test.go | 31 ++++ .../cmd/apply/apply_view_last_applied_test.go | 31 ++++ .../kubectl/pkg/cmd/attach/attach_test.go | 8 + .../pkg/cmd/autoscale/autoscale_test.go | 38 +++++ .../pkg/cmd/config/delete_cluster_test.go | 17 ++ .../pkg/cmd/config/delete_context_test.go | 17 ++ .../pkg/cmd/config/rename_context_test.go | 17 ++ .../pkg/cmd/config/use_context_test.go | 17 ++ .../kubectl/pkg/cmd/delete/delete_test.go | 15 ++ .../kubectl/pkg/cmd/describe/describe_test.go | 8 + .../kubectl/pkg/cmd/drain/drain_test.go | 21 +++ .../k8s.io/kubectl/pkg/cmd/edit/edit_test.go | 7 + .../k8s.io/kubectl/pkg/cmd/exec/exec_test.go | 8 + .../kubectl/pkg/cmd/expose/expose_test.go | 16 ++ .../k8s.io/kubectl/pkg/cmd/get/get_test.go | 8 + .../kubectl/pkg/cmd/label/label_test.go | 8 + .../k8s.io/kubectl/pkg/cmd/logs/logs_test.go | 8 + .../kubectl/pkg/cmd/patch/patch_test.go | 8 + .../pkg/cmd/portforward/portforward_test.go | 7 + .../pkg/cmd/rollout/rollout_history_test.go | 38 +++++ .../pkg/cmd/rollout/rollout_pause_test.go | 15 ++ .../pkg/cmd/rollout/rollout_restart_test.go | 38 +++++ .../pkg/cmd/rollout/rollout_resume_test.go | 38 +++++ .../pkg/cmd/rollout/rollout_status_test.go | 38 +++++ .../pkg/cmd/rollout/rollout_undo_test.go | 38 +++++ .../kubectl/pkg/cmd/scale/scale_test.go | 38 +++++ .../kubectl/pkg/cmd/taint/taint_test.go | 15 ++ .../kubectl/pkg/cmd/testing/completion.go | 158 ++++++++++++++++++ .../kubectl/pkg/cmd/top/top_node_test.go | 8 + .../kubectl/pkg/cmd/top/top_pod_test.go | 8 + 31 files changed, 730 insertions(+) create mode 100644 staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_edit_last_applied_test.go create mode 100644 staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_view_last_applied_test.go create mode 100644 staging/src/k8s.io/kubectl/pkg/cmd/autoscale/autoscale_test.go create mode 100644 staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_history_test.go create mode 100644 staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_restart_test.go create mode 100644 staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_resume_test.go create mode 100644 staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_status_test.go create mode 100644 staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_undo_test.go create mode 100644 staging/src/k8s.io/kubectl/pkg/cmd/scale/scale_test.go create mode 100644 staging/src/k8s.io/kubectl/pkg/cmd/testing/completion.go diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/annotate/annotate_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/annotate/annotate_test.go index c1306f1fa445..4636b66b06da 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/annotate/annotate_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/annotate/annotate_test.go @@ -24,6 +24,7 @@ import ( "strings" "testing" + "github.com/spf13/cobra" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -710,3 +711,10 @@ func TestAnnotateMultipleObjects(t *testing.T) { t.Fatalf("unexpected error: %v", err) } } + +func TestCompletionAnnotateOneArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdAnnotate("kubectl", tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_edit_last_applied_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_edit_last_applied_test.go new file mode 100644 index 000000000000..e6b17629d3bd --- /dev/null +++ b/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_edit_last_applied_test.go @@ -0,0 +1,31 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package apply + +import ( + "testing" + + "github.com/spf13/cobra" + cmdtesting "k8s.io/kubectl/pkg/cmd/testing" +) + +func TestCompletionApplyEditLastAppliedOneArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdApplyEditLastApplied(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_view_last_applied_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_view_last_applied_test.go new file mode 100644 index 000000000000..e331acf0929f --- /dev/null +++ b/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_view_last_applied_test.go @@ -0,0 +1,31 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package apply + +import ( + "testing" + + "github.com/spf13/cobra" + cmdtesting "k8s.io/kubectl/pkg/cmd/testing" +) + +func TestCompletionApplyViewLastAppliedOneArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdApplyViewLastApplied(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach_test.go index c9b809a441a1..6f02f5ad1213 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach_test.go @@ -26,6 +26,7 @@ import ( "testing" "time" + "github.com/spf13/cobra" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -447,6 +448,13 @@ func TestAttachWarnings(t *testing.T) { } } +func TestCompletionAttachNoArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdAttach(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + func attachPod() *corev1.Pod { return &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"}, diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/autoscale/autoscale_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/autoscale/autoscale_test.go new file mode 100644 index 000000000000..e68e8321fbf3 --- /dev/null +++ b/staging/src/k8s.io/kubectl/pkg/cmd/autoscale/autoscale_test.go @@ -0,0 +1,38 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package autoscale + +import ( + "testing" + + "github.com/spf13/cobra" + cmdtesting "k8s.io/kubectl/pkg/cmd/testing" +) + +func TestCompletionAutoscaleNoArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdAutoscale(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "") + cmdtesting.CheckCompletion(t, comps, []string{"deployment", "replicationcontroller", "replicaset", "statefulset"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestCompletionAutoscaleOneArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdAutoscale(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_cluster_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_cluster_test.go index 3becba3fdf69..52149cfc2b67 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_cluster_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_cluster_test.go @@ -24,8 +24,10 @@ import ( "reflect" "testing" + "github.com/spf13/cobra" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + cmdtesting "k8s.io/kubectl/pkg/cmd/testing" ) type deleteClusterTest struct { @@ -95,3 +97,18 @@ func (test deleteClusterTest) run(t *testing.T) { t.Errorf("expected clusters %v, but found %v in kubeconfig", test.expectedClusters, clusters) } } + +func TestCompletionDeleteClusterNoArg(t *testing.T) { + // FIXME + t.Skip("Should not be skipeed") + + pathOptions, streams, factory := cmdtesting.PrepareConfigForCompletion(t) + configCmd := NewCmdConfig(factory, pathOptions, streams) + cmd, _, err := configCmd.Find([]string{"delete-cluster"}) + if err != nil { + t.Fatalf("unexpected error finding delete-cluster command: %v", err) + } + + comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "my") + cmdtesting.CheckCompletion(t, comps, []string{"my-cluster"}, directive, cobra.ShellCompDirectiveNoFileComp) +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_context_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_context_test.go index 04bbe17cbb00..2d5de6af1d49 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_context_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_context_test.go @@ -24,8 +24,10 @@ import ( "reflect" "testing" + "github.com/spf13/cobra" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + cmdtesting "k8s.io/kubectl/pkg/cmd/testing" ) type deleteContextTest struct { @@ -96,3 +98,18 @@ func (test deleteContextTest) run(t *testing.T) { t.Errorf("expected contexts %v, but found %v in kubeconfig", test.expectedContexts, contexts) } } + +func TestCompletionDeleteContextNoArg(t *testing.T) { + // FIXME + t.Skip("Should not be skipeed") + + pathOptions, streams, factory := cmdtesting.PrepareConfigForCompletion(t) + configCmd := NewCmdConfig(factory, pathOptions, streams) + cmd, _, err := configCmd.Find([]string{"delete-context"}) + if err != nil { + t.Fatalf("unexpected error finding delete-context command: %v", err) + } + + comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "my") + cmdtesting.CheckCompletion(t, comps, []string{"my-context"}, directive, cobra.ShellCompDirectiveNoFileComp) +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/rename_context_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/rename_context_test.go index cfacb90102a4..ed3758e45a52 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/rename_context_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/rename_context_test.go @@ -24,8 +24,10 @@ import ( "strings" "testing" + "github.com/spf13/cobra" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + cmdtesting "k8s.io/kubectl/pkg/cmd/testing" ) const ( @@ -154,3 +156,18 @@ func (test renameContextTest) run(t *testing.T) { } } } + +func TestCompletionRenameContextNoArg(t *testing.T) { + // FIXME + t.Skip("Should not be skipeed") + + pathOptions, streams, factory := cmdtesting.PrepareConfigForCompletion(t) + configCmd := NewCmdConfig(factory, pathOptions, streams) + cmd, _, err := configCmd.Find([]string{"rename-context"}) + if err != nil { + t.Fatalf("unexpected error finding rename-context command: %v", err) + } + + comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "my") + cmdtesting.CheckCompletion(t, comps, []string{"my-context"}, directive, cobra.ShellCompDirectiveNoFileComp) +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/use_context_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/use_context_test.go index 74424bc51b9c..59488a13d4f2 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/use_context_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/use_context_test.go @@ -22,8 +22,10 @@ import ( "os" "testing" + "github.com/spf13/cobra" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + cmdtesting "k8s.io/kubectl/pkg/cmd/testing" ) type useContextTest struct { @@ -102,3 +104,18 @@ func (test useContextTest) run(t *testing.T) { t.Errorf("Failed in :%q\n expected config %v, but found %v\n in kubeconfig\n", test.description, test.expectedConfig, config) } } + +func TestCompletionUseContextNoArg(t *testing.T) { + // FIXME + t.Skip("Should not be skipeed") + + pathOptions, streams, factory := cmdtesting.PrepareConfigForCompletion(t) + configCmd := NewCmdConfig(factory, pathOptions, streams) + cmd, _, err := configCmd.Find([]string{"use-context"}) + if err != nil { + t.Fatalf("unexpected error finding use-context command: %v", err) + } + + comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "my") + cmdtesting.CheckCompletion(t, comps, []string{"my-context"}, directive, cobra.ShellCompDirectiveNoFileComp) +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/delete/delete_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/delete/delete_test.go index dbe94cad3e74..e2781d03605f 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/delete/delete_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/delete/delete_test.go @@ -832,3 +832,18 @@ func TestResourceErrors(t *testing.T) { }) } } + +// TODO +// func TestCompletionDeleteNoArg(t *testing.T) { +// tf, streams := cmdtesting.PreparePodsForCompletion(t) +// cmd := NewCmdDelete(tf, streams) +// comps, directive := cmd.ValidArgsFunction(cmd, []string{""}, "po") +// cmdtesting.CheckCompletionOfResources(t, comps, directive) +// } + +func TestCompletionDeleteOneArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdDelete(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/describe/describe_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/describe/describe_test.go index b6fbfc444bac..c942bc1073a2 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/describe/describe_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/describe/describe_test.go @@ -22,6 +22,7 @@ import ( "strings" "testing" + "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/api/meta" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/resource" @@ -323,6 +324,13 @@ func TestDescribeNoResourcesFound(t *testing.T) { } } +func TestCompletionDescribeOneArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdDescribe("kubectl", tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + type testDescriber struct { Name, Namespace string Settings describe.DescriberSettings diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain_test.go index eacef7fd300a..5569cc98a221 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain_test.go @@ -930,6 +930,27 @@ func TestDrain(t *testing.T) { } } +func TestCompletionCordonNoArg(t *testing.T) { + tf, streams := cmdtesting.PrepareNodesForCompletion(t) + cmd := NewCmdCordon(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "f") + cmdtesting.CheckCompletion(t, comps, []string{"firstnode"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestCompletionDrainNoArg(t *testing.T) { + tf, streams := cmdtesting.PrepareNodesForCompletion(t) + cmd := NewCmdDrain(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "f") + cmdtesting.CheckCompletion(t, comps, []string{"firstnode"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestCompletionUncordonNoArg(t *testing.T) { + tf, streams := cmdtesting.PrepareNodesForCompletion(t) + cmd := NewCmdUncordon(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "f") + cmdtesting.CheckCompletion(t, comps, []string{"firstnode"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + type MyReq struct { Request *http.Request } diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/edit/edit_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/edit/edit_test.go index df1d07ba4a15..dd89aac8fd20 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/edit/edit_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/edit/edit_test.go @@ -283,6 +283,13 @@ func TestEdit(t *testing.T) { } } +func TestCompletionEditOneArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdEdit(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + func tryIndent(data []byte) []byte { indented := &bytes.Buffer{} if err := json.Indent(indented, data, "", "\t"); err == nil { diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/exec/exec_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/exec/exec_test.go index 814e3e87c443..7bd7f4c7a2d3 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/exec/exec_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/exec/exec_test.go @@ -27,6 +27,7 @@ import ( "strings" "testing" + "github.com/spf13/cobra" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" @@ -408,3 +409,10 @@ func TestSetupTTY(t *testing.T) { t.Errorf("attach stdin, TTY, is a terminal: tty.Out should equal o.Out") } } + +func TestCompletionExecNoArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdExec(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/expose/expose_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/expose/expose_test.go index 0cb405020124..b1027d8299e6 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/expose/expose_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/expose/expose_test.go @@ -21,6 +21,7 @@ import ( "strings" "testing" + "github.com/spf13/cobra" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -676,3 +677,18 @@ func TestRunExposeService(t *testing.T) { }) } } + +func TestCompletionExposeNoArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdExposeService(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "") + expectedComps := []string{"deployment", "pod", "replicaset", "replicationcontroller", "service"} + cmdtesting.CheckCompletion(t, comps, expectedComps, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestCompletionExposeOneArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdExposeService(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/get/get_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/get/get_test.go index ee0e39a7b66a..6f1898b28a83 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/get/get_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/get/get_test.go @@ -29,6 +29,7 @@ import ( "strings" "testing" + "github.com/spf13/cobra" appsv1 "k8s.io/api/apps/v1" autoscalingv1 "k8s.io/api/autoscaling/v1" batchv1 "k8s.io/api/batch/v1" @@ -2733,6 +2734,13 @@ foo 0/0 0 } } +func TestCompletionGetOneArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdGet("kubectl", tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + func watchBody(codec runtime.Codec, events []watch.Event) io.ReadCloser { buf := bytes.NewBuffer([]byte{}) enc := restclientwatch.NewEncoder(streaming.NewEncoder(buf, codec), codec) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/label/label_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/label/label_test.go index 63176eedabe3..b8e065c8684a 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/label/label_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/label/label_test.go @@ -24,6 +24,7 @@ import ( "strings" "testing" + "github.com/spf13/cobra" "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -565,3 +566,10 @@ func TestLabelResourceVersion(t *testing.T) { t.Fatalf("unexpected error: %v", err) } } + +func TestCompletionLabelOneArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdLabel(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/logs/logs_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/logs/logs_test.go index cb9b7cc9228b..c40acff99bba 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/logs/logs_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/logs/logs_test.go @@ -30,6 +30,7 @@ import ( "testing/iotest" "time" + "github.com/spf13/cobra" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -833,6 +834,13 @@ func TestNoResourceFoundMessage(t *testing.T) { } } +func TestCompletionLogsNoArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdLogs(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + type responseWrapperMock struct { data io.Reader err error diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/patch/patch_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/patch/patch_test.go index 1fce8bfeda11..0d5ac1f6f713 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/patch/patch_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/patch/patch_test.go @@ -21,6 +21,7 @@ import ( "strings" "testing" + "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/resource" "k8s.io/client-go/rest/fake" @@ -190,3 +191,10 @@ func TestPatchObjectFromFileOutput(t *testing.T) { t.Errorf("unexpected output: %s", buf.String()) } } + +func TestCompletionPatchOneArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdPatch(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/portforward/portforward_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/portforward/portforward_test.go index 06b99081ac6b..058f0db7be74 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/portforward/portforward_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/portforward/portforward_test.go @@ -980,3 +980,10 @@ func TestCheckUDPPort(t *testing.T) { } } } + +func TestCompletionPortForwardNoArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdPortForward(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_history_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_history_test.go new file mode 100644 index 000000000000..2d1c7b7d91a5 --- /dev/null +++ b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_history_test.go @@ -0,0 +1,38 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package rollout + +import ( + "testing" + + "github.com/spf13/cobra" + cmdtesting "k8s.io/kubectl/pkg/cmd/testing" +) + +func TestCompletionRolloutHistoryNoArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdRolloutHistory(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "") + cmdtesting.CheckCompletion(t, comps, []string{"deployment", "daemonset", "statefulset"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestCompletionRolloutHistoryOneArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdRolloutHistory(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_pause_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_pause_test.go index 779ce0366c4c..6f888f975679 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_pause_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_pause_test.go @@ -22,6 +22,7 @@ import ( "net/http" "testing" + "github.com/spf13/cobra" appsv1 "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -72,6 +73,20 @@ func TestRolloutPause(t *testing.T) { } } +func TestCompletionRolloutPauseNoArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdRolloutPause(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "") + cmdtesting.CheckCompletion(t, comps, []string{"deployment"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestCompletionRolloutPauseOneArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdRolloutPause(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + type RolloutPauseRESTClient struct { *fake.RESTClient } diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_restart_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_restart_test.go new file mode 100644 index 000000000000..b2f1379f14c1 --- /dev/null +++ b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_restart_test.go @@ -0,0 +1,38 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package rollout + +import ( + "testing" + + "github.com/spf13/cobra" + cmdtesting "k8s.io/kubectl/pkg/cmd/testing" +) + +func TestCompletionRolloutRestartNoArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdRolloutRestart(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "") + cmdtesting.CheckCompletion(t, comps, []string{"deployment", "daemonset", "statefulset"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestCompletionRolloutRestartOneArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdRolloutRestart(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_resume_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_resume_test.go new file mode 100644 index 000000000000..cf4be53cd4a3 --- /dev/null +++ b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_resume_test.go @@ -0,0 +1,38 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package rollout + +import ( + "testing" + + "github.com/spf13/cobra" + cmdtesting "k8s.io/kubectl/pkg/cmd/testing" +) + +func TestCompletionRolloutResumeNoArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdRolloutResume(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "") + cmdtesting.CheckCompletion(t, comps, []string{"deployment"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestCompletionRolloutResumeOneArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdRolloutResume(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_status_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_status_test.go new file mode 100644 index 000000000000..bcb0eef7627a --- /dev/null +++ b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_status_test.go @@ -0,0 +1,38 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package rollout + +import ( + "testing" + + "github.com/spf13/cobra" + cmdtesting "k8s.io/kubectl/pkg/cmd/testing" +) + +func TestCompletionRolloutStatusNoArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdRolloutStatus(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "") + cmdtesting.CheckCompletion(t, comps, []string{"deployment", "daemonset", "statefulset"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestCompletionRolloutStatusOneArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdRolloutStatus(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_undo_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_undo_test.go new file mode 100644 index 000000000000..c28d3767734e --- /dev/null +++ b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_undo_test.go @@ -0,0 +1,38 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package rollout + +import ( + "testing" + + "github.com/spf13/cobra" + cmdtesting "k8s.io/kubectl/pkg/cmd/testing" +) + +func TestCompletionRolloutUndoNoArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdRolloutUndo(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "") + cmdtesting.CheckCompletion(t, comps, []string{"deployment", "daemonset", "statefulset"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestCompletionRolloutUndoOneArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdRolloutUndo(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/scale/scale_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/scale/scale_test.go new file mode 100644 index 000000000000..53d5aca35259 --- /dev/null +++ b/staging/src/k8s.io/kubectl/pkg/cmd/scale/scale_test.go @@ -0,0 +1,38 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package scale + +import ( + "testing" + + "github.com/spf13/cobra" + cmdtesting "k8s.io/kubectl/pkg/cmd/testing" +) + +func TestCompletionScaleNoArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdScale(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "") + cmdtesting.CheckCompletion(t, comps, []string{"deployment", "replicaset", "replicationcontroller", "statefulset"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestCompletionScaleOneArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdScale(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/taint/taint_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/taint/taint_test.go index a154c97dfd87..200bd96e0c57 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/taint/taint_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/taint/taint_test.go @@ -24,6 +24,7 @@ import ( "testing" "time" + "github.com/spf13/cobra" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -388,6 +389,20 @@ func TestValidateFlags(t *testing.T) { } } +func TestCompletionTaintNoArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdTaint(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "") + cmdtesting.CheckCompletion(t, comps, []string{"node"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestCompletionTaintOneArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdTaint(tf, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + type MyReq struct { Request *http.Request } diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/testing/completion.go b/staging/src/k8s.io/kubectl/pkg/cmd/testing/completion.go new file mode 100644 index 000000000000..6ca72ac89750 --- /dev/null +++ b/staging/src/k8s.io/kubectl/pkg/cmd/testing/completion.go @@ -0,0 +1,158 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package testing + +import ( + "io/ioutil" + "net/http" + "os" + "sort" + "testing" + "time" + + "github.com/spf13/cobra" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/cli-runtime/pkg/genericclioptions" + "k8s.io/cli-runtime/pkg/resource" + "k8s.io/client-go/rest/fake" + "k8s.io/client-go/tools/clientcmd" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + cmdutil "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/kubectl/pkg/scheme" +) + +// PreparePodsForCompletion prepares the test factory and streams for pods +func PreparePodsForCompletion(t *testing.T) (cmdutil.Factory, genericclioptions.IOStreams) { + pods, _, _ := TestData() + + tf := NewTestFactory().WithNamespace("test") + defer tf.Cleanup() + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, + Resp: &http.Response{StatusCode: http.StatusOK, Header: DefaultHeader(), Body: ObjBody(codec, pods)}, + } + + streams, _, _, _ := genericclioptions.NewTestIOStreams() + return tf, streams +} + +// PrepareNodesForCompletion prepares the test factory and streams for pods +func PrepareNodesForCompletion(t *testing.T) (cmdutil.Factory, genericclioptions.IOStreams) { + // TODO create more than one node + // nodes := &corev1.NodeList{ + // ListMeta: metav1.ListMeta{ + // ResourceVersion: "1", + // }, + // Items: []corev1.Node{ + // { + // ObjectMeta: metav1.ObjectMeta{ + // Name: "firstnode", + // CreationTimestamp: metav1.Time{Time: time.Now()}, + // }, + // Status: corev1.NodeStatus{}, + // }, + // { + // ObjectMeta: metav1.ObjectMeta{ + // Name: "secondnode", + // CreationTimestamp: metav1.Time{Time: time.Now()}, + // }, + // Status: corev1.NodeStatus{}, + // }, + // }, + // } + + nodes := &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: "firstnode", + CreationTimestamp: metav1.Time{Time: time.Now()}, + }, + Status: corev1.NodeStatus{}, + } + + tf := NewTestFactory() + defer tf.Cleanup() + + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + ns := scheme.Codecs.WithoutConversion() + + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: ns, + Resp: &http.Response{StatusCode: http.StatusOK, Header: DefaultHeader(), Body: ObjBody(codec, nodes)}, + } + + streams, _, _, _ := genericclioptions.NewTestIOStreams() + return tf, streams +} + +// PrepareConfigForCompletion prepares some contexts for completion testing +func PrepareConfigForCompletion(t *testing.T) (*clientcmd.PathOptions, genericclioptions.IOStreams, cmdutil.Factory) { + conf := clientcmdapi.Config{ + Kind: "Config", + APIVersion: "v1", + Clusters: map[string]*clientcmdapi.Cluster{ + "minikube-cluster": {Server: "https://192.168.99.100:8443"}, + "my-cluster": {Server: "https://192.168.0.1:3434"}, + }, + Contexts: map[string]*clientcmdapi.Context{ + "minikube-context": {AuthInfo: "minikube", Cluster: "minikube"}, + "my-context": {AuthInfo: "my-context", Cluster: "my-context"}, + }, + CurrentContext: "minikube-context", + } + + fakeKubeFile, err := ioutil.TempFile(os.TempDir(), "") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + defer os.Remove(fakeKubeFile.Name()) + err = clientcmd.WriteToFile(conf, fakeKubeFile.Name()) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + pathOptions := clientcmd.NewDefaultPathOptions() + pathOptions.GlobalFile = fakeKubeFile.Name() + pathOptions.EnvVar = "" + + streams, _, _, _ := genericclioptions.NewTestIOStreams() + factory := cmdutil.NewFactory(genericclioptions.NewTestConfigFlags()) + + return pathOptions, streams, factory +} + +// CheckCompletion checks that the directive is correct and that each completion is present +func CheckCompletion(t *testing.T, comps, expectedComps []string, directive, expectedDirective cobra.ShellCompDirective) { + if e, d := expectedDirective, directive; e != d { + t.Errorf("expected directive\n%v\nbut got\n%v", e, d) + } + + sort.Strings(comps) + sort.Strings(expectedComps) + + if len(expectedComps) != len(comps) { + t.Fatalf("expected completions\n%v\nbut got\n%v", expectedComps, comps) + } + + for i := range comps { + if expectedComps[i] != comps[i] { + t.Errorf("expected completions\n%v\nbut got\n%v", expectedComps, comps) + break + } + } +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/top/top_node_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/top/top_node_test.go index dca44b83f244..528b7358ed20 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/top/top_node_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/top/top_node_test.go @@ -25,6 +25,7 @@ import ( "strings" "testing" + "github.com/spf13/cobra" "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/cli-runtime/pkg/genericclioptions" @@ -424,3 +425,10 @@ func TestTopNodeWithSortByMemoryMetricsFrom(t *testing.T) { } } + +func TestCompletionTopNodeNoArg(t *testing.T) { + tf, streams := cmdtesting.PrepareNodesForCompletion(t) + cmd := NewCmdTopNode(tf, nil, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "f") + cmdtesting.CheckCompletion(t, comps, []string{"firstnode"}, directive, cobra.ShellCompDirectiveNoFileComp) +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/top/top_pod_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/top/top_pod_test.go index d8b575cc79fb..7986f77bdc08 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/top/top_pod_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/top/top_pod_test.go @@ -26,6 +26,7 @@ import ( "testing" "time" + "github.com/spf13/cobra" "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -393,6 +394,13 @@ func TestTopPodNoResourcesFound(t *testing.T) { } } +func TestCompletionTopPodNoArg(t *testing.T) { + tf, streams := cmdtesting.PreparePodsForCompletion(t) + cmd := NewCmdTopPod(tf, nil, streams) + comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "b") + cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + func testV1beta1PodMetricsData() []metricsv1beta1api.PodMetrics { return []metricsv1beta1api.PodMetrics{ { From 9d03185d3c12a0b3f2f36a5096d4a9658bba5472 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Mon, 3 May 2021 14:18:00 -0400 Subject: [PATCH 4/8] Update staging/src/k8s.io/kubectl/pkg/cmd/config/config.go Co-authored-by: Philipp Stehle --- staging/src/k8s.io/kubectl/pkg/cmd/config/config.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/config.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/config.go index 4930e0d43fe0..4e1bbdffa25e 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/config.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/config.go @@ -110,7 +110,7 @@ func helpErrorf(cmd *cobra.Command, format string, args ...interface{}) error { // The completion function need the factory, so we initialize them once it is available func initCompletionFunctions(f cmdutil.Factory) { - CompListContextsInConfig = func(notused cmdutil.Factory, toComplete string) []string { + CompListContextsInConfig = func(_ cmdutil.Factory, toComplete string) []string { config, err := f.ToRawKubeConfigLoader().RawConfig() if err != nil { return nil @@ -124,7 +124,7 @@ func initCompletionFunctions(f cmdutil.Factory) { return ret } - CompListClustersInConfig = func(notused cmdutil.Factory, toComplete string) []string { + CompListClustersInConfig = func(_ cmdutil.Factory, toComplete string) []string { config, err := f.ToRawKubeConfigLoader().RawConfig() if err != nil { return nil @@ -138,7 +138,7 @@ func initCompletionFunctions(f cmdutil.Factory) { return ret } - CompListUsersInConfig = func(notused cmdutil.Factory, toComplete string) []string { + CompListUsersInConfig = func(_ cmdutil.Factory, toComplete string) []string { config, err := f.ToRawKubeConfigLoader().RawConfig() if err != nil { return nil From 9625872d1e4fd0d4d397b3ab732c6969a3a376fe Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Sat, 15 May 2021 21:22:42 -0400 Subject: [PATCH 5/8] Join common functions for completion Signed-off-by: Marc Khouzam --- .../kubectl/pkg/cmd/annotate/annotate.go | 13 +- .../pkg/cmd/apply/apply_edit_last_applied.go | 13 +- .../pkg/cmd/apply/apply_view_last_applied.go | 12 +- .../k8s.io/kubectl/pkg/cmd/attach/attach.go | 10 +- .../kubectl/pkg/cmd/autoscale/autoscale.go | 16 +- staging/src/k8s.io/kubectl/pkg/cmd/cmd.go | 8 +- .../k8s.io/kubectl/pkg/cmd/config/config.go | 60 ------- .../kubectl/pkg/cmd/config/delete_cluster.go | 8 +- .../kubectl/pkg/cmd/config/delete_context.go | 8 +- .../kubectl/pkg/cmd/config/rename_context.go | 8 +- .../kubectl/pkg/cmd/config/use_context.go | 8 +- .../k8s.io/kubectl/pkg/cmd/delete/delete.go | 13 +- .../kubectl/pkg/cmd/describe/describe.go | 13 +- .../src/k8s.io/kubectl/pkg/cmd/drain/drain.go | 26 +-- .../src/k8s.io/kubectl/pkg/cmd/edit/edit.go | 13 +- .../src/k8s.io/kubectl/pkg/cmd/exec/exec.go | 10 +- .../k8s.io/kubectl/pkg/cmd/expose/expose.go | 15 +- .../src/k8s.io/kubectl/pkg/cmd/label/label.go | 13 +- .../src/k8s.io/kubectl/pkg/cmd/logs/logs.go | 11 +- .../src/k8s.io/kubectl/pkg/cmd/patch/patch.go | 13 +- .../pkg/cmd/portforward/portforward.go | 9 +- .../pkg/cmd/rollout/rollout_history.go | 17 +- .../kubectl/pkg/cmd/rollout/rollout_pause.go | 17 +- .../pkg/cmd/rollout/rollout_restart.go | 17 +- .../kubectl/pkg/cmd/rollout/rollout_resume.go | 17 +- .../kubectl/pkg/cmd/rollout/rollout_status.go | 17 +- .../kubectl/pkg/cmd/rollout/rollout_undo.go | 17 +- .../src/k8s.io/kubectl/pkg/cmd/scale/scale.go | 17 +- .../src/k8s.io/kubectl/pkg/cmd/taint/taint.go | 16 +- .../k8s.io/kubectl/pkg/cmd/top/top_node.go | 10 +- .../src/k8s.io/kubectl/pkg/cmd/top/top_pod.go | 10 +- .../src/k8s.io/kubectl/pkg/util/completion.go | 158 ++++++++++++++++++ 32 files changed, 218 insertions(+), 395 deletions(-) create mode 100644 staging/src/k8s.io/kubectl/pkg/util/completion.go diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/annotate/annotate.go b/staging/src/k8s.io/kubectl/pkg/cmd/annotate/annotate.go index abb081abd651..e4c2ce0ad6b4 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/annotate/annotate.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/annotate/annotate.go @@ -35,11 +35,10 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" - "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" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -136,15 +135,7 @@ func NewCmdAnnotate(parent string, f cmdutil.Factory, ioStreams genericclioption Short: i18n.T("Update the annotations on a resource"), Long: annotateLong + "\n\n" + cmdutil.SuggestAPIResources(parent), Example: annotateExample, - 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 if len(args) == 1 { - comps = get.CompGetResource(f, cmd, args[0], toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.ResourceTypeAndNameCompletionFunc(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/apply/apply_edit_last_applied.go b/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_edit_last_applied.go index ba677d002862..47e375cc6796 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_edit_last_applied.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_edit_last_applied.go @@ -20,10 +20,9 @@ import ( "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" - "k8s.io/kubectl/pkg/cmd/apiresources" - "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/cmd/util/editor" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -68,15 +67,7 @@ func NewCmdApplyEditLastApplied(f cmdutil.Factory, ioStreams genericclioptions.I Short: i18n.T("Edit latest last-applied-configuration annotations of a resource/object"), Long: applyEditLastAppliedLong, Example: applyEditLastAppliedExample, - 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 if len(args) == 1 { - comps = get.CompGetResource(f, cmd, args[0], toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.ResourceTypeAndNameCompletionFunc(f), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, args, cmd)) cmdutil.CheckErr(o.Run()) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_view_last_applied.go b/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_view_last_applied.go index 939b926bafc3..74e7f37f6d24 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_view_last_applied.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_view_last_applied.go @@ -24,8 +24,6 @@ import ( "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/resource" - "k8s.io/kubectl/pkg/cmd/apiresources" - "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" @@ -79,15 +77,7 @@ func NewCmdApplyViewLastApplied(f cmdutil.Factory, ioStreams genericclioptions.I Short: i18n.T("View latest last-applied-configuration annotations of a resource/object"), Long: applyViewLastAppliedLong, Example: applyViewLastAppliedExample, - 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 if len(args) == 1 { - comps = get.CompGetResource(f, cmd, args[0], toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.ResourceTypeAndNameCompletionFunc(f), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(options.Complete(cmd, f, args)) cmdutil.CheckErr(options.Validate(cmd)) 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 37c48776c9d6..f89cfd83eb6c 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach.go @@ -31,11 +31,11 @@ import ( restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/remotecommand" "k8s.io/kubectl/pkg/cmd/exec" - "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/cmd/util/podcmd" "k8s.io/kubectl/pkg/polymorphichelpers" "k8s.io/kubectl/pkg/scheme" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -105,13 +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: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - comps = get.CompGetResource(f, cmd, "pod", toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.ResourceNameCompletionFunc(f, "pod"), 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/autoscale/autoscale.go b/staging/src/k8s.io/kubectl/pkg/cmd/autoscale/autoscale.go index bf08890d324d..afe7117dde47 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/autoscale/autoscale.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/autoscale/autoscale.go @@ -19,7 +19,6 @@ package autoscale import ( "context" "fmt" - "strings" "github.com/spf13/cobra" "k8s.io/klog/v2" @@ -32,7 +31,6 @@ import ( "k8s.io/cli-runtime/pkg/resource" autoscalingv1client "k8s.io/client-go/kubernetes/typed/autoscaling/v1" "k8s.io/client-go/scale" - "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/scheme" "k8s.io/kubectl/pkg/util" @@ -109,19 +107,7 @@ func NewCmdAutoscale(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) * Short: i18n.T("Auto-scale a Deployment, ReplicaSet, StatefulSet, or ReplicationController"), Long: autoscaleLong, Example: autoscaleExample, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - for _, comp := range validArgs { - if strings.HasPrefix(comp, toComplete) { - comps = append(comps, comp) - } - } - } else if len(args) == 1 { - comps = get.CompGetResource(f, cmd, args[0], toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.SpecifiedResourceTypeAndNameCompletionFunc(f, validArgs), 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/cmd.go b/staging/src/k8s.io/kubectl/pkg/cmd/cmd.go index f397c334b68b..5c4713286e64 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/cmd.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/cmd.go @@ -71,6 +71,7 @@ import ( cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/cmd/version" "k8s.io/kubectl/pkg/cmd/wait" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" "k8s.io/kubectl/pkg/util/term" @@ -379,6 +380,7 @@ func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command { templates.ActsAsRootCommand(cmds, filters, groups...) + util.SetFactoryForCompletion(f) registerCompletionFuncForGlobalFlags(cmds, f) cmds.AddCommand(alpha) @@ -437,16 +439,16 @@ func registerCompletionFuncForGlobalFlags(cmd *cobra.Command, f cmdutil.Factory) cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc( "context", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return cmdconfig.CompListContextsInConfig(nil, toComplete), cobra.ShellCompDirectiveNoFileComp + return util.ListContextsInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp })) cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc( "cluster", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return cmdconfig.CompListClustersInConfig(nil, toComplete), cobra.ShellCompDirectiveNoFileComp + return util.ListClustersInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp })) cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc( "user", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return cmdconfig.CompListUsersInConfig(nil, toComplete), cobra.ShellCompDirectiveNoFileComp + return util.ListUsersInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp })) } diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/config.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/config.go index 4e1bbdffa25e..74a0e2175158 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/config.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/config.go @@ -20,7 +20,6 @@ import ( "fmt" "path" "strconv" - "strings" "github.com/spf13/cobra" @@ -31,22 +30,8 @@ import ( "k8s.io/kubectl/pkg/util/templates" ) -var ( - // CompListContextsInConfig returns a list of context names which begin with `toComplete` - // We allow to pass in a factory to be ready for a future improvement - CompListContextsInConfig func(f cmdutil.Factory, toComplete string) []string - // CompListClustersInConfig returns a list of cluster names which begin with `toComplete` - // We allow to pass in a factory to be ready for a future improvement - CompListClustersInConfig func(f cmdutil.Factory, toComplete string) []string - // CompListUsersInConfig returns a list of user names which begin with `toComplete` - // We allow to pass in a factory to be ready for a future improvement - CompListUsersInConfig func(f cmdutil.Factory, toComplete string) []string -) - // NewCmdConfig creates a command object for the "config" action, and adds all child commands to it. func NewCmdConfig(f cmdutil.Factory, pathOptions *clientcmd.PathOptions, streams genericclioptions.IOStreams) *cobra.Command { - initCompletionFunctions(f) - if len(pathOptions.ExplicitFileFlag) == 0 { pathOptions.ExplicitFileFlag = clientcmd.RecommendedConfigPathFlag } @@ -107,48 +92,3 @@ func helpErrorf(cmd *cobra.Command, format string, args ...interface{}) error { msg := fmt.Sprintf(format, args...) return fmt.Errorf("%s", msg) } - -// The completion function need the factory, so we initialize them once it is available -func initCompletionFunctions(f cmdutil.Factory) { - CompListContextsInConfig = func(_ cmdutil.Factory, toComplete string) []string { - config, err := f.ToRawKubeConfigLoader().RawConfig() - if err != nil { - return nil - } - var ret []string - for name := range config.Contexts { - if strings.HasPrefix(name, toComplete) { - ret = append(ret, name) - } - } - return ret - } - - CompListClustersInConfig = func(_ cmdutil.Factory, toComplete string) []string { - config, err := f.ToRawKubeConfigLoader().RawConfig() - if err != nil { - return nil - } - var ret []string - for name := range config.Clusters { - if strings.HasPrefix(name, toComplete) { - ret = append(ret, name) - } - } - return ret - } - - CompListUsersInConfig = func(_ cmdutil.Factory, toComplete string) []string { - config, err := f.ToRawKubeConfigLoader().RawConfig() - if err != nil { - return nil - } - var ret []string - for name := range config.AuthInfos { - if strings.HasPrefix(name, toComplete) { - ret = append(ret, name) - } - } - return ret - } -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_cluster.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_cluster.go index e76b3ae4d36e..23a09b2e38db 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_cluster.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_cluster.go @@ -23,6 +23,7 @@ import ( "github.com/spf13/cobra" "k8s.io/client-go/tools/clientcmd" cmdutil "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -41,12 +42,7 @@ func NewCmdConfigDeleteCluster(out io.Writer, configAccess clientcmd.ConfigAcces Short: i18n.T("Delete the specified cluster from the kubeconfig"), Long: i18n.T("Delete the specified cluster from the kubeconfig"), Example: deleteClusterExample, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) == 0 { - return CompListClustersInConfig(nil, toComplete), cobra.ShellCompDirectiveNoFileComp - } - return nil, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.ClusterCompletionFunc, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(runDeleteCluster(out, configAccess, cmd)) }, diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_context.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_context.go index 8828d2f1ffcd..7e12ba888c5a 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_context.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_context.go @@ -23,6 +23,7 @@ import ( "github.com/spf13/cobra" "k8s.io/client-go/tools/clientcmd" cmdutil "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -41,12 +42,7 @@ func NewCmdConfigDeleteContext(out, errOut io.Writer, configAccess clientcmd.Con Short: i18n.T("Delete the specified context from the kubeconfig"), Long: i18n.T("Delete the specified context from the kubeconfig"), Example: deleteContextExample, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) == 0 { - return CompListContextsInConfig(nil, toComplete), cobra.ShellCompDirectiveNoFileComp - } - return nil, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.ContextCompletionFunc, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(runDeleteContext(out, errOut, configAccess, cmd)) }, diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/rename_context.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/rename_context.go index 52fa7773ac84..153ba090ab59 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/rename_context.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/rename_context.go @@ -25,6 +25,7 @@ import ( "k8s.io/client-go/tools/clientcmd" cmdutil "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -67,12 +68,7 @@ func NewCmdConfigRenameContext(out io.Writer, configAccess clientcmd.ConfigAcces Short: renameContextShort, Long: renameContextLong, Example: renameContextExample, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) == 0 { - return CompListContextsInConfig(nil, toComplete), cobra.ShellCompDirectiveNoFileComp - } - return nil, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.ContextCompletionFunc, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(options.Complete(cmd, args, out)) cmdutil.CheckErr(options.Validate()) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/use_context.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/use_context.go index 1a8678b66a1a..758d82b9cbf6 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/use_context.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/use_context.go @@ -26,6 +26,7 @@ import ( "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" cmdutil "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -52,12 +53,7 @@ func NewCmdConfigUseContext(out io.Writer, configAccess clientcmd.ConfigAccess) Aliases: []string{"use"}, Long: `Sets the current-context in a kubeconfig file`, Example: useContextExample, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) == 0 { - return CompListContextsInConfig(nil, toComplete), cobra.ShellCompDirectiveNoFileComp - } - return nil, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.ContextCompletionFunc, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(options.complete(cmd)) cmdutil.CheckErr(options.run()) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/delete/delete.go b/staging/src/k8s.io/kubectl/pkg/cmd/delete/delete.go index 234ae3b6714f..1a48ecf2d657 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/delete/delete.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/delete/delete.go @@ -33,11 +33,10 @@ import ( "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" "k8s.io/client-go/dynamic" - "k8s.io/kubectl/pkg/cmd/apiresources" - "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" cmdwait "k8s.io/kubectl/pkg/cmd/wait" "k8s.io/kubectl/pkg/rawhttp" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -137,15 +136,7 @@ func NewCmdDelete(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra Short: i18n.T("Delete resources by filenames, stdin, resources and names, or by resources and label selector"), Long: deleteLong, Example: deleteExample, - 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 if len(args) == 1 { - comps = get.CompGetResource(f, cmd, args[0], toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.ResourceTypeAndNameCompletionFunc(f), Run: func(cmd *cobra.Command, args []string) { o, err := deleteFlags.ToOptions(nil, streams) cmdutil.CheckErr(err) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/describe/describe.go b/staging/src/k8s.io/kubectl/pkg/cmd/describe/describe.go index 7199b2b932a2..f2866f578d19 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/describe/describe.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/describe/describe.go @@ -28,10 +28,9 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/resource" - "k8s.io/kubectl/pkg/cmd/apiresources" - "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/describe" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -108,15 +107,7 @@ func NewCmdDescribe(parent string, f cmdutil.Factory, streams genericclioptions. Short: i18n.T("Show details of a specific resource or group of resources"), Long: describeLong + "\n\n" + cmdutil.SuggestAPIResources(parent), Example: describeExample, - 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 if len(args) == 1 { - comps = get.CompGetResource(f, cmd, args[0], toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.ResourceTypeAndNameCompletionFunc(f), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.Run()) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go b/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go index e81488016e60..bf4cb5655b46 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go @@ -31,10 +31,10 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" - "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/drain" "k8s.io/kubectl/pkg/scheme" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -69,13 +69,7 @@ func NewCmdCordon(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cob Short: i18n.T("Mark node as unschedulable"), Long: cordonLong, Example: cordonExample, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - comps = get.CompGetResource(f, cmd, "node", toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.ResourceNameCompletionFunc(f, "node"), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.RunCordonOrUncordon(true)) @@ -104,13 +98,7 @@ func NewCmdUncordon(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *c Short: i18n.T("Mark node as schedulable"), Long: uncordonLong, Example: uncordonExample, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - comps = get.CompGetResource(f, cmd, "node", toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.ResourceNameCompletionFunc(f, "node"), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.RunCordonOrUncordon(false)) @@ -196,13 +184,7 @@ func NewCmdDrain(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobr Short: i18n.T("Drain node in preparation for maintenance"), Long: drainLong, Example: drainExample, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - comps = get.CompGetResource(f, cmd, "node", toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.ResourceNameCompletionFunc(f, "node"), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.RunDrain()) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/edit/edit.go b/staging/src/k8s.io/kubectl/pkg/cmd/edit/edit.go index faa25f15e8e7..7384fcc19344 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/edit/edit.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/edit/edit.go @@ -20,10 +20,9 @@ import ( "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" - "k8s.io/kubectl/pkg/cmd/apiresources" - "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/cmd/util/editor" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -78,15 +77,7 @@ func NewCmdEdit(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra Short: i18n.T("Edit a resource on the server"), Long: editLong, Example: editExample, - 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 if len(args) == 1 { - comps = get.CompGetResource(f, cmd, args[0], toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.ResourceTypeAndNameCompletionFunc(f), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, args, cmd)) cmdutil.CheckErr(o.Run()) 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 7354f9af77e6..5c8923d4a1ee 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/exec/exec.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/exec/exec.go @@ -33,11 +33,11 @@ import ( restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/remotecommand" - "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/cmd/util/podcmd" "k8s.io/kubectl/pkg/polymorphichelpers" "k8s.io/kubectl/pkg/scheme" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/interrupt" "k8s.io/kubectl/pkg/util/templates" @@ -89,13 +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: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - comps = get.CompGetResource(f, cmd, "pod", toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.ResourceNameCompletionFunc(f, "pod"), Run: func(cmd *cobra.Command, args []string) { argsLenAtDash := cmd.ArgsLenAtDash() cmdutil.CheckErr(options.Complete(f, cmd, args, argsLenAtDash)) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/expose/expose.go b/staging/src/k8s.io/kubectl/pkg/cmd/expose/expose.go index a63f4a41fb82..c95421dcc22b 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/expose/expose.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/expose/expose.go @@ -31,7 +31,6 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" - "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/generate" generateversioned "k8s.io/kubectl/pkg/generate/versioned" @@ -135,19 +134,7 @@ func NewCmdExposeService(f cmdutil.Factory, streams genericclioptions.IOStreams) Short: i18n.T("Take a replication controller, service, deployment or pod and expose it as a new Kubernetes Service"), Long: exposeLong, Example: exposeExample, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - for _, comp := range validArgs { - if strings.HasPrefix(comp, toComplete) { - comps = append(comps, comp) - } - } - } else if len(args) == 1 { - comps = get.CompGetResource(f, cmd, args[0], toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.SpecifiedResourceTypeAndNameCompletionFunc(f, validArgs), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd)) cmdutil.CheckErr(o.RunExpose(cmd, args)) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/label/label.go b/staging/src/k8s.io/kubectl/pkg/cmd/label/label.go index faaf67e21122..5781b6e628e7 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/label/label.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/label/label.go @@ -37,10 +37,9 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" - "k8s.io/kubectl/pkg/cmd/apiresources" - "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/scheme" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -134,15 +133,7 @@ func NewCmdLabel(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobr Short: i18n.T("Update the labels on a resource"), Long: fmt.Sprintf(labelLong, validation.LabelValueMaxLength), Example: labelExample, - 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 if len(args) == 1 { - comps = get.CompGetResource(f, cmd, args[0], toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.ResourceTypeAndNameCompletionFunc(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/logs/logs.go b/staging/src/k8s.io/kubectl/pkg/cmd/logs/logs.go index ecd4994b2dbb..e4625f43f318 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/logs/logs.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/logs/logs.go @@ -33,7 +33,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/client-go/rest" - "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/polymorphichelpers" "k8s.io/kubectl/pkg/scheme" @@ -153,15 +152,7 @@ func NewCmdLogs(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.C Short: i18n.T("Print the logs for a container in a pod"), Long: logsLong, Example: logsExample, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - comps = get.CompGetResource(f, cmd, "pod", toComplete) - } else if len(args) == 1 { - comps = get.CompGetContainers(f, cmd, args[0], toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.PodResourceNameAndContainerCompletionFunc(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/patch/patch.go b/staging/src/k8s.io/kubectl/pkg/cmd/patch/patch.go index 59151f7c1f89..f98fe9e8cde0 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/patch/patch.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/patch/patch.go @@ -37,10 +37,9 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" - "k8s.io/kubectl/pkg/cmd/apiresources" - "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/scheme" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -116,15 +115,7 @@ func NewCmdPatch(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobr Short: i18n.T("Update field(s) of a resource"), Long: patchLong, Example: patchExample, - 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 if len(args) == 1 { - comps = get.CompGetResource(f, cmd, args[0], toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.ResourceTypeAndNameCompletionFunc(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/portforward/portforward.go b/staging/src/k8s.io/kubectl/pkg/cmd/portforward/portforward.go index d55545593e7a..4770d1094029 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/portforward/portforward.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/portforward/portforward.go @@ -38,7 +38,6 @@ import ( restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/portforward" "k8s.io/client-go/transport/spdy" - "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/polymorphichelpers" "k8s.io/kubectl/pkg/util" @@ -110,13 +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: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - comps = get.CompGetResource(f, cmd, "pod", toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.ResourceNameCompletionFunc(f, "pod"), 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/cmd/rollout/rollout_history.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_history.go index c0aaee29c188..f51378793e4a 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_history.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_history.go @@ -18,17 +18,16 @@ package rollout import ( "fmt" - "strings" "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" - "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/polymorphichelpers" "k8s.io/kubectl/pkg/scheme" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -84,19 +83,7 @@ func NewCmdRolloutHistory(f cmdutil.Factory, streams genericclioptions.IOStreams Short: i18n.T("View rollout history"), Long: historyLong, Example: historyExample, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - for _, comp := range validArgs { - if strings.HasPrefix(comp, toComplete) { - comps = append(comps, comp) - } - } - } else if len(args) == 1 { - comps = get.CompGetResource(f, cmd, args[0], toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.SpecifiedResourceTypeAndNameCompletionFunc(f, validArgs), 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/rollout/rollout_pause.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_pause.go index 59b53748fb1d..b1ddd4970897 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_pause.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_pause.go @@ -18,7 +18,6 @@ package rollout import ( "fmt" - "strings" "github.com/spf13/cobra" @@ -27,11 +26,11 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" - "k8s.io/kubectl/pkg/cmd/get" "k8s.io/kubectl/pkg/cmd/set" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/polymorphichelpers" "k8s.io/kubectl/pkg/scheme" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -84,19 +83,7 @@ func NewCmdRolloutPause(f cmdutil.Factory, streams genericclioptions.IOStreams) Short: i18n.T("Mark the provided resource as paused"), Long: pauseLong, Example: pauseExample, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - for _, comp := range validArgs { - if strings.HasPrefix(comp, toComplete) { - comps = append(comps, comp) - } - } - } else if len(args) == 1 { - comps = get.CompGetResource(f, cmd, args[0], toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.SpecifiedResourceTypeAndNameCompletionFunc(f, validArgs), 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/rollout/rollout_restart.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_restart.go index e8cc3ed10085..13a86ab2ac7a 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_restart.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_restart.go @@ -18,7 +18,6 @@ package rollout import ( "fmt" - "strings" "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/types" @@ -26,11 +25,11 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" - "k8s.io/kubectl/pkg/cmd/get" "k8s.io/kubectl/pkg/cmd/set" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/polymorphichelpers" "k8s.io/kubectl/pkg/scheme" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -88,19 +87,7 @@ func NewCmdRolloutRestart(f cmdutil.Factory, streams genericclioptions.IOStreams Short: i18n.T("Restart a resource"), Long: restartLong, Example: restartExample, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - for _, comp := range validArgs { - if strings.HasPrefix(comp, toComplete) { - comps = append(comps, comp) - } - } - } else if len(args) == 1 { - comps = get.CompGetResource(f, cmd, args[0], toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.SpecifiedResourceTypeAndNameCompletionFunc(f, validArgs), 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/rollout/rollout_resume.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_resume.go index c31d9ff7831a..8659809c33a1 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_resume.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_resume.go @@ -18,7 +18,6 @@ package rollout import ( "fmt" - "strings" "github.com/spf13/cobra" @@ -27,11 +26,11 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" - "k8s.io/kubectl/pkg/cmd/get" "k8s.io/kubectl/pkg/cmd/set" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/polymorphichelpers" "k8s.io/kubectl/pkg/scheme" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -88,19 +87,7 @@ func NewCmdRolloutResume(f cmdutil.Factory, streams genericclioptions.IOStreams) Short: i18n.T("Resume a paused resource"), Long: resumeLong, Example: resumeExample, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - for _, comp := range validArgs { - if strings.HasPrefix(comp, toComplete) { - comps = append(comps, comp) - } - } - } else if len(args) == 1 { - comps = get.CompGetResource(f, cmd, args[0], toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.SpecifiedResourceTypeAndNameCompletionFunc(f, validArgs), 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/rollout/rollout_status.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_status.go index b6e5a0decb73..11704af03a91 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_status.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_status.go @@ -19,7 +19,6 @@ package rollout import ( "context" "fmt" - "strings" "time" "github.com/spf13/cobra" @@ -35,10 +34,10 @@ import ( "k8s.io/client-go/dynamic" "k8s.io/client-go/tools/cache" watchtools "k8s.io/client-go/tools/watch" - "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/polymorphichelpers" "k8s.io/kubectl/pkg/scheme" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/interrupt" "k8s.io/kubectl/pkg/util/templates" @@ -103,19 +102,7 @@ func NewCmdRolloutStatus(f cmdutil.Factory, streams genericclioptions.IOStreams) Short: i18n.T("Show the status of the rollout"), Long: statusLong, Example: statusExample, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - for _, comp := range validArgs { - if strings.HasPrefix(comp, toComplete) { - comps = append(comps, comp) - } - } - } else if len(args) == 1 { - comps = get.CompGetResource(f, cmd, args[0], toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.SpecifiedResourceTypeAndNameCompletionFunc(f, validArgs), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, args)) cmdutil.CheckErr(o.Validate()) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_undo.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_undo.go index 4debce3b62f0..aabf67d10f73 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_undo.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_undo.go @@ -18,17 +18,16 @@ package rollout import ( "fmt" - "strings" "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" - "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/polymorphichelpers" "k8s.io/kubectl/pkg/scheme" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -88,19 +87,7 @@ func NewCmdRolloutUndo(f cmdutil.Factory, streams genericclioptions.IOStreams) * Short: i18n.T("Undo a previous rollout"), Long: undoLong, Example: undoExample, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - for _, comp := range validArgs { - if strings.HasPrefix(comp, toComplete) { - comps = append(comps, comp) - } - } - } else if len(args) == 1 { - comps = get.CompGetResource(f, cmd, args[0], toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.SpecifiedResourceTypeAndNameCompletionFunc(f, validArgs), 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/scale/scale.go b/staging/src/k8s.io/kubectl/pkg/cmd/scale/scale.go index 8978e7c54fd4..a87c3e8e5f5d 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/scale/scale.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/scale/scale.go @@ -18,7 +18,6 @@ package scale import ( "fmt" - "strings" "time" "github.com/spf13/cobra" @@ -30,9 +29,9 @@ import ( "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" "k8s.io/client-go/kubernetes" - "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/scale" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -115,19 +114,7 @@ func NewCmdScale(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobr Short: i18n.T("Set a new size for a Deployment, ReplicaSet or Replication Controller"), Long: scaleLong, Example: scaleExample, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - for _, comp := range validArgs { - if strings.HasPrefix(comp, toComplete) { - comps = append(comps, comp) - } - } - } else if len(args) == 1 { - comps = get.CompGetResource(f, cmd, args[0], toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.SpecifiedResourceTypeAndNameCompletionFunc(f, validArgs), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.Validate(cmd)) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/taint/taint.go b/staging/src/k8s.io/kubectl/pkg/cmd/taint/taint.go index 4f6c7409f986..33b1608a3f02 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/taint/taint.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/taint/taint.go @@ -24,6 +24,7 @@ import ( "github.com/spf13/cobra" "k8s.io/klog/v2" "k8s.io/kubectl/pkg/explain" + "k8s.io/kubectl/pkg/util" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" @@ -35,7 +36,6 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" - "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/scheme" "k8s.io/kubectl/pkg/util/i18n" @@ -109,19 +109,7 @@ func NewCmdTaint(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra. Short: i18n.T("Update the taints on one or more nodes"), Long: fmt.Sprintf(taintLong, validation.DNS1123SubdomainMaxLength, validation.LabelValueMaxLength), Example: taintExample, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - for _, comp := range validArgs { - if strings.HasPrefix(comp, toComplete) { - comps = append(comps, comp) - } - } - } else if len(args) == 1 { - comps = get.CompGetResource(f, cmd, args[0], toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.SpecifiedResourceTypeAndNameCompletionFunc(f, validArgs), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(options.Complete(f, cmd, args)) cmdutil.CheckErr(options.Validate()) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/top/top_node.go b/staging/src/k8s.io/kubectl/pkg/cmd/top/top_node.go index 2561323444c7..5febad85d564 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/top/top_node.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/top/top_node.go @@ -27,9 +27,9 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/client-go/discovery" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" - "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/metricsutil" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" metricsapi "k8s.io/metrics/pkg/apis/metrics" @@ -81,13 +81,7 @@ func NewCmdTopNode(f cmdutil.Factory, o *TopNodeOptions, streams genericclioptio Short: i18n.T("Display Resource (CPU/Memory) usage of nodes"), Long: topNodeLong, Example: topNodeExample, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - comps = get.CompGetResource(f, cmd, "node", toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.ResourceNameCompletionFunc(f, "node"), 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/top/top_pod.go b/staging/src/k8s.io/kubectl/pkg/cmd/top/top_pod.go index eeb1981a2a6b..bfaaf9270e1a 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/top/top_pod.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/top/top_pod.go @@ -27,9 +27,9 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/discovery" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" - "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/metricsutil" + "k8s.io/kubectl/pkg/util" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" metricsapi "k8s.io/metrics/pkg/apis/metrics" @@ -98,13 +98,7 @@ func NewCmdTopPod(f cmdutil.Factory, o *TopPodOptions, streams genericclioptions Short: i18n.T("Display Resource (CPU/Memory) usage of pods"), Long: topPodLong, Example: topPodExample, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - comps = get.CompGetResource(f, cmd, "pod", toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + ValidArgsFunction: util.ResourceNameCompletionFunc(f, "pod"), 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/util/completion.go b/staging/src/k8s.io/kubectl/pkg/util/completion.go new file mode 100644 index 000000000000..3253b75764c6 --- /dev/null +++ b/staging/src/k8s.io/kubectl/pkg/util/completion.go @@ -0,0 +1,158 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "strings" + + "github.com/spf13/cobra" + "k8s.io/kubectl/pkg/cmd/apiresources" + "k8s.io/kubectl/pkg/cmd/get" + cmdutil "k8s.io/kubectl/pkg/cmd/util" +) + +var factory cmdutil.Factory + +// SetFactoryForCompletion Store the factory which is needed by the completion functions +// Not all commands have access to the factory, so cannot pass it to the completion functions. +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 as a second argument the resource names that match +// the toComplete prefix. +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 if len(args) == 1 { + comps = get.CompGetResource(f, cmd, args[0], toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + } +} + +// 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 as a second argument the specified resource names that match the toComplete prefix. +func SpecifiedResourceTypeAndNameCompletionFunc(f cmdutil.Factory, allowedTypes []string) 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 len(args) == 1 { + comps = get.CompGetResource(f, cmd, args[0], toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + } +} + +// 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. +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 + if len(args) == 0 { + comps = get.CompGetResource(f, cmd, resourceType, toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + } +} + +// 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. +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 + if len(args) == 0 { + comps = get.CompGetResource(f, cmd, "pod", toComplete) + } else if len(args) == 1 { + comps = get.CompGetContainers(f, cmd, args[0], toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + } +} + +// ContextCompletionFunc is a completion function that completes as a first argument the +// context names that match the toComplete prefix +func ContextCompletionFunc(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) == 0 { + return ListContextsInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp + } + return nil, cobra.ShellCompDirectiveNoFileComp +} + +// ClusterCompletionFunc is a completion function that completes as a first argument the +// cluster names that match the toComplete prefix +func ClusterCompletionFunc(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) == 0 { + return ListClustersInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp + } + return nil, cobra.ShellCompDirectiveNoFileComp +} + +// ListContextsInConfig returns a list of context names which begin with `toComplete` +func ListContextsInConfig(toComplete string) []string { + config, err := factory.ToRawKubeConfigLoader().RawConfig() + if err != nil { + return nil + } + var ret []string + for name := range config.Contexts { + if strings.HasPrefix(name, toComplete) { + ret = append(ret, name) + } + } + return ret +} + +// ListClustersInConfig returns a list of cluster names which begin with `toComplete` +func ListClustersInConfig(toComplete string) []string { + config, err := factory.ToRawKubeConfigLoader().RawConfig() + if err != nil { + return nil + } + var ret []string + for name := range config.Clusters { + if strings.HasPrefix(name, toComplete) { + ret = append(ret, name) + } + } + return ret +} + +// ListUsersInConfig returns a list of user names which begin with `toComplete` +func ListUsersInConfig(toComplete string) []string { + config, err := factory.ToRawKubeConfigLoader().RawConfig() + if err != nil { + return nil + } + var ret []string + for name := range config.AuthInfos { + if strings.HasPrefix(name, toComplete) { + ret = append(ret, name) + } + } + return ret +} From 1babceac650cfe8ec7ac71b34ed1ecf118b50f9d Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Sat, 22 May 2021 20:47:14 -0400 Subject: [PATCH 6/8] Revert "Add Go tests for custom completions" This reverts commit 0e18f0380042b652996d795559bfb818698abec3. --- .../kubectl/pkg/cmd/annotate/annotate_test.go | 8 - .../cmd/apply/apply_edit_last_applied_test.go | 31 ---- .../cmd/apply/apply_view_last_applied_test.go | 31 ---- .../kubectl/pkg/cmd/attach/attach_test.go | 8 - .../pkg/cmd/autoscale/autoscale_test.go | 38 ----- .../pkg/cmd/config/delete_cluster_test.go | 17 -- .../pkg/cmd/config/delete_context_test.go | 17 -- .../pkg/cmd/config/rename_context_test.go | 17 -- .../pkg/cmd/config/use_context_test.go | 17 -- .../kubectl/pkg/cmd/delete/delete_test.go | 15 -- .../kubectl/pkg/cmd/describe/describe_test.go | 8 - .../kubectl/pkg/cmd/drain/drain_test.go | 21 --- .../k8s.io/kubectl/pkg/cmd/edit/edit_test.go | 7 - .../k8s.io/kubectl/pkg/cmd/exec/exec_test.go | 8 - .../kubectl/pkg/cmd/expose/expose_test.go | 16 -- .../k8s.io/kubectl/pkg/cmd/get/get_test.go | 8 - .../kubectl/pkg/cmd/label/label_test.go | 8 - .../k8s.io/kubectl/pkg/cmd/logs/logs_test.go | 8 - .../kubectl/pkg/cmd/patch/patch_test.go | 8 - .../pkg/cmd/portforward/portforward_test.go | 7 - .../pkg/cmd/rollout/rollout_history_test.go | 38 ----- .../pkg/cmd/rollout/rollout_pause_test.go | 15 -- .../pkg/cmd/rollout/rollout_restart_test.go | 38 ----- .../pkg/cmd/rollout/rollout_resume_test.go | 38 ----- .../pkg/cmd/rollout/rollout_status_test.go | 38 ----- .../pkg/cmd/rollout/rollout_undo_test.go | 38 ----- .../kubectl/pkg/cmd/scale/scale_test.go | 38 ----- .../kubectl/pkg/cmd/taint/taint_test.go | 15 -- .../kubectl/pkg/cmd/testing/completion.go | 158 ------------------ .../kubectl/pkg/cmd/top/top_node_test.go | 8 - .../kubectl/pkg/cmd/top/top_pod_test.go | 8 - 31 files changed, 730 deletions(-) delete mode 100644 staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_edit_last_applied_test.go delete mode 100644 staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_view_last_applied_test.go delete mode 100644 staging/src/k8s.io/kubectl/pkg/cmd/autoscale/autoscale_test.go delete mode 100644 staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_history_test.go delete mode 100644 staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_restart_test.go delete mode 100644 staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_resume_test.go delete mode 100644 staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_status_test.go delete mode 100644 staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_undo_test.go delete mode 100644 staging/src/k8s.io/kubectl/pkg/cmd/scale/scale_test.go delete mode 100644 staging/src/k8s.io/kubectl/pkg/cmd/testing/completion.go diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/annotate/annotate_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/annotate/annotate_test.go index 4636b66b06da..c1306f1fa445 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/annotate/annotate_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/annotate/annotate_test.go @@ -24,7 +24,6 @@ import ( "strings" "testing" - "github.com/spf13/cobra" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -711,10 +710,3 @@ func TestAnnotateMultipleObjects(t *testing.T) { t.Fatalf("unexpected error: %v", err) } } - -func TestCompletionAnnotateOneArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdAnnotate("kubectl", tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_edit_last_applied_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_edit_last_applied_test.go deleted file mode 100644 index e6b17629d3bd..000000000000 --- a/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_edit_last_applied_test.go +++ /dev/null @@ -1,31 +0,0 @@ -/* -Copyright 2021 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package apply - -import ( - "testing" - - "github.com/spf13/cobra" - cmdtesting "k8s.io/kubectl/pkg/cmd/testing" -) - -func TestCompletionApplyEditLastAppliedOneArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdApplyEditLastApplied(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_view_last_applied_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_view_last_applied_test.go deleted file mode 100644 index e331acf0929f..000000000000 --- a/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_view_last_applied_test.go +++ /dev/null @@ -1,31 +0,0 @@ -/* -Copyright 2021 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package apply - -import ( - "testing" - - "github.com/spf13/cobra" - cmdtesting "k8s.io/kubectl/pkg/cmd/testing" -) - -func TestCompletionApplyViewLastAppliedOneArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdApplyViewLastApplied(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach_test.go index 6f02f5ad1213..c9b809a441a1 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach_test.go @@ -26,7 +26,6 @@ import ( "testing" "time" - "github.com/spf13/cobra" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -448,13 +447,6 @@ func TestAttachWarnings(t *testing.T) { } } -func TestCompletionAttachNoArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdAttach(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} - func attachPod() *corev1.Pod { return &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"}, diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/autoscale/autoscale_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/autoscale/autoscale_test.go deleted file mode 100644 index e68e8321fbf3..000000000000 --- a/staging/src/k8s.io/kubectl/pkg/cmd/autoscale/autoscale_test.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2021 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package autoscale - -import ( - "testing" - - "github.com/spf13/cobra" - cmdtesting "k8s.io/kubectl/pkg/cmd/testing" -) - -func TestCompletionAutoscaleNoArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdAutoscale(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "") - cmdtesting.CheckCompletion(t, comps, []string{"deployment", "replicationcontroller", "replicaset", "statefulset"}, directive, cobra.ShellCompDirectiveNoFileComp) -} - -func TestCompletionAutoscaleOneArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdAutoscale(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_cluster_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_cluster_test.go index 52149cfc2b67..3becba3fdf69 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_cluster_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_cluster_test.go @@ -24,10 +24,8 @@ import ( "reflect" "testing" - "github.com/spf13/cobra" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" - cmdtesting "k8s.io/kubectl/pkg/cmd/testing" ) type deleteClusterTest struct { @@ -97,18 +95,3 @@ func (test deleteClusterTest) run(t *testing.T) { t.Errorf("expected clusters %v, but found %v in kubeconfig", test.expectedClusters, clusters) } } - -func TestCompletionDeleteClusterNoArg(t *testing.T) { - // FIXME - t.Skip("Should not be skipeed") - - pathOptions, streams, factory := cmdtesting.PrepareConfigForCompletion(t) - configCmd := NewCmdConfig(factory, pathOptions, streams) - cmd, _, err := configCmd.Find([]string{"delete-cluster"}) - if err != nil { - t.Fatalf("unexpected error finding delete-cluster command: %v", err) - } - - comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "my") - cmdtesting.CheckCompletion(t, comps, []string{"my-cluster"}, directive, cobra.ShellCompDirectiveNoFileComp) -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_context_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_context_test.go index 2d5de6af1d49..04bbe17cbb00 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_context_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_context_test.go @@ -24,10 +24,8 @@ import ( "reflect" "testing" - "github.com/spf13/cobra" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" - cmdtesting "k8s.io/kubectl/pkg/cmd/testing" ) type deleteContextTest struct { @@ -98,18 +96,3 @@ func (test deleteContextTest) run(t *testing.T) { t.Errorf("expected contexts %v, but found %v in kubeconfig", test.expectedContexts, contexts) } } - -func TestCompletionDeleteContextNoArg(t *testing.T) { - // FIXME - t.Skip("Should not be skipeed") - - pathOptions, streams, factory := cmdtesting.PrepareConfigForCompletion(t) - configCmd := NewCmdConfig(factory, pathOptions, streams) - cmd, _, err := configCmd.Find([]string{"delete-context"}) - if err != nil { - t.Fatalf("unexpected error finding delete-context command: %v", err) - } - - comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "my") - cmdtesting.CheckCompletion(t, comps, []string{"my-context"}, directive, cobra.ShellCompDirectiveNoFileComp) -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/rename_context_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/rename_context_test.go index ed3758e45a52..cfacb90102a4 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/rename_context_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/rename_context_test.go @@ -24,10 +24,8 @@ import ( "strings" "testing" - "github.com/spf13/cobra" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" - cmdtesting "k8s.io/kubectl/pkg/cmd/testing" ) const ( @@ -156,18 +154,3 @@ func (test renameContextTest) run(t *testing.T) { } } } - -func TestCompletionRenameContextNoArg(t *testing.T) { - // FIXME - t.Skip("Should not be skipeed") - - pathOptions, streams, factory := cmdtesting.PrepareConfigForCompletion(t) - configCmd := NewCmdConfig(factory, pathOptions, streams) - cmd, _, err := configCmd.Find([]string{"rename-context"}) - if err != nil { - t.Fatalf("unexpected error finding rename-context command: %v", err) - } - - comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "my") - cmdtesting.CheckCompletion(t, comps, []string{"my-context"}, directive, cobra.ShellCompDirectiveNoFileComp) -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/use_context_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/use_context_test.go index 59488a13d4f2..74424bc51b9c 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/use_context_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/use_context_test.go @@ -22,10 +22,8 @@ import ( "os" "testing" - "github.com/spf13/cobra" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" - cmdtesting "k8s.io/kubectl/pkg/cmd/testing" ) type useContextTest struct { @@ -104,18 +102,3 @@ func (test useContextTest) run(t *testing.T) { t.Errorf("Failed in :%q\n expected config %v, but found %v\n in kubeconfig\n", test.description, test.expectedConfig, config) } } - -func TestCompletionUseContextNoArg(t *testing.T) { - // FIXME - t.Skip("Should not be skipeed") - - pathOptions, streams, factory := cmdtesting.PrepareConfigForCompletion(t) - configCmd := NewCmdConfig(factory, pathOptions, streams) - cmd, _, err := configCmd.Find([]string{"use-context"}) - if err != nil { - t.Fatalf("unexpected error finding use-context command: %v", err) - } - - comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "my") - cmdtesting.CheckCompletion(t, comps, []string{"my-context"}, directive, cobra.ShellCompDirectiveNoFileComp) -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/delete/delete_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/delete/delete_test.go index e2781d03605f..dbe94cad3e74 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/delete/delete_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/delete/delete_test.go @@ -832,18 +832,3 @@ func TestResourceErrors(t *testing.T) { }) } } - -// TODO -// func TestCompletionDeleteNoArg(t *testing.T) { -// tf, streams := cmdtesting.PreparePodsForCompletion(t) -// cmd := NewCmdDelete(tf, streams) -// comps, directive := cmd.ValidArgsFunction(cmd, []string{""}, "po") -// cmdtesting.CheckCompletionOfResources(t, comps, directive) -// } - -func TestCompletionDeleteOneArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdDelete(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/describe/describe_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/describe/describe_test.go index c942bc1073a2..b6fbfc444bac 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/describe/describe_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/describe/describe_test.go @@ -22,7 +22,6 @@ import ( "strings" "testing" - "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/api/meta" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/resource" @@ -324,13 +323,6 @@ func TestDescribeNoResourcesFound(t *testing.T) { } } -func TestCompletionDescribeOneArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdDescribe("kubectl", tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} - type testDescriber struct { Name, Namespace string Settings describe.DescriberSettings diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain_test.go index 5569cc98a221..eacef7fd300a 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain_test.go @@ -930,27 +930,6 @@ func TestDrain(t *testing.T) { } } -func TestCompletionCordonNoArg(t *testing.T) { - tf, streams := cmdtesting.PrepareNodesForCompletion(t) - cmd := NewCmdCordon(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "f") - cmdtesting.CheckCompletion(t, comps, []string{"firstnode"}, directive, cobra.ShellCompDirectiveNoFileComp) -} - -func TestCompletionDrainNoArg(t *testing.T) { - tf, streams := cmdtesting.PrepareNodesForCompletion(t) - cmd := NewCmdDrain(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "f") - cmdtesting.CheckCompletion(t, comps, []string{"firstnode"}, directive, cobra.ShellCompDirectiveNoFileComp) -} - -func TestCompletionUncordonNoArg(t *testing.T) { - tf, streams := cmdtesting.PrepareNodesForCompletion(t) - cmd := NewCmdUncordon(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "f") - cmdtesting.CheckCompletion(t, comps, []string{"firstnode"}, directive, cobra.ShellCompDirectiveNoFileComp) -} - type MyReq struct { Request *http.Request } diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/edit/edit_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/edit/edit_test.go index dd89aac8fd20..df1d07ba4a15 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/edit/edit_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/edit/edit_test.go @@ -283,13 +283,6 @@ func TestEdit(t *testing.T) { } } -func TestCompletionEditOneArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdEdit(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} - func tryIndent(data []byte) []byte { indented := &bytes.Buffer{} if err := json.Indent(indented, data, "", "\t"); err == nil { diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/exec/exec_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/exec/exec_test.go index 7bd7f4c7a2d3..814e3e87c443 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/exec/exec_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/exec/exec_test.go @@ -27,7 +27,6 @@ import ( "strings" "testing" - "github.com/spf13/cobra" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" @@ -409,10 +408,3 @@ func TestSetupTTY(t *testing.T) { t.Errorf("attach stdin, TTY, is a terminal: tty.Out should equal o.Out") } } - -func TestCompletionExecNoArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdExec(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/expose/expose_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/expose/expose_test.go index b1027d8299e6..0cb405020124 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/expose/expose_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/expose/expose_test.go @@ -21,7 +21,6 @@ import ( "strings" "testing" - "github.com/spf13/cobra" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -677,18 +676,3 @@ func TestRunExposeService(t *testing.T) { }) } } - -func TestCompletionExposeNoArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdExposeService(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "") - expectedComps := []string{"deployment", "pod", "replicaset", "replicationcontroller", "service"} - cmdtesting.CheckCompletion(t, comps, expectedComps, directive, cobra.ShellCompDirectiveNoFileComp) -} - -func TestCompletionExposeOneArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdExposeService(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/get/get_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/get/get_test.go index 6f1898b28a83..ee0e39a7b66a 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/get/get_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/get/get_test.go @@ -29,7 +29,6 @@ import ( "strings" "testing" - "github.com/spf13/cobra" appsv1 "k8s.io/api/apps/v1" autoscalingv1 "k8s.io/api/autoscaling/v1" batchv1 "k8s.io/api/batch/v1" @@ -2734,13 +2733,6 @@ foo 0/0 0 } } -func TestCompletionGetOneArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdGet("kubectl", tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} - func watchBody(codec runtime.Codec, events []watch.Event) io.ReadCloser { buf := bytes.NewBuffer([]byte{}) enc := restclientwatch.NewEncoder(streaming.NewEncoder(buf, codec), codec) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/label/label_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/label/label_test.go index b8e065c8684a..63176eedabe3 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/label/label_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/label/label_test.go @@ -24,7 +24,6 @@ import ( "strings" "testing" - "github.com/spf13/cobra" "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -566,10 +565,3 @@ func TestLabelResourceVersion(t *testing.T) { t.Fatalf("unexpected error: %v", err) } } - -func TestCompletionLabelOneArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdLabel(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/logs/logs_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/logs/logs_test.go index c40acff99bba..cb9b7cc9228b 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/logs/logs_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/logs/logs_test.go @@ -30,7 +30,6 @@ import ( "testing/iotest" "time" - "github.com/spf13/cobra" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -834,13 +833,6 @@ func TestNoResourceFoundMessage(t *testing.T) { } } -func TestCompletionLogsNoArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdLogs(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} - type responseWrapperMock struct { data io.Reader err error diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/patch/patch_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/patch/patch_test.go index 0d5ac1f6f713..1fce8bfeda11 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/patch/patch_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/patch/patch_test.go @@ -21,7 +21,6 @@ import ( "strings" "testing" - "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/resource" "k8s.io/client-go/rest/fake" @@ -191,10 +190,3 @@ func TestPatchObjectFromFileOutput(t *testing.T) { t.Errorf("unexpected output: %s", buf.String()) } } - -func TestCompletionPatchOneArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdPatch(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/portforward/portforward_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/portforward/portforward_test.go index 058f0db7be74..06b99081ac6b 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/portforward/portforward_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/portforward/portforward_test.go @@ -980,10 +980,3 @@ func TestCheckUDPPort(t *testing.T) { } } } - -func TestCompletionPortForwardNoArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdPortForward(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_history_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_history_test.go deleted file mode 100644 index 2d1c7b7d91a5..000000000000 --- a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_history_test.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2021 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rollout - -import ( - "testing" - - "github.com/spf13/cobra" - cmdtesting "k8s.io/kubectl/pkg/cmd/testing" -) - -func TestCompletionRolloutHistoryNoArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdRolloutHistory(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "") - cmdtesting.CheckCompletion(t, comps, []string{"deployment", "daemonset", "statefulset"}, directive, cobra.ShellCompDirectiveNoFileComp) -} - -func TestCompletionRolloutHistoryOneArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdRolloutHistory(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_pause_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_pause_test.go index 6f888f975679..779ce0366c4c 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_pause_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_pause_test.go @@ -22,7 +22,6 @@ import ( "net/http" "testing" - "github.com/spf13/cobra" appsv1 "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -73,20 +72,6 @@ func TestRolloutPause(t *testing.T) { } } -func TestCompletionRolloutPauseNoArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdRolloutPause(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "") - cmdtesting.CheckCompletion(t, comps, []string{"deployment"}, directive, cobra.ShellCompDirectiveNoFileComp) -} - -func TestCompletionRolloutPauseOneArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdRolloutPause(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} - type RolloutPauseRESTClient struct { *fake.RESTClient } diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_restart_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_restart_test.go deleted file mode 100644 index b2f1379f14c1..000000000000 --- a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_restart_test.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2021 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rollout - -import ( - "testing" - - "github.com/spf13/cobra" - cmdtesting "k8s.io/kubectl/pkg/cmd/testing" -) - -func TestCompletionRolloutRestartNoArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdRolloutRestart(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "") - cmdtesting.CheckCompletion(t, comps, []string{"deployment", "daemonset", "statefulset"}, directive, cobra.ShellCompDirectiveNoFileComp) -} - -func TestCompletionRolloutRestartOneArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdRolloutRestart(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_resume_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_resume_test.go deleted file mode 100644 index cf4be53cd4a3..000000000000 --- a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_resume_test.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2021 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rollout - -import ( - "testing" - - "github.com/spf13/cobra" - cmdtesting "k8s.io/kubectl/pkg/cmd/testing" -) - -func TestCompletionRolloutResumeNoArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdRolloutResume(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "") - cmdtesting.CheckCompletion(t, comps, []string{"deployment"}, directive, cobra.ShellCompDirectiveNoFileComp) -} - -func TestCompletionRolloutResumeOneArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdRolloutResume(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_status_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_status_test.go deleted file mode 100644 index bcb0eef7627a..000000000000 --- a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_status_test.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2021 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rollout - -import ( - "testing" - - "github.com/spf13/cobra" - cmdtesting "k8s.io/kubectl/pkg/cmd/testing" -) - -func TestCompletionRolloutStatusNoArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdRolloutStatus(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "") - cmdtesting.CheckCompletion(t, comps, []string{"deployment", "daemonset", "statefulset"}, directive, cobra.ShellCompDirectiveNoFileComp) -} - -func TestCompletionRolloutStatusOneArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdRolloutStatus(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_undo_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_undo_test.go deleted file mode 100644 index c28d3767734e..000000000000 --- a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_undo_test.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2021 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rollout - -import ( - "testing" - - "github.com/spf13/cobra" - cmdtesting "k8s.io/kubectl/pkg/cmd/testing" -) - -func TestCompletionRolloutUndoNoArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdRolloutUndo(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "") - cmdtesting.CheckCompletion(t, comps, []string{"deployment", "daemonset", "statefulset"}, directive, cobra.ShellCompDirectiveNoFileComp) -} - -func TestCompletionRolloutUndoOneArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdRolloutUndo(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/scale/scale_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/scale/scale_test.go deleted file mode 100644 index 53d5aca35259..000000000000 --- a/staging/src/k8s.io/kubectl/pkg/cmd/scale/scale_test.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2021 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package scale - -import ( - "testing" - - "github.com/spf13/cobra" - cmdtesting "k8s.io/kubectl/pkg/cmd/testing" -) - -func TestCompletionScaleNoArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdScale(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "") - cmdtesting.CheckCompletion(t, comps, []string{"deployment", "replicaset", "replicationcontroller", "statefulset"}, directive, cobra.ShellCompDirectiveNoFileComp) -} - -func TestCompletionScaleOneArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdScale(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/taint/taint_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/taint/taint_test.go index 200bd96e0c57..a154c97dfd87 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/taint/taint_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/taint/taint_test.go @@ -24,7 +24,6 @@ import ( "testing" "time" - "github.com/spf13/cobra" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -389,20 +388,6 @@ func TestValidateFlags(t *testing.T) { } } -func TestCompletionTaintNoArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdTaint(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "") - cmdtesting.CheckCompletion(t, comps, []string{"node"}, directive, cobra.ShellCompDirectiveNoFileComp) -} - -func TestCompletionTaintOneArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdTaint(tf, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{"pods"}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} - type MyReq struct { Request *http.Request } diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/testing/completion.go b/staging/src/k8s.io/kubectl/pkg/cmd/testing/completion.go deleted file mode 100644 index 6ca72ac89750..000000000000 --- a/staging/src/k8s.io/kubectl/pkg/cmd/testing/completion.go +++ /dev/null @@ -1,158 +0,0 @@ -/* -Copyright 2021 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package testing - -import ( - "io/ioutil" - "net/http" - "os" - "sort" - "testing" - "time" - - "github.com/spf13/cobra" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/cli-runtime/pkg/genericclioptions" - "k8s.io/cli-runtime/pkg/resource" - "k8s.io/client-go/rest/fake" - "k8s.io/client-go/tools/clientcmd" - clientcmdapi "k8s.io/client-go/tools/clientcmd/api" - cmdutil "k8s.io/kubectl/pkg/cmd/util" - "k8s.io/kubectl/pkg/scheme" -) - -// PreparePodsForCompletion prepares the test factory and streams for pods -func PreparePodsForCompletion(t *testing.T) (cmdutil.Factory, genericclioptions.IOStreams) { - pods, _, _ := TestData() - - tf := NewTestFactory().WithNamespace("test") - defer tf.Cleanup() - codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) - - tf.UnstructuredClient = &fake.RESTClient{ - NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, - Resp: &http.Response{StatusCode: http.StatusOK, Header: DefaultHeader(), Body: ObjBody(codec, pods)}, - } - - streams, _, _, _ := genericclioptions.NewTestIOStreams() - return tf, streams -} - -// PrepareNodesForCompletion prepares the test factory and streams for pods -func PrepareNodesForCompletion(t *testing.T) (cmdutil.Factory, genericclioptions.IOStreams) { - // TODO create more than one node - // nodes := &corev1.NodeList{ - // ListMeta: metav1.ListMeta{ - // ResourceVersion: "1", - // }, - // Items: []corev1.Node{ - // { - // ObjectMeta: metav1.ObjectMeta{ - // Name: "firstnode", - // CreationTimestamp: metav1.Time{Time: time.Now()}, - // }, - // Status: corev1.NodeStatus{}, - // }, - // { - // ObjectMeta: metav1.ObjectMeta{ - // Name: "secondnode", - // CreationTimestamp: metav1.Time{Time: time.Now()}, - // }, - // Status: corev1.NodeStatus{}, - // }, - // }, - // } - - nodes := &corev1.Node{ - ObjectMeta: metav1.ObjectMeta{ - Name: "firstnode", - CreationTimestamp: metav1.Time{Time: time.Now()}, - }, - Status: corev1.NodeStatus{}, - } - - tf := NewTestFactory() - defer tf.Cleanup() - - codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) - ns := scheme.Codecs.WithoutConversion() - - tf.UnstructuredClient = &fake.RESTClient{ - NegotiatedSerializer: ns, - Resp: &http.Response{StatusCode: http.StatusOK, Header: DefaultHeader(), Body: ObjBody(codec, nodes)}, - } - - streams, _, _, _ := genericclioptions.NewTestIOStreams() - return tf, streams -} - -// PrepareConfigForCompletion prepares some contexts for completion testing -func PrepareConfigForCompletion(t *testing.T) (*clientcmd.PathOptions, genericclioptions.IOStreams, cmdutil.Factory) { - conf := clientcmdapi.Config{ - Kind: "Config", - APIVersion: "v1", - Clusters: map[string]*clientcmdapi.Cluster{ - "minikube-cluster": {Server: "https://192.168.99.100:8443"}, - "my-cluster": {Server: "https://192.168.0.1:3434"}, - }, - Contexts: map[string]*clientcmdapi.Context{ - "minikube-context": {AuthInfo: "minikube", Cluster: "minikube"}, - "my-context": {AuthInfo: "my-context", Cluster: "my-context"}, - }, - CurrentContext: "minikube-context", - } - - fakeKubeFile, err := ioutil.TempFile(os.TempDir(), "") - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - defer os.Remove(fakeKubeFile.Name()) - err = clientcmd.WriteToFile(conf, fakeKubeFile.Name()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - pathOptions := clientcmd.NewDefaultPathOptions() - pathOptions.GlobalFile = fakeKubeFile.Name() - pathOptions.EnvVar = "" - - streams, _, _, _ := genericclioptions.NewTestIOStreams() - factory := cmdutil.NewFactory(genericclioptions.NewTestConfigFlags()) - - return pathOptions, streams, factory -} - -// CheckCompletion checks that the directive is correct and that each completion is present -func CheckCompletion(t *testing.T, comps, expectedComps []string, directive, expectedDirective cobra.ShellCompDirective) { - if e, d := expectedDirective, directive; e != d { - t.Errorf("expected directive\n%v\nbut got\n%v", e, d) - } - - sort.Strings(comps) - sort.Strings(expectedComps) - - if len(expectedComps) != len(comps) { - t.Fatalf("expected completions\n%v\nbut got\n%v", expectedComps, comps) - } - - for i := range comps { - if expectedComps[i] != comps[i] { - t.Errorf("expected completions\n%v\nbut got\n%v", expectedComps, comps) - break - } - } -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/top/top_node_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/top/top_node_test.go index 528b7358ed20..dca44b83f244 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/top/top_node_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/top/top_node_test.go @@ -25,7 +25,6 @@ import ( "strings" "testing" - "github.com/spf13/cobra" "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/cli-runtime/pkg/genericclioptions" @@ -425,10 +424,3 @@ func TestTopNodeWithSortByMemoryMetricsFrom(t *testing.T) { } } - -func TestCompletionTopNodeNoArg(t *testing.T) { - tf, streams := cmdtesting.PrepareNodesForCompletion(t) - cmd := NewCmdTopNode(tf, nil, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "f") - cmdtesting.CheckCompletion(t, comps, []string{"firstnode"}, directive, cobra.ShellCompDirectiveNoFileComp) -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/top/top_pod_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/top/top_pod_test.go index 7986f77bdc08..d8b575cc79fb 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/top/top_pod_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/top/top_pod_test.go @@ -26,7 +26,6 @@ import ( "testing" "time" - "github.com/spf13/cobra" "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -394,13 +393,6 @@ func TestTopPodNoResourcesFound(t *testing.T) { } } -func TestCompletionTopPodNoArg(t *testing.T) { - tf, streams := cmdtesting.PreparePodsForCompletion(t) - cmd := NewCmdTopPod(tf, nil, streams) - comps, directive := cmd.ValidArgsFunction(cmd, []string{}, "b") - cmdtesting.CheckCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) -} - func testV1beta1PodMetricsData() []metricsv1beta1api.PodMetrics { return []metricsv1beta1api.PodMetrics{ { From 46aa6045e4c383db2106307ee687abce2910153a Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Sat, 5 Jun 2021 21:11:34 -0400 Subject: [PATCH 7/8] Add tests for completion utility functions Signed-off-by: Marc Khouzam --- .../kubectl/pkg/util/completion_test.go | 188 ++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 staging/src/k8s.io/kubectl/pkg/util/completion_test.go diff --git a/staging/src/k8s.io/kubectl/pkg/util/completion_test.go b/staging/src/k8s.io/kubectl/pkg/util/completion_test.go new file mode 100644 index 000000000000..663b306720e3 --- /dev/null +++ b/staging/src/k8s.io/kubectl/pkg/util/completion_test.go @@ -0,0 +1,188 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "net/http" + "sort" + "testing" + + "github.com/spf13/cobra" + "k8s.io/cli-runtime/pkg/genericclioptions" + "k8s.io/cli-runtime/pkg/resource" + "k8s.io/client-go/rest/fake" + "k8s.io/kubectl/pkg/cmd/get" + cmdtesting "k8s.io/kubectl/pkg/cmd/testing" + + // cmdutil "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/kubectl/pkg/scheme" +) + +func TestResourceTypeAndNameCompletionFuncOneArg(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + compFunc := ResourceTypeAndNameCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{"pod"}, "b") + checkCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestResourceTypeAndNameCompletionFuncTooManyArgs(t *testing.T) { + tf := cmdtesting.NewTestFactory() + defer tf.Cleanup() + + streams, _, _, _ := genericclioptions.NewTestIOStreams() + cmd := get.NewCmdGet("kubectl", tf, streams) + compFunc := ResourceTypeAndNameCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{"pod", "pod-name"}, "") + checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestSpecifiedResourceTypeAndNameCompletionFuncNoArgs(t *testing.T) { + tf := cmdtesting.NewTestFactory() + defer tf.Cleanup() + + streams, _, _, _ := genericclioptions.NewTestIOStreams() + cmd := get.NewCmdGet("kubectl", tf, streams) + compFunc := SpecifiedResourceTypeAndNameCompletionFunc(tf, []string{"pod", "service", "statefulset"}) + comps, directive := compFunc(cmd, []string{}, "s") + checkCompletion(t, comps, []string{"service", "statefulset"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestSpecifiedResourceTypeAndNameCompletionFuncOneArg(t *testing.T) { + tf := cmdtesting.NewTestFactory().WithNamespace("test") + defer tf.Cleanup() + + pods, _, _ := cmdtesting.TestData() + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, + Resp: &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, pods)}, + } + + streams, _, _, _ := genericclioptions.NewTestIOStreams() + cmd := get.NewCmdGet("kubectl", tf, streams) + compFunc := SpecifiedResourceTypeAndNameCompletionFunc(tf, []string{"pod"}) + comps, directive := compFunc(cmd, []string{"pod"}, "b") + checkCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestSpecifiedResourceTypeAndNameCompletionFuncTooManyArgs(t *testing.T) { + tf := cmdtesting.NewTestFactory() + defer tf.Cleanup() + + streams, _, _, _ := genericclioptions.NewTestIOStreams() + cmd := get.NewCmdGet("kubectl", tf, streams) + compFunc := SpecifiedResourceTypeAndNameCompletionFunc(tf, []string{"pod"}) + comps, directive := compFunc(cmd, []string{"pod", "pod-name"}, "") + checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestResourceNameCompletionFuncNoArgs(t *testing.T) { + tf := cmdtesting.NewTestFactory().WithNamespace("test") + defer tf.Cleanup() + + pods, _, _ := cmdtesting.TestData() + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, + Resp: &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, pods)}, + } + + streams, _, _, _ := genericclioptions.NewTestIOStreams() + cmd := get.NewCmdGet("kubectl", tf, streams) + compFunc := ResourceNameCompletionFunc(tf, "pod") + comps, directive := compFunc(cmd, []string{}, "b") + checkCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestResourceNameCompletionFuncTooManyArgs(t *testing.T) { + tf := cmdtesting.NewTestFactory() + defer tf.Cleanup() + + streams, _, _, _ := genericclioptions.NewTestIOStreams() + cmd := get.NewCmdGet("kubectl", tf, streams) + compFunc := ResourceNameCompletionFunc(tf, "pod") + comps, directive := compFunc(cmd, []string{"pod-name"}, "") + checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestPodResourceNameAndContainerCompletionFuncNoArgs(t *testing.T) { + tf := cmdtesting.NewTestFactory().WithNamespace("test") + defer tf.Cleanup() + + pods, _, _ := cmdtesting.TestData() + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, + Resp: &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, pods)}, + } + + streams, _, _, _ := genericclioptions.NewTestIOStreams() + cmd := get.NewCmdGet("kubectl", tf, streams) + compFunc := PodResourceNameAndContainerCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{}, "b") + checkCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestPodResourceNameAndContainerCompletionFuncTooManyArgs(t *testing.T) { + tf := cmdtesting.NewTestFactory().WithNamespace("test") + defer tf.Cleanup() + + streams, _, _, _ := genericclioptions.NewTestIOStreams() + cmd := get.NewCmdGet("kubectl", tf, streams) + compFunc := PodResourceNameAndContainerCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{"pod-name", "container-name"}, "") + checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func prepareCompletionTest() (*cmdtesting.TestFactory, *cobra.Command) { + tf := cmdtesting.NewTestFactory().WithNamespace("test") + defer tf.Cleanup() + + streams, _, _, _ := genericclioptions.NewTestIOStreams() + cmd := get.NewCmdGet("kubectl", tf, streams) + return tf, cmd +} + +func addPodsToFactory(tf *cmdtesting.TestFactory) { + pods, _, _ := cmdtesting.TestData() + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, + Resp: &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, pods)}, + } +} + +func checkCompletion(t *testing.T, comps, expectedComps []string, directive, expectedDirective cobra.ShellCompDirective) { + if e, d := expectedDirective, directive; e != d { + t.Errorf("expected directive\n%v\nbut got\n%v", e, d) + } + + sort.Strings(comps) + sort.Strings(expectedComps) + + if len(expectedComps) != len(comps) { + t.Fatalf("expected completions\n%v\nbut got\n%v", expectedComps, comps) + } + + for i := range comps { + if expectedComps[i] != comps[i] { + t.Errorf("expected completions\n%v\nbut got\n%v", expectedComps, comps) + break + } + } +} From 77bb053102cbe1581857dd35e9c095888d2c85d3 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Sat, 5 Jun 2021 22:09:03 -0400 Subject: [PATCH 8/8] Use native zsh completion Signed-off-by: Marc Khouzam --- .../kubectl/pkg/cmd/completion/completion.go | 119 +----------------- 1 file changed, 2 insertions(+), 117 deletions(-) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/completion/completion.go b/staging/src/k8s.io/kubectl/pkg/cmd/completion/completion.go index ac7235efb01b..6399ffe20fef 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/completion/completion.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/completion/completion.go @@ -17,7 +17,6 @@ limitations under the License. package completion import ( - "bytes" "io" "github.com/spf13/cobra" @@ -150,8 +149,7 @@ func runCompletionBash(out io.Writer, boilerPlate string, kubectl *cobra.Command } func runCompletionZsh(out io.Writer, boilerPlate string, kubectl *cobra.Command) error { - zshHead := "#compdef kubectl\n" - + zshHead := "#compdef kubectl\ncompdef _kubectl kubectl\n" out.Write([]byte(zshHead)) if len(boilerPlate) == 0 { @@ -161,118 +159,5 @@ func runCompletionZsh(out io.Writer, boilerPlate string, kubectl *cobra.Command) return err } - zshInitialization := ` -__kubectl_bash_source() { - alias shopt=':' - emulate -L sh - setopt kshglob noshglob braceexpand - - source "$@" -} - -__kubectl_type() { - # -t is not supported by zsh - if [ "$1" == "-t" ]; then - shift - - # fake Bash 4 to disable "complete -o nospace". Instead - # "compopt +-o nospace" is used in the code to toggle trailing - # spaces. We don't support that, but leave trailing spaces on - # all the time - if [ "$1" = "__kubectl_compopt" ]; then - echo builtin - return 0 - fi - fi - type "$@" -} - -__kubectl_compgen() { - local completions w - completions=( $(compgen "$@") ) || return $? - - # filter by given word as prefix - while [[ "$1" = -* && "$1" != -- ]]; do - shift - shift - done - if [[ "$1" == -- ]]; then - shift - fi - for w in "${completions[@]}"; do - if [[ "${w}" = "$1"* ]]; then - echo "${w}" - fi - done -} - -__kubectl_compopt() { - true # don't do anything. Not supported by bashcompinit in zsh -} - -__kubectl_ltrim_colon_completions() -{ - if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then - # Remove colon-word prefix from COMPREPLY items - local colon_word=${1%${1##*:}} - local i=${#COMPREPLY[*]} - while [[ $((--i)) -ge 0 ]]; do - COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"} - done - fi -} - -__kubectl_get_comp_words_by_ref() { - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[${COMP_CWORD}-1]}" - words=("${COMP_WORDS[@]}") - cword=("${COMP_CWORD[@]}") -} - -__kubectl_filedir() { - # Don't need to do anything here. - # Otherwise we will get trailing space without "compopt -o nospace" - true -} - -autoload -U +X bashcompinit && bashcompinit - -# use word boundary patterns for BSD or GNU sed -LWORD='[[:<:]]' -RWORD='[[:>:]]' -if sed --version 2>&1 | grep -q GNU; then - LWORD='\<' - RWORD='\>' -fi - -__kubectl_convert_bash_to_zsh() { - sed \ - -e 's/declare -F/whence -w/' \ - -e 's/_get_comp_words_by_ref "\$@"/_get_comp_words_by_ref "\$*"/' \ - -e 's/local \([a-zA-Z0-9_]*\)=/local \1; \1=/' \ - -e 's/flags+=("\(--.*\)=")/flags+=("\1"); two_word_flags+=("\1")/' \ - -e 's/must_have_one_flag+=("\(--.*\)=")/must_have_one_flag+=("\1")/' \ - -e "s/${LWORD}_filedir${RWORD}/__kubectl_filedir/g" \ - -e "s/${LWORD}_get_comp_words_by_ref${RWORD}/__kubectl_get_comp_words_by_ref/g" \ - -e "s/${LWORD}__ltrim_colon_completions${RWORD}/__kubectl_ltrim_colon_completions/g" \ - -e "s/${LWORD}compgen${RWORD}/__kubectl_compgen/g" \ - -e "s/${LWORD}compopt${RWORD}/__kubectl_compopt/g" \ - -e "s/${LWORD}declare${RWORD}/builtin declare/g" \ - -e "s/\\\$(type${RWORD}/\$(__kubectl_type/g" \ - <<'BASH_COMPLETION_EOF' -` - out.Write([]byte(zshInitialization)) - - buf := new(bytes.Buffer) - kubectl.GenBashCompletion(buf) - out.Write(buf.Bytes()) - - zshTail := ` -BASH_COMPLETION_EOF -} - -__kubectl_bash_source <(__kubectl_convert_bash_to_zsh) -` - out.Write([]byte(zshTail)) - return nil + return kubectl.GenZshCompletion(out) }