Support completion for the <type>/<name> form

This commit teaches the shell completion logic how to handle the
<type>/<name> form for resource specification.

It also teaches the 'exec' command how to complete its '--container/-c'
flag using container names.

Also, for commands that work on pods, kubectl will now also suggest
completion choices of the form <type>/<name> for resource types that
contain pods (see below for more details).

The following commands can now have completion of the <type>/<name>
form. Commands that accept any resource type:

annotate
apply edit-last-applied
apply view-last-applied
delete
describe
edit
get
label
patch

Commands that accept a subset of resource types:

autoscale
expose
rollout history
rollout pause
rollout restart
rollout resume
rollout status
rollout undo
scale
taint

Commands that apply to resource types that contain pods:

attach
exec
logs
port-foward

For these last four commands, the possible resource types are now
included in the completion choices.  For example:
    kubectl exec d<TAB>
will suggest
    daemonsets/   deployments/

Signed-off-by: Marc Khouzam <marc.khouzam@montreal.ca>

Kubernetes-commit: cf66f5c3cbd0a0e2f223af438ee4c6bc7e4a907c
This commit is contained in:
Marc Khouzam 2022-03-01 10:45:59 -05:00 committed by Kubernetes Publisher
parent a1f04cb16d
commit cd245d2e22
6 changed files with 369 additions and 66 deletions

View File

@ -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: util.PodResourceNameCompletionFunc(f),
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())

View File

@ -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: util.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", util.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")

View File

@ -170,18 +170,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: ResourceTypeAndNameCompletionFunc(f, nil, true),
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd))
@ -932,3 +921,82 @@ func CompGetFromTemplate(template *string, f cmdutil.Factory, namespace string,
}
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

@ -109,7 +109,7 @@ func NewCmdPortForward(f cmdutil.Factory, streams genericclioptions.IOStreams) *
Short: i18n.T("Forward one or more local ports to a pod"),
Long: portforwardLong,
Example: portforwardExample,
ValidArgsFunction: util.ResourceNameCompletionFunc(f, "pod"),
ValidArgsFunction: util.PodResourceNameCompletionFunc(f),
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(opts.Complete(f, cmd, args))
cmdutil.CheckErr(opts.Validate())

View File

@ -17,12 +17,15 @@ limitations under the License.
package util
import (
"fmt"
"strings"
"time"
"github.com/spf13/cobra"
"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
@ -33,61 +36,32 @@ 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.
// 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) {
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
}
// The logic is in the 'get' package because the 'get' command must be able to call it also
return get.ResourceTypeAndNameCompletionFunc(f, nil, true)
}
// 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.
// 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 doSpecifiedResourceTypeAndNameComp(f, allowedTypes, true)
return get.ResourceTypeAndNameCompletionFunc(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.
// 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 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
}
return get.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 <type>/<name> 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 <type>/<name>
// 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
@ -98,16 +72,49 @@ func ResourceNameCompletionFunc(f cmdutil.Factory, resourceType string) func(*co
}
}
// 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.
// 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 = get.CompGetResource(f, cmd, "pod", toComplete)
comps, directive = doPodResourceCompletion(f, cmd, toComplete)
} else if len(args) == 1 {
comps = get.CompGetContainers(f, cmd, args[0], toComplete)
podName := convertResourceNameToPodName(f, args[0])
comps = get.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 <type>/<name> 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 <type>/<name> so we need to convert it.
if len(args) > 0 {
podName := convertResourceNameToPodName(f, args[0])
comps = get.CompGetContainers(f, cmd, podName, toComplete)
}
return comps, cobra.ShellCompDirectiveNoFileComp
}
@ -184,3 +191,85 @@ func ListUsersInConfig(toComplete string) []string {
}
return ret
}
// 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 = get.CompGetResource(f, cmd, "pod", toComplete)
// Also include resource choices for the <type>/<name> 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
// <type>/. 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 <type>/<name> form, use the specified resource type
resourceType := toComplete[:slashIdx]
toComplete = toComplete[slashIdx+1:]
nameComps := get.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 <type>/<name>, 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 <type>/<name> form, the resource name is the pod name
podName = resourceName
} else {
// if the resource name is of the form <type>/<name>, 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
}

View File

@ -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 <type>/<name> 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 <type>/<name> 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)