From 879cdc5fa9e518ce76b9e1dc7e795132457966f3 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Fri, 5 Mar 2021 15:43:31 -0500 Subject: [PATCH] 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 00fe8423b6d..abb081abd65 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 b1177a119e0..b0eff26a73c 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 da7065313c5..ba677d00286 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 4327ea6aef1..939b926bafc 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 f7cc2b833c9..37c48776c9d 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 bb09cbec91e..bf08890d324 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 aa641545399..f397c334b68 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 a561c07d45b..00000000000 --- 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 74a0e217515..4930e0d43fe 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 318629cf10c..e76b3ae4d36 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 8221bba873d..8828d2f1ffc 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 3a8022538e4..52fa7773ac8 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 c87dd5f7d87..1a8678b66a1 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 8282e82497f..79d064113bc 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 e973dc9517c..7757b2c6609 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 022c9917181..234ae3b6714 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 dfd2c1e873f..7199b2b932a 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 c2a13a078e8..e81488016e6 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 d466fbc1efd..faa25f15e8e 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 1ff45f26fd5..7354f9af77e 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 008dcb32c34..a63f4a41fb8 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 4f1f5a8c4ee..3252bb14f04 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 430ac7b5ff2..faaf67e2112 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 4b6c0fc373c..ecd4994b2db 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 fe563ecbc3f..59151f7c1f8 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 71922bb9d23..d55545593e7 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 9cf3639218d..c0aaee29c18 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 2470c004927..59b53748fb1 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 0a980cf8f5d..e8cc3ed1008 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 915c779e565..c31d9ff7831 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 4abee4ba084..b6e5a0decb7 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 783ace37952..4debce3b62f 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 1607ef3ff59..8978e7c54fd 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 9430028e628..4f6c7409f98 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 2f530d78f51..2561323444c 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 5d9649e707f..eeb1981a2a6 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 bfb9f9bfe8b..00000000000 --- 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 4064ac74e0e..ebc4e11be34 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