Refactor completion code into its own package

Signed-off-by: Marc Khouzam <marc.khouzam@montreal.ca>
This commit is contained in:
Marc Khouzam 2022-03-24 09:06:05 -04:00
parent cf66f5c3cb
commit c4f8c57b43
40 changed files with 278 additions and 268 deletions

View File

@ -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())

View File

@ -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
}

View File

@ -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())

View File

@ -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))

View File

@ -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())

View File

@ -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())

View File

@ -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
}))
}

View File

@ -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))
},

View File

@ -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))
},

View File

@ -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())

View File

@ -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())

View File

@ -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()

View File

@ -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())

View File

@ -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>/<pod>
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))
}

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -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")

View File

@ -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))

View File

@ -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 <type>/<name> 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 <type> (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
// <type> form (not the form <type>/<name>).
// 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 / (<type>/<name>).
// 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 <type>/<name>
// 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
}
}

View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -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))

View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -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 <type>/<name> 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 <type>/<name> 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 <type>/<name> 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 <type>/<name> 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 <type>/<name> 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 <type> (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
// <type> form (not the form <type>/<name>).
// 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 / (<type>/<name>).
// 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 <type>/<name>
// 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 <type>/<name> 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 <type>/<name> 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))
}

View File

@ -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"

1
vendor/modules.txt vendored
View File

@ -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