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 5a099c7a480..6849cd63707 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 64b0959d33e..30983fe3cff 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 48975f6847c..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.ResourceNameCompletionFunc(f, "pod"), + 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 ddc79f00ba5..75ddf863a27 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" ) @@ -80,7 +80,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.Validate()) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/exec/exec.go b/staging/src/k8s.io/kubectl/pkg/cmd/exec/exec.go index 436e547d380..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.ResourceNameCompletionFunc(f, "pod"), + ValidArgsFunction: completion.PodResourceNameCompletionFunc(f), Run: func(cmd *cobra.Command, args []string) { argsLenAtDash := cmd.ArgsLenAtDash() cmdutil.CheckErr(options.Complete(f, cmd, args, argsLenAtDash)) @@ -101,6 +101,8 @@ func NewCmdExec(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.C cmdutil.AddJsonFilenameFlag(cmd.Flags(), &options.FilenameOptions.Filenames, "to use to exec into the resource") // TODO support UID cmdutil.AddContainerVarFlags(cmd, &options.ContainerName, options.ContainerName) + cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc("container", 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") cmd.Flags().BoolVarP(&options.Quiet, "quiet", "q", options.Quiet, "Only print output from the remote session") diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/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 17e0801c34b..82debee32e2 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" @@ -170,18 +166,7 @@ func NewCmdGet(parent string, f cmdutil.Factory, streams genericclioptions.IOStr Short: i18n.T("Display one or many resources"), Long: getLong + "\n\n" + cmdutil.SuggestAPIResources(parent), Example: getExample, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - comps = apiresources.CompGetResourceList(f, cmd, toComplete) - } else { - comps = CompGetResource(f, cmd, args[0], toComplete) - if len(args) > 1 { - comps = cmdutil.Difference(comps, args[1:]) - } - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, + // ValidArgsFunction 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)) @@ -874,61 +859,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 -} 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 165965d7de3..fa4177958e7 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/slice" "k8s.io/kubectl/pkg/util/templates" @@ -122,7 +122,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 6c9fdba5c86..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.ResourceNameCompletionFunc(f, "pod"), + 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.go deleted file mode 100644 index 5ccd16306a6..00000000000 --- a/staging/src/k8s.io/kubectl/pkg/util/completion.go +++ /dev/null @@ -1,186 +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 util - -import ( - "strings" - - "github.com/spf13/cobra" - "k8s.io/kubectl/pkg/cmd/apiresources" - "k8s.io/kubectl/pkg/cmd/get" - cmdutil "k8s.io/kubectl/pkg/cmd/util" -) - -var factory cmdutil.Factory - -// SetFactoryForCompletion Store the factory which is needed by the completion functions -// Not all commands have access to the factory, so cannot pass it to the completion functions. -func SetFactoryForCompletion(f cmdutil.Factory) { - factory = f -} - -// ResourceTypeAndNameCompletionFunc Returns a completion function that completes as a first argument -// the resource types that match the toComplete prefix, and all following arguments as resource names that match -// the toComplete prefix. -func ResourceTypeAndNameCompletionFunc(f cmdutil.Factory) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { - return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - comps = apiresources.CompGetResourceList(f, cmd, toComplete) - } else { - comps = get.CompGetResource(f, cmd, args[0], toComplete) - if len(args) > 1 { - comps = cmdutil.Difference(comps, args[1:]) - } - } - return comps, cobra.ShellCompDirectiveNoFileComp - } -} - -// SpecifiedResourceTypeAndNameCompletionFunc Returns a completion function that completes as a first -// argument the resource types that match the toComplete prefix and are limited to the allowedTypes, -// and all following arguments as resource names that match the toComplete prefix. -func SpecifiedResourceTypeAndNameCompletionFunc(f cmdutil.Factory, allowedTypes []string) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { - return doSpecifiedResourceTypeAndNameComp(f, allowedTypes, true) -} - -// SpecifiedResourceTypeAndNameNoRepeatCompletionFunc Returns a completion function that completes as a first -// argument the resource types that match the toComplete prefix and are limited to the allowedTypes, and as -// a second argument a resource name that match the toComplete prefix. -func SpecifiedResourceTypeAndNameNoRepeatCompletionFunc(f cmdutil.Factory, allowedTypes []string) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { - return doSpecifiedResourceTypeAndNameComp(f, allowedTypes, false) -} - -func doSpecifiedResourceTypeAndNameComp(f cmdutil.Factory, allowedTypes []string, repeat bool) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { - return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - for _, comp := range allowedTypes { - if strings.HasPrefix(comp, toComplete) { - comps = append(comps, comp) - } - } - } else { - if repeat || len(args) == 1 { - comps = get.CompGetResource(f, cmd, args[0], toComplete) - if repeat && len(args) > 1 { - comps = cmdutil.Difference(comps, args[1:]) - } - } - } - return comps, cobra.ShellCompDirectiveNoFileComp - } -} - -// ResourceNameCompletionFunc Returns a completion function that completes as a first argument -// the resource names specified by the resourceType parameter, and which match the toComplete prefix. -func ResourceNameCompletionFunc(f cmdutil.Factory, resourceType string) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { - return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - comps = get.CompGetResource(f, cmd, resourceType, toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - } -} - -// PodResourceNameAndContainerCompletionFunc Returns a completion function that completes as a first -// argument pod names that match the toComplete prefix, and as a second argument the containers -// within the specified pod. -func PodResourceNameAndContainerCompletionFunc(f cmdutil.Factory) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { - return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - comps = get.CompGetResource(f, cmd, "pod", toComplete) - } else if len(args) == 1 { - comps = get.CompGetContainers(f, cmd, args[0], toComplete) - } - return comps, cobra.ShellCompDirectiveNoFileComp - } -} - -// ContextCompletionFunc is a completion function that completes as a first argument the -// context names that match the toComplete prefix -func ContextCompletionFunc(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) == 0 { - return ListContextsInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp - } - return nil, cobra.ShellCompDirectiveNoFileComp -} - -// ClusterCompletionFunc is a completion function that completes as a first argument the -// cluster names that match the toComplete prefix -func ClusterCompletionFunc(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) == 0 { - return ListClustersInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp - } - return nil, cobra.ShellCompDirectiveNoFileComp -} - -// UserCompletionFunc is a completion function that completes as a first argument the -// user names that match the toComplete prefix -func UserCompletionFunc(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) == 0 { - return ListUsersInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp - } - return nil, cobra.ShellCompDirectiveNoFileComp -} - -// ListContextsInConfig returns a list of context names which begin with `toComplete` -func ListContextsInConfig(toComplete string) []string { - config, err := factory.ToRawKubeConfigLoader().RawConfig() - if err != nil { - return nil - } - var ret []string - for name := range config.Contexts { - if strings.HasPrefix(name, toComplete) { - ret = append(ret, name) - } - } - return ret -} - -// ListClustersInConfig returns a list of cluster names which begin with `toComplete` -func ListClustersInConfig(toComplete string) []string { - config, err := factory.ToRawKubeConfigLoader().RawConfig() - if err != nil { - return nil - } - var ret []string - for name := range config.Clusters { - if strings.HasPrefix(name, toComplete) { - ret = append(ret, name) - } - } - return ret -} - -// ListUsersInConfig returns a list of user names which begin with `toComplete` -func ListUsersInConfig(toComplete string) []string { - config, err := factory.ToRawKubeConfigLoader().RawConfig() - if err != nil { - return nil - } - var ret []string - for name := range config.AuthInfos { - if strings.HasPrefix(name, toComplete) { - ret = append(ret, name) - } - } - return ret -} diff --git a/staging/src/k8s.io/kubectl/pkg/util/completion/completion.go b/staging/src/k8s.io/kubectl/pkg/util/completion/completion.go new file mode 100644 index 00000000000..6110b50a077 --- /dev/null +++ b/staging/src/k8s.io/kubectl/pkg/util/completion/completion.go @@ -0,0 +1,453 @@ +/* +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 ( + "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" + "k8s.io/kubectl/pkg/scheme" +) + +var factory cmdutil.Factory + +// SetFactoryForCompletion Store the factory which is needed by the completion functions. +// Not all commands have access to the factory, so cannot pass it to the completion functions. +func SetFactoryForCompletion(f cmdutil.Factory) { + factory = f +} + +// ResourceTypeAndNameCompletionFunc Returns a completion function that completes resource types +// and resource names that match the toComplete prefix. It supports the / form. +func ResourceTypeAndNameCompletionFunc(f cmdutil.Factory) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { + return 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 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 resourceTypeAndNameCompletionFunc(f, allowedTypes, false) +} + +// ResourceNameCompletionFunc Returns a completion function that completes as a first argument +// the resource names specified by the resourceType parameter, and which match the toComplete prefix. +// This function does NOT support the / form: it is meant to be used by commands +// that don't support that form. For commands that apply to pods and that support the / +// form, please use PodResourceNameCompletionFunc() +func ResourceNameCompletionFunc(f cmdutil.Factory, resourceType string) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { + return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + if len(args) == 0 { + comps = CompGetResource(f, cmd, resourceType, toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + } +} + +// PodResourceNameCompletionFunc Returns a completion function that completes: +// 1- pod names that match the toComplete prefix +// 2- resource types containing pods which match the toComplete prefix +func PodResourceNameCompletionFunc(f cmdutil.Factory) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { + return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + directive := cobra.ShellCompDirectiveNoFileComp + if len(args) == 0 { + comps, directive = doPodResourceCompletion(f, cmd, toComplete) + } + return comps, directive + } +} + +// PodResourceNameAndContainerCompletionFunc Returns a completion function that completes, as a first argument: +// 1- pod names that match the toComplete prefix +// 2- resource types containing pods which match the toComplete prefix +// and as a second argument the containers within the specified pod. +func PodResourceNameAndContainerCompletionFunc(f cmdutil.Factory) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { + return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + directive := cobra.ShellCompDirectiveNoFileComp + if len(args) == 0 { + comps, directive = doPodResourceCompletion(f, cmd, toComplete) + } else if len(args) == 1 { + podName := convertResourceNameToPodName(f, args[0]) + comps = CompGetContainers(f, cmd, podName, toComplete) + } + return comps, directive + } +} + +// ContainerCompletionFunc Returns a completion function that completes the containers within the +// pod specified by the first argument. The resource containing the pod can be specified in +// the / form. +func ContainerCompletionFunc(f cmdutil.Factory) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { + return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + // We need the pod name to be able to complete the container names, it must be in args[0]. + // That first argument can also be of the form / so we need to convert it. + if len(args) > 0 { + podName := convertResourceNameToPodName(f, args[0]) + comps = CompGetContainers(f, cmd, podName, toComplete) + } + return comps, cobra.ShellCompDirectiveNoFileComp + } +} + +// ContextCompletionFunc is a completion function that completes as a first argument the +// context names that match the toComplete prefix +func ContextCompletionFunc(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) == 0 { + return ListContextsInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp + } + return nil, cobra.ShellCompDirectiveNoFileComp +} + +// ClusterCompletionFunc is a completion function that completes as a first argument the +// cluster names that match the toComplete prefix +func ClusterCompletionFunc(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) == 0 { + return ListClustersInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp + } + return nil, cobra.ShellCompDirectiveNoFileComp +} + +// UserCompletionFunc is a completion function that completes as a first argument the +// user names that match the toComplete prefix +func UserCompletionFunc(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) == 0 { + return ListUsersInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp + } + 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() + if err != nil { + return nil + } + var ret []string + for name := range config.Contexts { + if strings.HasPrefix(name, toComplete) { + ret = append(ret, name) + } + } + return ret +} + +// ListClustersInConfig returns a list of cluster names which begin with `toComplete` +func ListClustersInConfig(toComplete string) []string { + config, err := factory.ToRawKubeConfigLoader().RawConfig() + if err != nil { + return nil + } + var ret []string + for name := range config.Clusters { + if strings.HasPrefix(name, toComplete) { + ret = append(ret, name) + } + } + return ret +} + +// ListUsersInConfig returns a list of user names which begin with `toComplete` +func ListUsersInConfig(toComplete string) []string { + config, err := factory.ToRawKubeConfigLoader().RawConfig() + if err != nil { + return nil + } + var ret []string + for name := range config.AuthInfos { + if strings.HasPrefix(name, toComplete) { + ret = append(ret, name) + } + } + return ret +} + +// 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 +func doPodResourceCompletion(f cmdutil.Factory, cmd *cobra.Command, toComplete string) ([]string, cobra.ShellCompDirective) { + var comps []string + directive := cobra.ShellCompDirectiveNoFileComp + slashIdx := strings.Index(toComplete, "/") + if slashIdx == -1 { + // Standard case, complete pod names + comps = CompGetResource(f, cmd, "pod", toComplete) + + // Also include resource choices for the / form, + // but only for resources that contain pods + resourcesWithPods := []string{ + "daemonsets", + "deployments", + "pods", + "jobs", + "replicasets", + "replicationcontrollers", + "services", + "statefulsets"} + + if len(comps) == 0 { + // If there are no pods to complete, we will only be completing + // /. We should disable adding a space after the /. + directive |= cobra.ShellCompDirectiveNoSpace + } + + for _, resource := range resourcesWithPods { + if strings.HasPrefix(resource, toComplete) { + comps = append(comps, fmt.Sprintf("%s/", resource)) + } + } + } else { + // Dealing with the / form, use the specified resource type + resourceType := toComplete[:slashIdx] + toComplete = toComplete[slashIdx+1:] + nameComps := CompGetResource(f, cmd, resourceType, toComplete) + for _, c := range nameComps { + comps = append(comps, fmt.Sprintf("%s/%s", resourceType, c)) + } + } + return comps, directive +} + +// convertResourceNameToPodName Converts a resource name to a pod name. +// If the resource name is of the form /, we use +// polymorphichelpers.AttachablePodForObjectFn(), if not, the resource name +// is already a pod name. +func convertResourceNameToPodName(f cmdutil.Factory, resourceName string) string { + var podName string + if !strings.Contains(resourceName, "/") { + // When we don't have the / form, the resource name is the pod name + podName = resourceName + } else { + // if the resource name is of the form /, we need to convert it to a pod name + ns, _, err := f.ToRawKubeConfigLoader().Namespace() + if err != nil { + return "" + } + + resourceWithPod, err := f.NewBuilder(). + WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...). + ContinueOnError(). + NamespaceParam(ns).DefaultNamespace(). + ResourceNames("pods", resourceName). + Do().Object() + if err != nil { + return "" + } + + // For shell completion, use a short timeout + forwardablePod, err := polymorphichelpers.AttachablePodForObjectFn(f, resourceWithPod, 100*time.Millisecond) + if err != nil { + return "" + } + podName = forwardablePod.Name + } + return podName +} diff --git a/staging/src/k8s.io/kubectl/pkg/util/completion_test.go b/staging/src/k8s.io/kubectl/pkg/util/completion/completion_test.go similarity index 61% 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 21e95eedc24..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" @@ -127,6 +127,25 @@ func TestResourceTypeAndNameCompletionFuncRepeating(t *testing.T) { checkCompletion(t, comps, []string{"foo"}, directive, cobra.ShellCompDirectiveNoFileComp) } +func TestResourceTypeAndNameCompletionFuncJointForm(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := ResourceTypeAndNameCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{}, "pod/b") + checkCompletion(t, comps, []string{"pod/bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestResourceTypeAndNameCompletionFuncJointFormRepeating(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := ResourceTypeAndNameCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{"pod/bar"}, "pod/") + // The other pods should be completed, but not the already specified ones + checkCompletion(t, comps, []string{"pod/foo"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + func TestSpecifiedResourceTypeAndNameCompletionFuncNoArgs(t *testing.T) { tf, cmd := prepareCompletionTest() addPodsToFactory(tf) @@ -155,6 +174,24 @@ func TestSpecifiedResourceTypeAndNameCompletionFuncRepeating(t *testing.T) { checkCompletion(t, comps, []string{"foo"}, directive, cobra.ShellCompDirectiveNoFileComp) } +func TestSpecifiedResourceTypeAndNameCompletionFuncJointFormOneArg(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := SpecifiedResourceTypeAndNameCompletionFunc(tf, []string{"pod"}) + comps, directive := compFunc(cmd, []string{}, "pod/b") + checkCompletion(t, comps, []string{"pod/bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestSpecifiedResourceTypeAndNameCompletionFuncJointFormRepeating(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := SpecifiedResourceTypeAndNameCompletionFunc(tf, []string{"pod"}) + comps, directive := compFunc(cmd, []string{"pod/bar"}, "pod/") + // The other pods should be completed, but not the already specified ones + checkCompletion(t, comps, []string{"pod/foo"}, directive, cobra.ShellCompDirectiveNoFileComp) +} func TestSpecifiedResourceTypeAndNameCompletionNoRepeatFuncOneArg(t *testing.T) { tf, cmd := prepareCompletionTest() addPodsToFactory(tf) @@ -174,6 +211,25 @@ func TestSpecifiedResourceTypeAndNameCompletionNoRepeatFuncMultiArg(t *testing.T checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) } +func TestSpecifiedResourceTypeAndNameCompletionNoRepeatFuncJointFormOneArg(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := SpecifiedResourceTypeAndNameNoRepeatCompletionFunc(tf, []string{"pod"}) + comps, directive := compFunc(cmd, []string{}, "pod/b") + checkCompletion(t, comps, []string{"pod/bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestSpecifiedResourceTypeAndNameCompletionNoRepeatFuncJointFormMultiArg(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := SpecifiedResourceTypeAndNameNoRepeatCompletionFunc(tf, []string{"pod"}) + comps, directive := compFunc(cmd, []string{"pod/bar"}, "pod/") + // There should not be any more pods shown as this function should not repeat the completion + checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) +} + func TestResourceNameCompletionFuncNoArgs(t *testing.T) { tf, cmd := prepareCompletionTest() addPodsToFactory(tf) @@ -192,7 +248,65 @@ func TestResourceNameCompletionFuncTooManyArgs(t *testing.T) { checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) } -func TestPodResourceNameAndContainerCompletionFuncNoArgs(t *testing.T) { +func TestResourceNameCompletionFuncJointFormNoArgs(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := ResourceNameCompletionFunc(tf, "pod") + comps, directive := compFunc(cmd, []string{}, "pod/b") + // The / should NOT be supported by this function + checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestPodResourceNameCompletionFuncNoArgsPodName(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := PodResourceNameCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{}, "b") + checkCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestPodResourceNameCompletionFuncNoArgsResources(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := PodResourceNameCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{}, "d") + checkCompletion( + t, comps, []string{"daemonsets/", "deployments/"}, + directive, cobra.ShellCompDirectiveNoFileComp|cobra.ShellCompDirectiveNoSpace) +} + +func TestPodResourceNameCompletionFuncTooManyArgs(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := PodResourceNameCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{"pod-name"}, "") + checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestPodResourceNameCompletionFuncJointFormNoArgs(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := PodResourceNameCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{}, "pod/b") + // The / SHOULD be supported by this function + checkCompletion(t, comps, []string{"pod/bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestPodResourceNameCompletionFuncJointFormTooManyArgs(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := PodResourceNameCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{"pod/name"}, "pod/b") + checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestPodResourceNameAndContainerCompletionFuncNoArgsPodName(t *testing.T) { tf, cmd := prepareCompletionTest() addPodsToFactory(tf) @@ -201,6 +315,18 @@ func TestPodResourceNameAndContainerCompletionFuncNoArgs(t *testing.T) { checkCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp) } +func TestPodResourceNameAndContainerCompletionFuncNoArgsResources(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := PodResourceNameAndContainerCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{}, "s") + checkCompletion( + t, comps, []string{"services/", "statefulsets/"}, + directive, cobra.ShellCompDirectiveNoFileComp|cobra.ShellCompDirectiveNoSpace) + +} + func TestPodResourceNameAndContainerCompletionFuncTooManyArgs(t *testing.T) { tf, cmd := prepareCompletionTest() addPodsToFactory(tf) @@ -210,6 +336,24 @@ func TestPodResourceNameAndContainerCompletionFuncTooManyArgs(t *testing.T) { checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) } +func TestPodResourceNameAndContainerCompletionFuncJointFormNoArgs(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := PodResourceNameAndContainerCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{}, "pod/b") + checkCompletion(t, comps, []string{"pod/bar"}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestPodResourceNameAndContainerCompletionFuncJointFormTooManyArgs(t *testing.T) { + tf, cmd := prepareCompletionTest() + addPodsToFactory(tf) + + compFunc := PodResourceNameAndContainerCompletionFunc(tf) + comps, directive := compFunc(cmd, []string{"pod/pod-name", "container-name"}, "") + checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) +} + func setMockFactory(config api.Config) { clientConfig := clientcmd.NewDefaultClientConfig(config, nil) testFactory := cmdtesting.NewTestFactory().WithClientConfig(clientConfig) diff --git a/vendor/modules.txt b/vendor/modules.txt index 28b28b34ade..dd3024cdf44 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -2151,6 +2151,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