mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 04:33:26 +00:00
Merge pull request #105711 from VilledeMontreal/feat/multiComp
Shell completion of multiple resource names
This commit is contained in:
commit
aa7c6338c6
@ -167,8 +167,11 @@ func NewCmdGet(parent string, f cmdutil.Factory, streams genericclioptions.IOStr
|
||||
var comps []string
|
||||
if len(args) == 0 {
|
||||
comps = apiresources.CompGetResourceList(f, cmd, toComplete)
|
||||
} else if len(args) == 1 {
|
||||
} else {
|
||||
comps = CompGetResource(f, cmd, args[0], toComplete)
|
||||
if len(args) > 1 {
|
||||
comps = cmdutil.Difference(comps, args[1:])
|
||||
}
|
||||
}
|
||||
return comps, cobra.ShellCompDirectiveNoFileComp
|
||||
},
|
||||
|
@ -102,7 +102,7 @@ func NewCmdRolloutStatus(f cmdutil.Factory, streams genericclioptions.IOStreams)
|
||||
Short: i18n.T("Show the status of the rollout"),
|
||||
Long: statusLong,
|
||||
Example: statusExample,
|
||||
ValidArgsFunction: util.SpecifiedResourceTypeAndNameCompletionFunc(f, validArgs),
|
||||
ValidArgsFunction: util.SpecifiedResourceTypeAndNameNoRepeatCompletionFunc(f, validArgs),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, args))
|
||||
cmdutil.CheckErr(o.Validate())
|
||||
|
@ -733,3 +733,18 @@ func Warning(cmdErr io.Writer, newGeneratorName, oldGeneratorName string) {
|
||||
oldGeneratorName,
|
||||
)
|
||||
}
|
||||
|
||||
// Difference removes any elements of subArray from fullArray and returns the result
|
||||
func Difference(fullArray []string, subArray []string) []string {
|
||||
exclude := make(map[string]bool, len(subArray))
|
||||
for _, elem := range subArray {
|
||||
exclude[elem] = true
|
||||
}
|
||||
var result []string
|
||||
for _, elem := range fullArray {
|
||||
if _, found := exclude[elem]; !found {
|
||||
result = append(result, elem)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
@ -25,6 +25,9 @@ import (
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
@ -321,3 +324,40 @@ func TestDumpReaderToFile(t *testing.T) {
|
||||
t.Fatalf("Wrong file content %s != %s", testString, stringData)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDifferenceFunc(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fullArray []string
|
||||
subArray []string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "remove some",
|
||||
fullArray: []string{"a", "b", "c", "d"},
|
||||
subArray: []string{"c", "b"},
|
||||
expected: []string{"a", "d"},
|
||||
},
|
||||
{
|
||||
name: "remove all",
|
||||
fullArray: []string{"a", "b", "c", "d"},
|
||||
subArray: []string{"b", "d", "a", "c"},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "remove none",
|
||||
fullArray: []string{"a", "b", "c", "d"},
|
||||
subArray: nil,
|
||||
expected: []string{"a", "b", "c", "d"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
result := Difference(tc.fullArray, tc.subArray)
|
||||
if !cmp.Equal(tc.expected, result, cmpopts.SortSlices(func(x, y string) bool {
|
||||
return x < y
|
||||
})) {
|
||||
t.Errorf("%s -> Expected: %v, but got: %v", tc.name, tc.expected, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,15 +34,18 @@ func SetFactoryForCompletion(f cmdutil.Factory) {
|
||||
}
|
||||
|
||||
// ResourceTypeAndNameCompletionFunc Returns a completion function that completes as a first argument
|
||||
// the resource types that match the toComplete prefix, and as a second argument the resource names that match
|
||||
// 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 if len(args) == 1 {
|
||||
} else {
|
||||
comps = get.CompGetResource(f, cmd, args[0], toComplete)
|
||||
if len(args) > 1 {
|
||||
comps = cmdutil.Difference(comps, args[1:])
|
||||
}
|
||||
}
|
||||
return comps, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
@ -50,8 +53,19 @@ func ResourceTypeAndNameCompletionFunc(f cmdutil.Factory) func(*cobra.Command, [
|
||||
|
||||
// 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 as a second argument the specified resource names that match the toComplete prefix.
|
||||
// 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 {
|
||||
@ -60,8 +74,13 @@ func SpecifiedResourceTypeAndNameCompletionFunc(f cmdutil.Factory, allowedTypes
|
||||
comps = append(comps, comp)
|
||||
}
|
||||
}
|
||||
} else if len(args) == 1 {
|
||||
comps = get.CompGetResource(f, cmd, args[0], toComplete)
|
||||
} 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
|
||||
}
|
||||
|
@ -35,115 +35,100 @@ import (
|
||||
func TestResourceTypeAndNameCompletionFuncOneArg(t *testing.T) {
|
||||
tf, cmd := prepareCompletionTest()
|
||||
addPodsToFactory(tf)
|
||||
|
||||
compFunc := ResourceTypeAndNameCompletionFunc(tf)
|
||||
comps, directive := compFunc(cmd, []string{"pod"}, "b")
|
||||
checkCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp)
|
||||
}
|
||||
|
||||
func TestResourceTypeAndNameCompletionFuncTooManyArgs(t *testing.T) {
|
||||
tf := cmdtesting.NewTestFactory()
|
||||
defer tf.Cleanup()
|
||||
func TestResourceTypeAndNameCompletionFuncRepeating(t *testing.T) {
|
||||
tf, cmd := prepareCompletionTest()
|
||||
addPodsToFactory(tf)
|
||||
|
||||
streams, _, _, _ := genericclioptions.NewTestIOStreams()
|
||||
cmd := get.NewCmdGet("kubectl", tf, streams)
|
||||
compFunc := ResourceTypeAndNameCompletionFunc(tf)
|
||||
comps, directive := compFunc(cmd, []string{"pod", "pod-name"}, "")
|
||||
checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp)
|
||||
comps, directive := compFunc(cmd, []string{"pod", "bar"}, "")
|
||||
// The other pods should be completed, but not the already specified ones
|
||||
checkCompletion(t, comps, []string{"foo"}, directive, cobra.ShellCompDirectiveNoFileComp)
|
||||
}
|
||||
|
||||
func TestSpecifiedResourceTypeAndNameCompletionFuncNoArgs(t *testing.T) {
|
||||
tf := cmdtesting.NewTestFactory()
|
||||
defer tf.Cleanup()
|
||||
tf, cmd := prepareCompletionTest()
|
||||
addPodsToFactory(tf)
|
||||
|
||||
streams, _, _, _ := genericclioptions.NewTestIOStreams()
|
||||
cmd := get.NewCmdGet("kubectl", tf, streams)
|
||||
compFunc := SpecifiedResourceTypeAndNameCompletionFunc(tf, []string{"pod", "service", "statefulset"})
|
||||
comps, directive := compFunc(cmd, []string{}, "s")
|
||||
checkCompletion(t, comps, []string{"service", "statefulset"}, directive, cobra.ShellCompDirectiveNoFileComp)
|
||||
}
|
||||
|
||||
func TestSpecifiedResourceTypeAndNameCompletionFuncOneArg(t *testing.T) {
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
tf, cmd := prepareCompletionTest()
|
||||
addPodsToFactory(tf)
|
||||
|
||||
pods, _, _ := cmdtesting.TestData()
|
||||
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||
Resp: &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, pods)},
|
||||
}
|
||||
|
||||
streams, _, _, _ := genericclioptions.NewTestIOStreams()
|
||||
cmd := get.NewCmdGet("kubectl", tf, streams)
|
||||
compFunc := SpecifiedResourceTypeAndNameCompletionFunc(tf, []string{"pod"})
|
||||
comps, directive := compFunc(cmd, []string{"pod"}, "b")
|
||||
checkCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp)
|
||||
}
|
||||
|
||||
func TestSpecifiedResourceTypeAndNameCompletionFuncTooManyArgs(t *testing.T) {
|
||||
tf := cmdtesting.NewTestFactory()
|
||||
defer tf.Cleanup()
|
||||
func TestSpecifiedResourceTypeAndNameCompletionFuncRepeating(t *testing.T) {
|
||||
tf, cmd := prepareCompletionTest()
|
||||
addPodsToFactory(tf)
|
||||
|
||||
streams, _, _, _ := genericclioptions.NewTestIOStreams()
|
||||
cmd := get.NewCmdGet("kubectl", tf, streams)
|
||||
compFunc := SpecifiedResourceTypeAndNameCompletionFunc(tf, []string{"pod"})
|
||||
comps, directive := compFunc(cmd, []string{"pod", "pod-name"}, "")
|
||||
comps, directive := compFunc(cmd, []string{"pod", "bar"}, "")
|
||||
// The other pods should be completed, but not the already specified ones
|
||||
checkCompletion(t, comps, []string{"foo"}, directive, cobra.ShellCompDirectiveNoFileComp)
|
||||
}
|
||||
|
||||
func TestSpecifiedResourceTypeAndNameCompletionNoRepeatFuncOneArg(t *testing.T) {
|
||||
tf, cmd := prepareCompletionTest()
|
||||
addPodsToFactory(tf)
|
||||
|
||||
compFunc := SpecifiedResourceTypeAndNameNoRepeatCompletionFunc(tf, []string{"pod"})
|
||||
comps, directive := compFunc(cmd, []string{"pod"}, "b")
|
||||
checkCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp)
|
||||
}
|
||||
|
||||
func TestSpecifiedResourceTypeAndNameCompletionNoRepeatFuncMultiArg(t *testing.T) {
|
||||
tf, cmd := prepareCompletionTest()
|
||||
addPodsToFactory(tf)
|
||||
|
||||
compFunc := SpecifiedResourceTypeAndNameNoRepeatCompletionFunc(tf, []string{"pod"})
|
||||
comps, directive := compFunc(cmd, []string{"pod", "bar"}, "")
|
||||
// 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 := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
tf, cmd := prepareCompletionTest()
|
||||
addPodsToFactory(tf)
|
||||
|
||||
pods, _, _ := cmdtesting.TestData()
|
||||
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||
Resp: &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, pods)},
|
||||
}
|
||||
|
||||
streams, _, _, _ := genericclioptions.NewTestIOStreams()
|
||||
cmd := get.NewCmdGet("kubectl", tf, streams)
|
||||
compFunc := ResourceNameCompletionFunc(tf, "pod")
|
||||
comps, directive := compFunc(cmd, []string{}, "b")
|
||||
checkCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp)
|
||||
}
|
||||
|
||||
func TestResourceNameCompletionFuncTooManyArgs(t *testing.T) {
|
||||
tf := cmdtesting.NewTestFactory()
|
||||
defer tf.Cleanup()
|
||||
tf, cmd := prepareCompletionTest()
|
||||
addPodsToFactory(tf)
|
||||
|
||||
streams, _, _, _ := genericclioptions.NewTestIOStreams()
|
||||
cmd := get.NewCmdGet("kubectl", tf, streams)
|
||||
compFunc := ResourceNameCompletionFunc(tf, "pod")
|
||||
comps, directive := compFunc(cmd, []string{"pod-name"}, "")
|
||||
checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp)
|
||||
}
|
||||
|
||||
func TestPodResourceNameAndContainerCompletionFuncNoArgs(t *testing.T) {
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
tf, cmd := prepareCompletionTest()
|
||||
addPodsToFactory(tf)
|
||||
|
||||
pods, _, _ := cmdtesting.TestData()
|
||||
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||
Resp: &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, pods)},
|
||||
}
|
||||
|
||||
streams, _, _, _ := genericclioptions.NewTestIOStreams()
|
||||
cmd := get.NewCmdGet("kubectl", tf, streams)
|
||||
compFunc := PodResourceNameAndContainerCompletionFunc(tf)
|
||||
comps, directive := compFunc(cmd, []string{}, "b")
|
||||
checkCompletion(t, comps, []string{"bar"}, directive, cobra.ShellCompDirectiveNoFileComp)
|
||||
}
|
||||
|
||||
func TestPodResourceNameAndContainerCompletionFuncTooManyArgs(t *testing.T) {
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
tf, cmd := prepareCompletionTest()
|
||||
addPodsToFactory(tf)
|
||||
|
||||
streams, _, _, _ := genericclioptions.NewTestIOStreams()
|
||||
cmd := get.NewCmdGet("kubectl", tf, streams)
|
||||
compFunc := PodResourceNameAndContainerCompletionFunc(tf)
|
||||
comps, directive := compFunc(cmd, []string{"pod-name", "container-name"}, "")
|
||||
checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp)
|
||||
|
Loading…
Reference in New Issue
Block a user