From c4f8c57b43843abd13bae677b73c622e1c30b46f Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Thu, 24 Mar 2022 09:06:05 -0400 Subject: [PATCH] Refactor completion code into its own package Signed-off-by: Marc Khouzam --- .../kubectl/pkg/cmd/annotate/annotate.go | 4 +- .../pkg/cmd/apiresources/apiresources.go | 38 ---- .../pkg/cmd/apply/apply_edit_last_applied.go | 4 +- .../pkg/cmd/apply/apply_view_last_applied.go | 3 +- .../k8s.io/kubectl/pkg/cmd/attach/attach.go | 4 +- .../kubectl/pkg/cmd/autoscale/autoscale.go | 3 +- staging/src/k8s.io/kubectl/pkg/cmd/cmd.go | 19 +- .../kubectl/pkg/cmd/config/delete_cluster.go | 4 +- .../kubectl/pkg/cmd/config/delete_context.go | 4 +- .../kubectl/pkg/cmd/config/delete_user.go | 4 +- .../kubectl/pkg/cmd/config/rename_context.go | 4 +- .../kubectl/pkg/cmd/config/set_context.go | 4 +- .../kubectl/pkg/cmd/config/use_context.go | 4 +- staging/src/k8s.io/kubectl/pkg/cmd/cp/cp.go | 10 +- .../cmd/create/create_clusterrolebinding.go | 4 +- .../kubectl/pkg/cmd/create/create_token.go | 4 +- .../k8s.io/kubectl/pkg/cmd/delete/delete.go | 4 +- .../kubectl/pkg/cmd/describe/describe.go | 4 +- .../src/k8s.io/kubectl/pkg/cmd/drain/drain.go | 8 +- .../src/k8s.io/kubectl/pkg/cmd/edit/edit.go | 4 +- .../src/k8s.io/kubectl/pkg/cmd/exec/exec.go | 6 +- .../k8s.io/kubectl/pkg/cmd/expose/expose.go | 3 +- staging/src/k8s.io/kubectl/pkg/cmd/get/get.go | 143 +------------ .../src/k8s.io/kubectl/pkg/cmd/label/label.go | 4 +- .../src/k8s.io/kubectl/pkg/cmd/logs/logs.go | 3 +- .../src/k8s.io/kubectl/pkg/cmd/patch/patch.go | 4 +- .../pkg/cmd/portforward/portforward.go | 3 +- .../pkg/cmd/rollout/rollout_history.go | 4 +- .../kubectl/pkg/cmd/rollout/rollout_pause.go | 4 +- .../pkg/cmd/rollout/rollout_restart.go | 4 +- .../kubectl/pkg/cmd/rollout/rollout_resume.go | 4 +- .../kubectl/pkg/cmd/rollout/rollout_status.go | 4 +- .../kubectl/pkg/cmd/rollout/rollout_undo.go | 4 +- .../src/k8s.io/kubectl/pkg/cmd/scale/scale.go | 4 +- .../src/k8s.io/kubectl/pkg/cmd/taint/taint.go | 4 +- .../k8s.io/kubectl/pkg/cmd/top/top_node.go | 4 +- .../src/k8s.io/kubectl/pkg/cmd/top/top_pod.go | 4 +- .../pkg/util/{ => completion}/completion.go | 200 +++++++++++++++++- .../util/{ => completion}/completion_test.go | 2 +- vendor/modules.txt | 1 + 40 files changed, 278 insertions(+), 268 deletions(-) rename staging/src/k8s.io/kubectl/pkg/util/{ => completion}/completion.go (59%) rename staging/src/k8s.io/kubectl/pkg/util/{ => completion}/completion_test.go (99%) 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 9ebfd97dad1..9c410604d78 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/annotate/annotate.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/annotate/annotate.go @@ -38,7 +38,7 @@ import ( 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/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -135,7 +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: util.ResourceTypeAndNameCompletionFunc(f), + ValidArgsFunction: completion.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/apiresources/apiresources.go b/staging/src/k8s.io/kubectl/pkg/cmd/apiresources/apiresources.go index 94ce9d32e28..685e0260df8 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/apiresources/apiresources.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/apiresources/apiresources.go @@ -17,11 +17,8 @@ limitations under the License. package apiresources import ( - "bytes" "fmt" "io" - "io/ioutil" - "os" "sort" "strings" @@ -278,38 +275,3 @@ 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 9623b9063c0..68f4c9f3d58 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 @@ -22,7 +22,7 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/cmd/util/editor" - "k8s.io/kubectl/pkg/util" + "k8s.io/kubectl/pkg/util/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -67,7 +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: util.ResourceTypeAndNameCompletionFunc(f), + ValidArgsFunction: completion.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 c0606f40836..280bc092c34 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 @@ -26,6 +26,7 @@ import ( "k8s.io/cli-runtime/pkg/resource" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/util" + "k8s.io/kubectl/pkg/util/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" "sigs.k8s.io/yaml" @@ -77,7 +78,7 @@ func NewCmdApplyViewLastApplied(f cmdutil.Factory, ioStreams genericclioptions.I Short: i18n.T("View the latest last-applied-configuration annotations of a resource/object"), Long: applyViewLastAppliedLong, Example: applyViewLastAppliedExample, - ValidArgsFunction: util.ResourceTypeAndNameCompletionFunc(f), + ValidArgsFunction: completion.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 2c41a1c08ee..bacf3f5cbb7 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach.go @@ -35,7 +35,7 @@ import ( "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/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -105,7 +105,7 @@ func NewCmdAttach(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra Short: i18n.T("Attach to a running container"), Long: i18n.T("Attach to a process that is already running inside an existing container."), Example: attachExample, - ValidArgsFunction: util.PodResourceNameCompletionFunc(f), + ValidArgsFunction: completion.PodResourceNameCompletionFunc(f), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.Validate()) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/autoscale/autoscale.go b/staging/src/k8s.io/kubectl/pkg/cmd/autoscale/autoscale.go index 72426a7d8d6..0523ed8f704 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/autoscale/autoscale.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/autoscale/autoscale.go @@ -34,6 +34,7 @@ import ( cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/scheme" "k8s.io/kubectl/pkg/util" + "k8s.io/kubectl/pkg/util/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -107,7 +108,7 @@ func NewCmdAutoscale(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) * Short: i18n.T("Auto-scale a deployment, replica set, stateful set, or replication controller"), Long: autoscaleLong, Example: autoscaleExample, - ValidArgsFunction: util.SpecifiedResourceTypeAndNameCompletionFunc(f, validArgs), + ValidArgsFunction: completion.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 ffee88841b6..123da078d1a 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/cmd.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/cmd.go @@ -70,7 +70,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" + utilcomp "k8s.io/kubectl/pkg/util/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" "k8s.io/kubectl/pkg/util/term" @@ -319,6 +319,11 @@ func NewKubectlCommand(o KubectlOptions) *cobra.Command { proxyCmd.PreRun = func(cmd *cobra.Command, args []string) { kubeConfigFlags.WrapConfigFn = nil } + + // Avoid import cycle by setting ValidArgsFunction here instead of in NewCmdGet() + getCmd := get.NewCmdGet("kubectl", f, o.IOStreams) + getCmd.ValidArgsFunction = utilcomp.ResourceTypeAndNameCompletionFunc(f) + groups := templates.CommandGroups{ { Message: "Basic Commands (Beginner):", @@ -333,7 +338,7 @@ func NewKubectlCommand(o KubectlOptions) *cobra.Command { Message: "Basic Commands (Intermediate):", Commands: []*cobra.Command{ explain.NewCmdExplain("kubectl", f, o.IOStreams), - get.NewCmdGet("kubectl", f, o.IOStreams), + getCmd, edit.NewCmdEdit(f, o.IOStreams), delete.NewCmdDelete(f, o.IOStreams), }, @@ -404,7 +409,7 @@ func NewKubectlCommand(o KubectlOptions) *cobra.Command { templates.ActsAsRootCommand(cmds, filters, groups...) - util.SetFactoryForCompletion(f) + utilcomp.SetFactoryForCompletion(f) registerCompletionFuncForGlobalFlags(cmds, f) cmds.AddCommand(alpha) @@ -474,21 +479,21 @@ 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 + return utilcomp.CompGetResource(f, cmd, "namespace", toComplete), cobra.ShellCompDirectiveNoFileComp })) cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc( "context", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return util.ListContextsInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp + return utilcomp.ListContextsInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp })) cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc( "cluster", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return util.ListClustersInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp + return utilcomp.ListClustersInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp })) cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc( "user", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return util.ListUsersInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp + return utilcomp.ListUsersInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp })) } 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 9a5ba2dc4eb..714dcdfed37 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,7 +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/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -42,7 +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: util.ClusterCompletionFunc, + ValidArgsFunction: completion.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 8c512e35069..7cfa49a3b09 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,7 +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/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -42,7 +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: util.ContextCompletionFunc, + ValidArgsFunction: completion.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/delete_user.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_user.go index 1949ead4d73..4ca935f318e 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_user.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/delete_user.go @@ -25,7 +25,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/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -65,7 +65,7 @@ func NewCmdConfigDeleteUser(streams genericclioptions.IOStreams, configAccess cl Short: i18n.T("Delete the specified user from the kubeconfig"), Long: i18n.T("Delete the specified user from the kubeconfig."), Example: deleteUserExample, - ValidArgsFunction: util.UserCompletionFunc, + ValidArgsFunction: completion.UserCompletionFunc, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(cmd, args)) cmdutil.CheckErr(o.Validate()) 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 3d37b318d2f..b2af39c36ad 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,7 +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/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -68,7 +68,7 @@ func NewCmdConfigRenameContext(out io.Writer, configAccess clientcmd.ConfigAcces Short: renameContextShort, Long: renameContextLong, Example: renameContextExample, - ValidArgsFunction: util.ContextCompletionFunc, + ValidArgsFunction: completion.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/set_context.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/set_context.go index b5fd94fa32a..1590bb5d2e0 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/set_context.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/set_context.go @@ -27,7 +27,7 @@ import ( clientcmdapi "k8s.io/client-go/tools/clientcmd/api" cliflag "k8s.io/component-base/cli/flag" cmdutil "k8s.io/kubectl/pkg/cmd/util" - "k8s.io/kubectl/pkg/util" + "k8s.io/kubectl/pkg/util/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -62,7 +62,7 @@ func NewCmdConfigSetContext(out io.Writer, configAccess clientcmd.ConfigAccess) Short: i18n.T("Set a context entry in kubeconfig"), Long: setContextLong, Example: setContextExample, - ValidArgsFunction: util.ContextCompletionFunc, + ValidArgsFunction: completion.ContextCompletionFunc, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(options.complete(cmd)) name, exists, err := options.run() 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 78978caa929..75f41b11ebf 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,7 +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/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -53,7 +53,7 @@ func NewCmdConfigUseContext(out io.Writer, configAccess clientcmd.ConfigAccess) Aliases: []string{"use"}, Long: `Set the current-context in a kubeconfig file.`, Example: useContextExample, - ValidArgsFunction: util.ContextCompletionFunc, + ValidArgsFunction: completion.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/cp/cp.go b/staging/src/k8s.io/kubectl/pkg/cmd/cp/cp.go index 3e25c6e5371..c7454751afe 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/cp/cp.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/cp/cp.go @@ -32,8 +32,8 @@ 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/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -102,20 +102,20 @@ func NewCmdCp(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.C if len(args) == 0 { if strings.IndexAny(toComplete, "/.~") == 0 { // Looks like a path, do nothing - } else if strings.Index(toComplete, ":") != -1 { + } else if strings.Contains(toComplete, ":") { // 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) + comps = completion.CompGetFromTemplate(&template, f, namespace, cmd, []string{"pod"}, toComplete) } else { // Complete namespaces followed by a / - for _, ns := range get.CompGetResource(f, cmd, "namespace", toComplete) { + for _, ns := range completion.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) { + for _, pod := range completion.CompGetResource(f, cmd, "pod", toComplete) { comps = append(comps, fmt.Sprintf("%s:", pod)) } 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 1115f79d8f5..a95d96a0918 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,10 +29,10 @@ 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" + "k8s.io/kubectl/pkg/util/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -109,7 +109,7 @@ func NewCmdCreateClusterRoleBinding(f cmdutil.Factory, ioStreams genericclioptio 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 completion.CompGetResource(f, cmd, "clusterrole", toComplete), cobra.ShellCompDirectiveNoFileComp })) return cmd diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/create/create_token.go b/staging/src/k8s.io/kubectl/pkg/cmd/create/create_token.go index 7323e392c85..ef15b911ac8 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/create/create_token.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/create/create_token.go @@ -33,7 +33,7 @@ import ( corev1client "k8s.io/client-go/kubernetes/typed/core/v1" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/scheme" - "k8s.io/kubectl/pkg/util" + "k8s.io/kubectl/pkg/util/completion" "k8s.io/kubectl/pkg/util/templates" "k8s.io/kubectl/pkg/util/term" "k8s.io/utils/pointer" @@ -115,7 +115,7 @@ func NewCmdCreateToken(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) Short: "Request a service account token", Long: tokenLong, Example: tokenExample, - ValidArgsFunction: util.ResourceNameCompletionFunc(f, "serviceaccount"), + ValidArgsFunction: completion.ResourceNameCompletionFunc(f, "serviceaccount"), Run: func(cmd *cobra.Command, args []string) { if err := o.Complete(f, cmd, args); err != nil { cmdutil.CheckErr(err) 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 7af71a767d5..90ed11c8052 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/delete/delete.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/delete/delete.go @@ -36,7 +36,7 @@ import ( 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/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -141,7 +141,7 @@ func NewCmdDelete(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra Short: i18n.T("Delete resources by file names, stdin, resources and names, or by resources and label selector"), Long: deleteLong, Example: deleteExample, - ValidArgsFunction: util.ResourceTypeAndNameCompletionFunc(f), + ValidArgsFunction: completion.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 986ff234220..6ca80e6325c 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/describe/describe.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/describe/describe.go @@ -30,7 +30,7 @@ import ( "k8s.io/cli-runtime/pkg/resource" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/describe" - "k8s.io/kubectl/pkg/util" + "k8s.io/kubectl/pkg/util/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -107,7 +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: util.ResourceTypeAndNameCompletionFunc(f), + ValidArgsFunction: completion.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 1f32a159771..c867aa9bf42 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go @@ -34,7 +34,7 @@ import ( 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/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -69,7 +69,7 @@ func NewCmdCordon(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cob Short: i18n.T("Mark node as unschedulable"), Long: cordonLong, Example: cordonExample, - ValidArgsFunction: util.ResourceNameCompletionFunc(f, "node"), + ValidArgsFunction: completion.ResourceNameCompletionFunc(f, "node"), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.RunCordonOrUncordon(true)) @@ -98,7 +98,7 @@ func NewCmdUncordon(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *c Short: i18n.T("Mark node as schedulable"), Long: uncordonLong, Example: uncordonExample, - ValidArgsFunction: util.ResourceNameCompletionFunc(f, "node"), + ValidArgsFunction: completion.ResourceNameCompletionFunc(f, "node"), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.RunCordonOrUncordon(false)) @@ -184,7 +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: util.ResourceNameCompletionFunc(f, "node"), + ValidArgsFunction: completion.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 6b26289706e..fb4b7b6689a 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/edit/edit.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/edit/edit.go @@ -22,7 +22,7 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/cmd/util/editor" - "k8s.io/kubectl/pkg/util" + "k8s.io/kubectl/pkg/util/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -77,7 +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: util.ResourceTypeAndNameCompletionFunc(f), + ValidArgsFunction: completion.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 ac6d278125f..f3be195db98 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/exec/exec.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/exec/exec.go @@ -37,7 +37,7 @@ import ( "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/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/interrupt" "k8s.io/kubectl/pkg/util/templates" @@ -89,7 +89,7 @@ func NewCmdExec(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.C Short: i18n.T("Execute a command in a container"), Long: i18n.T("Execute a command in a container."), Example: execExample, - ValidArgsFunction: util.PodResourceNameCompletionFunc(f), + ValidArgsFunction: completion.PodResourceNameCompletionFunc(f), Run: func(cmd *cobra.Command, args []string) { argsLenAtDash := cmd.ArgsLenAtDash() cmdutil.CheckErr(options.Complete(f, cmd, args, argsLenAtDash)) @@ -101,7 +101,7 @@ func NewCmdExec(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.C cmdutil.AddJsonFilenameFlag(cmd.Flags(), &options.FilenameOptions.Filenames, "to use to exec into the resource") // TODO support UID cmdutil.AddContainerVarFlags(cmd, &options.ContainerName, options.ContainerName) - cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc("container", util.ContainerCompletionFunc(f))) + cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc("container", completion.ContainerCompletionFunc(f))) cmd.Flags().BoolVarP(&options.Stdin, "stdin", "i", options.Stdin, "Pass stdin to the container") cmd.Flags().BoolVarP(&options.TTY, "tty", "t", options.TTY, "Stdin is a TTY") 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 312c6efd78c..c69b72bbdf8 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/expose/expose.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/expose/expose.go @@ -38,6 +38,7 @@ import ( "k8s.io/kubectl/pkg/polymorphichelpers" "k8s.io/kubectl/pkg/scheme" "k8s.io/kubectl/pkg/util" + "k8s.io/kubectl/pkg/util/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -137,7 +138,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: util.SpecifiedResourceTypeAndNameCompletionFunc(f, validArgs), + ValidArgsFunction: completion.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/get/get.go b/staging/src/k8s.io/kubectl/pkg/cmd/get/get.go index 6a2b31ceeeb..7c0928d0f24 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/get/get.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/get/get.go @@ -17,14 +17,11 @@ limitations under the License. package get import ( - "bytes" "context" "encoding/json" "fmt" "io" - "io/ioutil" "net/url" - "os" "strings" "github.com/spf13/cobra" @@ -45,7 +42,6 @@ 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" @@ -163,7 +159,7 @@ func NewCmdGet(parent string, f cmdutil.Factory, streams genericclioptions.IOStr Short: i18n.T("Display one or many resources"), Long: getLong + "\n\n" + cmdutil.SuggestAPIResources(parent), Example: getExample, - ValidArgsFunction: ResourceTypeAndNameCompletionFunc(f, nil, true), + // ValidArgsFunction is set when this function is called so that we have access to the util package Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.Validate(cmd)) @@ -851,140 +847,3 @@ 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 -} - -// ResourceTypeAndNameCompletionFunc Returns a completion function that completes resource types -// and resource names that match the toComplete prefix. It supports the / form. -func ResourceTypeAndNameCompletionFunc(f cmdutil.Factory, allowedTypes []string, allowRepeat bool) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { - return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - directive := cobra.ShellCompDirectiveNoFileComp - - if len(args) > 0 && !strings.Contains(args[0], "/") { - // The first argument is of the form (e.g., pods) - // All following arguments should be a resource name. - if allowRepeat || len(args) == 1 { - comps = CompGetResource(f, cmd, args[0], toComplete) - - // Remove choices already on the command-line - if len(args) > 1 { - comps = cmdutil.Difference(comps, args[1:]) - } - } - } else { - slashIdx := strings.Index(toComplete, "/") - if slashIdx == -1 { - if len(args) == 0 { - // We are completing the first argument. We default to the normal - // form (not the form /). - // So we suggest resource types and let the shell add a space after - // the completion. - if len(allowedTypes) == 0 { - comps = apiresources.CompGetResourceList(f, cmd, toComplete) - } else { - for _, c := range allowedTypes { - if strings.HasPrefix(c, toComplete) { - comps = append(comps, c) - } - } - } - } else { - // Here we know the first argument contains a / (/). - // All other arguments must also use that form. - if allowRepeat { - // Since toComplete does not already contain a / we know we are completing a - // resource type. Disable adding a space after the completion, and add the / - directive |= cobra.ShellCompDirectiveNoSpace - - if len(allowedTypes) == 0 { - typeComps := apiresources.CompGetResourceList(f, cmd, toComplete) - for _, c := range typeComps { - comps = append(comps, fmt.Sprintf("%s/", c)) - } - } else { - for _, c := range allowedTypes { - if strings.HasPrefix(c, toComplete) { - comps = append(comps, fmt.Sprintf("%s/", c)) - } - } - } - } - } - } else { - // We are completing an argument of the form / - // and since the / is already present, we are completing the resource name. - if allowRepeat || len(args) == 0 { - resourceType := toComplete[:slashIdx] - toComplete = toComplete[slashIdx+1:] - nameComps := CompGetResource(f, cmd, resourceType, toComplete) - for _, c := range nameComps { - comps = append(comps, fmt.Sprintf("%s/%s", resourceType, c)) - } - - // Remove choices already on the command-line. - if len(args) > 0 { - comps = cmdutil.Difference(comps, args[0:]) - } - } - } - } - return comps, directive - } -} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/label/label.go b/staging/src/k8s.io/kubectl/pkg/cmd/label/label.go index c1c65292560..bf9c12c38af 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/label/label.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/label/label.go @@ -39,7 +39,7 @@ import ( "k8s.io/cli-runtime/pkg/resource" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/scheme" - "k8s.io/kubectl/pkg/util" + "k8s.io/kubectl/pkg/util/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -139,7 +139,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: util.ResourceTypeAndNameCompletionFunc(f), + ValidArgsFunction: completion.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 d031888a6fa..bd901a9c072 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/logs/logs.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/logs/logs.go @@ -37,6 +37,7 @@ import ( "k8s.io/kubectl/pkg/polymorphichelpers" "k8s.io/kubectl/pkg/scheme" "k8s.io/kubectl/pkg/util" + "k8s.io/kubectl/pkg/util/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -152,7 +153,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: util.PodResourceNameAndContainerCompletionFunc(f), + ValidArgsFunction: completion.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 b39404961a4..8c2275f7a9d 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/patch/patch.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/patch/patch.go @@ -39,7 +39,7 @@ import ( "k8s.io/cli-runtime/pkg/resource" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/scheme" - "k8s.io/kubectl/pkg/util" + "k8s.io/kubectl/pkg/util/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -115,7 +115,7 @@ func NewCmdPatch(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobr Short: i18n.T("Update fields of a resource"), Long: patchLong, Example: patchExample, - ValidArgsFunction: util.ResourceTypeAndNameCompletionFunc(f), + ValidArgsFunction: completion.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 4f7c771ef88..baff6b5b469 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/portforward/portforward.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/portforward/portforward.go @@ -41,6 +41,7 @@ import ( cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/polymorphichelpers" "k8s.io/kubectl/pkg/util" + "k8s.io/kubectl/pkg/util/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -109,7 +110,7 @@ func NewCmdPortForward(f cmdutil.Factory, streams genericclioptions.IOStreams) * Short: i18n.T("Forward one or more local ports to a pod"), Long: portforwardLong, Example: portforwardExample, - ValidArgsFunction: util.PodResourceNameCompletionFunc(f), + ValidArgsFunction: completion.PodResourceNameCompletionFunc(f), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(opts.Complete(f, cmd, args)) cmdutil.CheckErr(opts.Validate()) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_history.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_history.go index c9f45994799..7e4e0664c56 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 @@ -27,7 +27,7 @@ import ( 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/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -84,7 +84,7 @@ func NewCmdRolloutHistory(f cmdutil.Factory, streams genericclioptions.IOStreams Short: i18n.T("View rollout history"), Long: historyLong, Example: historyExample, - ValidArgsFunction: util.SpecifiedResourceTypeAndNameCompletionFunc(f, validArgs), + ValidArgsFunction: completion.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 fdac5c46727..dea64a27062 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 @@ -30,7 +30,7 @@ import ( 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/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -84,7 +84,7 @@ func NewCmdRolloutPause(f cmdutil.Factory, streams genericclioptions.IOStreams) Short: i18n.T("Mark the provided resource as paused"), Long: pauseLong, Example: pauseExample, - ValidArgsFunction: util.SpecifiedResourceTypeAndNameCompletionFunc(f, validArgs), + ValidArgsFunction: completion.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 d46bddbcd9b..a7cc5bc762b 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 @@ -29,7 +29,7 @@ import ( 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/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -91,7 +91,7 @@ func NewCmdRolloutRestart(f cmdutil.Factory, streams genericclioptions.IOStreams Short: i18n.T("Restart a resource"), Long: restartLong, Example: restartExample, - ValidArgsFunction: util.SpecifiedResourceTypeAndNameCompletionFunc(f, validArgs), + ValidArgsFunction: completion.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 c5564e019aa..ed90424d017 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 @@ -30,7 +30,7 @@ import ( 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/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -88,7 +88,7 @@ func NewCmdRolloutResume(f cmdutil.Factory, streams genericclioptions.IOStreams) Short: i18n.T("Resume a paused resource"), Long: resumeLong, Example: resumeExample, - ValidArgsFunction: util.SpecifiedResourceTypeAndNameCompletionFunc(f, validArgs), + ValidArgsFunction: completion.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 adeafa9a3d4..59bf6ea22b4 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 @@ -37,7 +37,7 @@ import ( 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/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/interrupt" "k8s.io/kubectl/pkg/util/templates" @@ -103,7 +103,7 @@ func NewCmdRolloutStatus(f cmdutil.Factory, streams genericclioptions.IOStreams) Short: i18n.T("Show the status of the rollout"), Long: statusLong, Example: statusExample, - ValidArgsFunction: util.SpecifiedResourceTypeAndNameNoRepeatCompletionFunc(f, validArgs), + ValidArgsFunction: completion.SpecifiedResourceTypeAndNameNoRepeatCompletionFunc(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 c12c55dd59d..5723e1f7d24 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 @@ -27,7 +27,7 @@ import ( 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/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -88,7 +88,7 @@ func NewCmdRolloutUndo(f cmdutil.Factory, streams genericclioptions.IOStreams) * Short: i18n.T("Undo a previous rollout"), Long: undoLong, Example: undoExample, - ValidArgsFunction: util.SpecifiedResourceTypeAndNameCompletionFunc(f, validArgs), + ValidArgsFunction: completion.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 aedfd897155..9d9696e7d34 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/scale/scale.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/scale/scale.go @@ -31,7 +31,7 @@ import ( "k8s.io/client-go/kubernetes" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/scale" - "k8s.io/kubectl/pkg/util" + "k8s.io/kubectl/pkg/util/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -114,7 +114,7 @@ func NewCmdScale(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobr Short: i18n.T("Set a new size for a deployment, replica set, or replication controller"), Long: scaleLong, Example: scaleExample, - ValidArgsFunction: util.SpecifiedResourceTypeAndNameCompletionFunc(f, validArgs), + ValidArgsFunction: completion.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 f8273ac3d39..144a3e26130 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/taint/taint.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/taint/taint.go @@ -24,7 +24,6 @@ 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" @@ -38,6 +37,7 @@ import ( "k8s.io/cli-runtime/pkg/resource" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/scheme" + "k8s.io/kubectl/pkg/util/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) @@ -109,7 +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: util.SpecifiedResourceTypeAndNameCompletionFunc(f, validArgs), + ValidArgsFunction: completion.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 f25596d90ca..8981207ae94 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 @@ -29,7 +29,7 @@ import ( corev1client "k8s.io/client-go/kubernetes/typed/core/v1" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/metricsutil" - "k8s.io/kubectl/pkg/util" + "k8s.io/kubectl/pkg/util/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" metricsapi "k8s.io/metrics/pkg/apis/metrics" @@ -82,7 +82,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: util.ResourceNameCompletionFunc(f, "node"), + ValidArgsFunction: completion.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 6de5424ac0c..4bc2934a5d0 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 @@ -30,7 +30,7 @@ import ( corev1client "k8s.io/client-go/kubernetes/typed/core/v1" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/metricsutil" - "k8s.io/kubectl/pkg/util" + "k8s.io/kubectl/pkg/util/completion" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" metricsapi "k8s.io/metrics/pkg/apis/metrics" @@ -100,7 +100,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: util.ResourceNameCompletionFunc(f, "pod"), + ValidArgsFunction: completion.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/completion.go similarity index 59% rename from staging/src/k8s.io/kubectl/pkg/util/completion.go rename to staging/src/k8s.io/kubectl/pkg/util/completion/completion.go index f9c0e628b83..6110b50a077 100644 --- a/staging/src/k8s.io/kubectl/pkg/util/completion.go +++ b/staging/src/k8s.io/kubectl/pkg/util/completion/completion.go @@ -14,14 +14,21 @@ See the License for the specific language governing permissions and limitations under the License. */ -package util +package completion import ( + "bytes" "fmt" + "io/ioutil" + "os" "strings" "time" "github.com/spf13/cobra" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/cli-runtime/pkg/genericclioptions" + "k8s.io/cli-runtime/pkg/printers" + "k8s.io/kubectl/pkg/cmd/apiresources" "k8s.io/kubectl/pkg/cmd/get" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/polymorphichelpers" @@ -30,7 +37,7 @@ import ( var factory cmdutil.Factory -// SetFactoryForCompletion Store the factory which is needed by the completion functions +// 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 @@ -39,22 +46,21 @@ func SetFactoryForCompletion(f cmdutil.Factory) { // ResourceTypeAndNameCompletionFunc Returns a completion function that completes resource types // and resource names that match the toComplete prefix. It supports the / form. func ResourceTypeAndNameCompletionFunc(f cmdutil.Factory) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { - // The logic is in the 'get' package because the 'get' command must be able to call it also - return get.ResourceTypeAndNameCompletionFunc(f, nil, true) + return resourceTypeAndNameCompletionFunc(f, nil, true) } // SpecifiedResourceTypeAndNameCompletionFunc Returns a completion function that completes resource // types limited to the specified allowedTypes, and resource names that match the toComplete prefix. // It allows for multiple resources. It supports the / form. func SpecifiedResourceTypeAndNameCompletionFunc(f cmdutil.Factory, allowedTypes []string) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { - return get.ResourceTypeAndNameCompletionFunc(f, allowedTypes, true) + return resourceTypeAndNameCompletionFunc(f, allowedTypes, true) } // SpecifiedResourceTypeAndNameNoRepeatCompletionFunc Returns a completion function that completes resource // types limited to the specified allowedTypes, and resource names that match the toComplete prefix. // It only allows for one resource. It supports the / form. func SpecifiedResourceTypeAndNameNoRepeatCompletionFunc(f cmdutil.Factory, allowedTypes []string) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { - return get.ResourceTypeAndNameCompletionFunc(f, allowedTypes, false) + return resourceTypeAndNameCompletionFunc(f, allowedTypes, false) } // ResourceNameCompletionFunc Returns a completion function that completes as a first argument @@ -66,7 +72,7 @@ func ResourceNameCompletionFunc(f cmdutil.Factory, resourceType string) func(*co 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) + comps = CompGetResource(f, cmd, resourceType, toComplete) } return comps, cobra.ShellCompDirectiveNoFileComp } @@ -98,7 +104,7 @@ func PodResourceNameAndContainerCompletionFunc(f cmdutil.Factory) func(*cobra.Co comps, directive = doPodResourceCompletion(f, cmd, toComplete) } else if len(args) == 1 { podName := convertResourceNameToPodName(f, args[0]) - comps = get.CompGetContainers(f, cmd, podName, toComplete) + comps = CompGetContainers(f, cmd, podName, toComplete) } return comps, directive } @@ -114,7 +120,7 @@ func ContainerCompletionFunc(f cmdutil.Factory) func(*cobra.Command, []string, s // That first argument can also be of the form / so we need to convert it. if len(args) > 0 { podName := convertResourceNameToPodName(f, args[0]) - comps = get.CompGetContainers(f, cmd, podName, toComplete) + comps = CompGetContainers(f, cmd, podName, toComplete) } return comps, cobra.ShellCompDirectiveNoFileComp } @@ -147,6 +153,64 @@ func UserCompletionFunc(cmd *cobra.Command, args []string, toComplete string) ([ return nil, cobra.ShellCompDirectiveNoFileComp } +// 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 := get.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 +} + // ListContextsInConfig returns a list of context names which begin with `toComplete` func ListContextsInConfig(toComplete string) []string { config, err := factory.ToRawKubeConfigLoader().RawConfig() @@ -192,6 +256,120 @@ func ListUsersInConfig(toComplete string) []string { return ret } +// 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 := apiresources.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 +} + +// resourceTypeAndNameCompletionFunc Returns a completion function that completes resource types +// and resource names that match the toComplete prefix. It supports the / form. +func resourceTypeAndNameCompletionFunc(f cmdutil.Factory, allowedTypes []string, allowRepeat bool) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { + return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + directive := cobra.ShellCompDirectiveNoFileComp + + if len(args) > 0 && !strings.Contains(args[0], "/") { + // The first argument is of the form (e.g., pods) + // All following arguments should be a resource name. + if allowRepeat || len(args) == 1 { + comps = CompGetResource(f, cmd, args[0], toComplete) + + // Remove choices already on the command-line + if len(args) > 1 { + comps = cmdutil.Difference(comps, args[1:]) + } + } + } else { + slashIdx := strings.Index(toComplete, "/") + if slashIdx == -1 { + if len(args) == 0 { + // We are completing the first argument. We default to the normal + // form (not the form /). + // So we suggest resource types and let the shell add a space after + // the completion. + if len(allowedTypes) == 0 { + comps = compGetResourceList(f, cmd, toComplete) + } else { + for _, c := range allowedTypes { + if strings.HasPrefix(c, toComplete) { + comps = append(comps, c) + } + } + } + } else { + // Here we know the first argument contains a / (/). + // All other arguments must also use that form. + if allowRepeat { + // Since toComplete does not already contain a / we know we are completing a + // resource type. Disable adding a space after the completion, and add the / + directive |= cobra.ShellCompDirectiveNoSpace + + if len(allowedTypes) == 0 { + typeComps := compGetResourceList(f, cmd, toComplete) + for _, c := range typeComps { + comps = append(comps, fmt.Sprintf("%s/", c)) + } + } else { + for _, c := range allowedTypes { + if strings.HasPrefix(c, toComplete) { + comps = append(comps, fmt.Sprintf("%s/", c)) + } + } + } + } + } + } else { + // We are completing an argument of the form / + // and since the / is already present, we are completing the resource name. + if allowRepeat || len(args) == 0 { + resourceType := toComplete[:slashIdx] + toComplete = toComplete[slashIdx+1:] + nameComps := CompGetResource(f, cmd, resourceType, toComplete) + for _, c := range nameComps { + comps = append(comps, fmt.Sprintf("%s/%s", resourceType, c)) + } + + // Remove choices already on the command-line. + if len(args) > 0 { + comps = cmdutil.Difference(comps, args[0:]) + } + } + } + } + return comps, directive + } +} + // doPodResourceCompletion Returns completions of: // 1- pod names that match the toComplete prefix // 2- resource types containing pods which match the toComplete prefix @@ -201,7 +379,7 @@ func doPodResourceCompletion(f cmdutil.Factory, cmd *cobra.Command, toComplete s slashIdx := strings.Index(toComplete, "/") if slashIdx == -1 { // Standard case, complete pod names - comps = get.CompGetResource(f, cmd, "pod", toComplete) + comps = CompGetResource(f, cmd, "pod", toComplete) // Also include resource choices for the / form, // but only for resources that contain pods @@ -230,7 +408,7 @@ func doPodResourceCompletion(f cmdutil.Factory, cmd *cobra.Command, toComplete s // Dealing with the / form, use the specified resource type resourceType := toComplete[:slashIdx] toComplete = toComplete[slashIdx+1:] - nameComps := get.CompGetResource(f, cmd, resourceType, toComplete) + nameComps := CompGetResource(f, cmd, resourceType, toComplete) for _, c := range nameComps { comps = append(comps, fmt.Sprintf("%s/%s", resourceType, c)) } diff --git a/staging/src/k8s.io/kubectl/pkg/util/completion_test.go b/staging/src/k8s.io/kubectl/pkg/util/completion/completion_test.go similarity index 99% rename from staging/src/k8s.io/kubectl/pkg/util/completion_test.go rename to staging/src/k8s.io/kubectl/pkg/util/completion/completion_test.go index aff36504110..8a20c363e9b 100644 --- a/staging/src/k8s.io/kubectl/pkg/util/completion_test.go +++ b/staging/src/k8s.io/kubectl/pkg/util/completion/completion_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package util +package completion import ( "net/http" diff --git a/vendor/modules.txt b/vendor/modules.txt index 6e25261a9d2..dda0ceca769 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -2150,6 +2150,7 @@ k8s.io/kubectl/pkg/scale k8s.io/kubectl/pkg/scheme k8s.io/kubectl/pkg/util k8s.io/kubectl/pkg/util/certificate +k8s.io/kubectl/pkg/util/completion k8s.io/kubectl/pkg/util/deployment k8s.io/kubectl/pkg/util/event k8s.io/kubectl/pkg/util/fieldpath