diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/auth/cani.go b/staging/src/k8s.io/kubectl/pkg/cmd/auth/cani.go index ca7899cfb44..10305cec8be 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/auth/cani.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/auth/cani.go @@ -98,6 +98,8 @@ var ( resourceVerbs = sets.NewString("get", "list", "watch", "create", "update", "patch", "delete", "deletecollection", "use", "bind", "impersonate", "*") nonResourceURLVerbs = sets.NewString("get", "put", "post", "head", "options", "delete", "patch", "*") + // holds all the server-supported resources that cannot be discovered by clients. i.e. users and groups for the impersonate verb + nonStandardResourceNames = sets.NewString("users", "groups") ) // NewCmdCanI returns an initialized Command for 'auth can-i' sub command @@ -304,10 +306,12 @@ func (o *CanIOptions) resourceFor(mapper meta.RESTMapper, resourceArg string) sc var err error gvr, err = mapper.ResourceFor(groupResource.WithVersion("")) if err != nil { - if len(groupResource.Group) == 0 { - fmt.Fprintf(o.ErrOut, "Warning: the server doesn't have a resource type '%s'\n", groupResource.Resource) - } else { - fmt.Fprintf(o.ErrOut, "Warning: the server doesn't have a resource type '%s' in group '%s'\n", groupResource.Resource, groupResource.Group) + if !nonStandardResourceNames.Has(groupResource.String()) { + if len(groupResource.Group) == 0 { + fmt.Fprintf(o.ErrOut, "Warning: the server doesn't have a resource type '%s'\n", groupResource.Resource) + } else { + fmt.Fprintf(o.ErrOut, "Warning: the server doesn't have a resource type '%s' in group '%s'\n", groupResource.Resource, groupResource.Group) + } } return schema.GroupVersionResource{Resource: resourceArg} } diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/auth/cani_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/auth/cani_test.go index 34f167956e2..deff3708274 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/auth/cani_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/auth/cani_test.go @@ -230,6 +230,85 @@ func TestRunAccessList(t *testing.T) { }) } +func TestRunResourceFor(t *testing.T) { + tests := []struct { + name string + o *CanIOptions + resourceArg string + + expectGVR schema.GroupVersionResource + expectedErrOut string + }{ + { + name: "any resources", + o: &CanIOptions{}, + resourceArg: "*", + expectGVR: schema.GroupVersionResource{ + Resource: "*", + }, + }, + { + name: "server-supported standard resources without group", + o: &CanIOptions{}, + resourceArg: "pods", + expectGVR: schema.GroupVersionResource{ + Version: "v1", + Resource: "pods", + }, + }, + { + name: "server-supported standard resources with group", + o: &CanIOptions{}, + resourceArg: "jobs", + expectGVR: schema.GroupVersionResource{ + Group: "batch", + Version: "v1", + Resource: "jobs", + }, + }, + { + name: "server-supported nonstandard resources", + o: &CanIOptions{}, + resourceArg: "users", + expectGVR: schema.GroupVersionResource{ + Resource: "users", + }, + }, + { + name: "invalid resources", + o: &CanIOptions{}, + resourceArg: "invalid", + expectGVR: schema.GroupVersionResource{ + Resource: "invalid", + }, + expectedErrOut: "Warning: the server doesn't have a resource type 'invalid'\n", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + tf := cmdtesting.NewTestFactory().WithNamespace("test") + defer tf.Cleanup() + + ioStreams, _, _, buf := genericclioptions.NewTestIOStreams() + test.o.IOStreams = ioStreams + + restMapper, err := tf.ToRESTMapper() + if err != nil { + t.Errorf("got unexpected error when do tf.ToRESTMapper(): %v", err) + return + } + gvr := test.o.resourceFor(restMapper, test.resourceArg) + if gvr != test.expectGVR { + t.Errorf("expected %v\n but got %v\n", test.expectGVR, gvr) + } + if buf.String() != test.expectedErrOut { + t.Errorf("expected %v\n but got %v\n", test.expectedErrOut, buf.String()) + } + }) + } +} + func getSelfSubjectRulesReview() *authorizationv1.SelfSubjectRulesReview { return &authorizationv1.SelfSubjectRulesReview{ Status: authorizationv1.SubjectRulesReviewStatus{