1
16
17 package create
18
19 import (
20 "testing"
21
22 rbac "k8s.io/api/rbac/v1"
23 "k8s.io/apimachinery/pkg/api/equality"
24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25 "k8s.io/apimachinery/pkg/runtime"
26 "k8s.io/cli-runtime/pkg/genericiooptions"
27 "k8s.io/client-go/rest/fake"
28 cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
29 "k8s.io/kubectl/pkg/scheme"
30 )
31
32 func TestCreateClusterRole(t *testing.T) {
33 clusterRoleName := "my-cluster-role"
34
35 tf := cmdtesting.NewTestFactory().WithNamespace("test")
36 defer tf.Cleanup()
37
38 tf.Client = &fake.RESTClient{}
39 tf.ClientConfigVal = cmdtesting.DefaultClientConfig()
40
41 tests := map[string]struct {
42 verbs string
43 resources string
44 nonResourceURL string
45 resourceNames string
46 aggregationRule string
47 expectedClusterRole *rbac.ClusterRole
48 }{
49 "test-duplicate-resources": {
50 verbs: "get,watch,list",
51 resources: "pods,pods",
52 expectedClusterRole: &rbac.ClusterRole{
53 TypeMeta: metav1.TypeMeta{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRole"},
54 ObjectMeta: metav1.ObjectMeta{
55 Name: clusterRoleName,
56 },
57 Rules: []rbac.PolicyRule{
58 {
59 Verbs: []string{"get", "watch", "list"},
60 Resources: []string{"pods"},
61 APIGroups: []string{""},
62 ResourceNames: []string{},
63 },
64 },
65 },
66 },
67 "test-valid-case-with-multiple-apigroups": {
68 verbs: "get,watch,list",
69 resources: "pods,deployments.extensions",
70 expectedClusterRole: &rbac.ClusterRole{
71 TypeMeta: metav1.TypeMeta{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRole"},
72 ObjectMeta: metav1.ObjectMeta{
73 Name: clusterRoleName,
74 },
75 Rules: []rbac.PolicyRule{
76 {
77 Verbs: []string{"get", "watch", "list"},
78 Resources: []string{"pods"},
79 APIGroups: []string{""},
80 ResourceNames: []string{},
81 },
82 {
83 Verbs: []string{"get", "watch", "list"},
84 Resources: []string{"deployments"},
85 APIGroups: []string{"extensions"},
86 ResourceNames: []string{},
87 },
88 },
89 },
90 },
91 "test-non-resource-url": {
92 verbs: "get",
93 nonResourceURL: "/logs/,/healthz",
94 expectedClusterRole: &rbac.ClusterRole{
95 TypeMeta: metav1.TypeMeta{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRole"},
96 ObjectMeta: metav1.ObjectMeta{
97 Name: clusterRoleName,
98 },
99 Rules: []rbac.PolicyRule{
100 {
101 Verbs: []string{"get"},
102 NonResourceURLs: []string{"/logs/", "/healthz"},
103 },
104 },
105 },
106 },
107 "test-resource-and-non-resource-url": {
108 verbs: "get",
109 nonResourceURL: "/logs/,/healthz",
110 resources: "pods",
111 expectedClusterRole: &rbac.ClusterRole{
112 TypeMeta: metav1.TypeMeta{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRole"},
113 ObjectMeta: metav1.ObjectMeta{
114 Name: clusterRoleName,
115 },
116 Rules: []rbac.PolicyRule{
117 {
118 Verbs: []string{"get"},
119 Resources: []string{"pods"},
120 APIGroups: []string{""},
121 ResourceNames: []string{},
122 },
123 {
124 Verbs: []string{"get"},
125 NonResourceURLs: []string{"/logs/", "/healthz"},
126 },
127 },
128 },
129 },
130 "test-aggregation-rules": {
131 aggregationRule: "foo1=foo2,foo3=foo4",
132 expectedClusterRole: &rbac.ClusterRole{
133 TypeMeta: metav1.TypeMeta{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRole"},
134 ObjectMeta: metav1.ObjectMeta{
135 Name: clusterRoleName,
136 },
137 AggregationRule: &rbac.AggregationRule{
138 ClusterRoleSelectors: []metav1.LabelSelector{
139 {
140 MatchLabels: map[string]string{
141 "foo1": "foo2",
142 "foo3": "foo4",
143 },
144 },
145 },
146 },
147 },
148 },
149 }
150
151 for name, test := range tests {
152 ioStreams, _, buf, _ := genericiooptions.NewTestIOStreams()
153 cmd := NewCmdCreateClusterRole(tf, ioStreams)
154 cmd.Flags().Set("dry-run", "client")
155 cmd.Flags().Set("output", "yaml")
156 cmd.Flags().Set("verb", test.verbs)
157 cmd.Flags().Set("resource", test.resources)
158 cmd.Flags().Set("non-resource-url", test.nonResourceURL)
159 cmd.Flags().Set("aggregation-rule", test.aggregationRule)
160 if test.resourceNames != "" {
161 cmd.Flags().Set("resource-name", test.resourceNames)
162 }
163 cmd.Run(cmd, []string{clusterRoleName})
164 actual := &rbac.ClusterRole{}
165 if err := runtime.DecodeInto(scheme.Codecs.UniversalDecoder(), buf.Bytes(), actual); err != nil {
166 t.Log(buf.String())
167 t.Fatal(err)
168 }
169 if !equality.Semantic.DeepEqual(test.expectedClusterRole, actual) {
170 t.Errorf("%s:\nexpected:\n%#v\nsaw:\n%#v", name, test.expectedClusterRole, actual)
171 }
172 }
173 }
174
175 func TestClusterRoleValidate(t *testing.T) {
176 tf := cmdtesting.NewTestFactory().WithNamespace("test")
177 defer tf.Cleanup()
178
179 tests := map[string]struct {
180 clusterRoleOptions *CreateClusterRoleOptions
181 expectErr bool
182 }{
183 "test-missing-name": {
184 clusterRoleOptions: &CreateClusterRoleOptions{
185 CreateRoleOptions: &CreateRoleOptions{},
186 },
187 expectErr: true,
188 },
189 "test-missing-verb": {
190 clusterRoleOptions: &CreateClusterRoleOptions{
191 CreateRoleOptions: &CreateRoleOptions{
192 Name: "my-clusterrole",
193 },
194 },
195 expectErr: true,
196 },
197 "test-missing-resource": {
198 clusterRoleOptions: &CreateClusterRoleOptions{
199 CreateRoleOptions: &CreateRoleOptions{
200 Name: "my-clusterrole",
201 Verbs: []string{"get"},
202 },
203 },
204 expectErr: true,
205 },
206 "test-missing-resource-existing-apigroup": {
207 clusterRoleOptions: &CreateClusterRoleOptions{
208 CreateRoleOptions: &CreateRoleOptions{
209 Name: "my-clusterrole",
210 Verbs: []string{"get"},
211 Resources: []ResourceOptions{
212 {
213 Group: "extensions",
214 },
215 },
216 },
217 },
218 expectErr: true,
219 },
220 "test-missing-resource-existing-subresource": {
221 clusterRoleOptions: &CreateClusterRoleOptions{
222 CreateRoleOptions: &CreateRoleOptions{
223 Name: "my-clusterrole",
224 Verbs: []string{"get"},
225 Resources: []ResourceOptions{
226 {
227 SubResource: "scale",
228 },
229 },
230 },
231 },
232 expectErr: true,
233 },
234 "test-invalid-verb": {
235 clusterRoleOptions: &CreateClusterRoleOptions{
236 CreateRoleOptions: &CreateRoleOptions{
237 Name: "my-clusterrole",
238 Verbs: []string{"invalid-verb"},
239 Resources: []ResourceOptions{
240 {
241 Resource: "pods",
242 },
243 },
244 },
245 },
246 expectErr: false,
247 },
248 "test-nonresource-verb": {
249 clusterRoleOptions: &CreateClusterRoleOptions{
250 CreateRoleOptions: &CreateRoleOptions{
251 Name: "my-clusterrole",
252 Verbs: []string{"post"},
253 Resources: []ResourceOptions{
254 {
255 Resource: "pods",
256 },
257 },
258 },
259 },
260 expectErr: false,
261 },
262 "test-special-verb": {
263 clusterRoleOptions: &CreateClusterRoleOptions{
264 CreateRoleOptions: &CreateRoleOptions{
265 Name: "my-clusterrole",
266 Verbs: []string{"use"},
267 Resources: []ResourceOptions{
268 {
269 Resource: "pods",
270 },
271 },
272 },
273 },
274 expectErr: true,
275 },
276 "test-mix-verbs": {
277 clusterRoleOptions: &CreateClusterRoleOptions{
278 CreateRoleOptions: &CreateRoleOptions{
279 Name: "my-clusterrole",
280 Verbs: []string{"impersonate", "use"},
281 Resources: []ResourceOptions{
282 {
283 Resource: "userextras",
284 SubResource: "scopes",
285 },
286 },
287 },
288 },
289 expectErr: true,
290 },
291 "test-special-verb-with-wrong-apigroup": {
292 clusterRoleOptions: &CreateClusterRoleOptions{
293 CreateRoleOptions: &CreateRoleOptions{
294 Name: "my-clusterrole",
295 Verbs: []string{"impersonate"},
296 Resources: []ResourceOptions{
297 {
298 Resource: "userextras",
299 SubResource: "scopes",
300 Group: "extensions",
301 },
302 },
303 },
304 },
305 expectErr: true,
306 },
307 "test-invalid-resource": {
308 clusterRoleOptions: &CreateClusterRoleOptions{
309 CreateRoleOptions: &CreateRoleOptions{
310 Name: "my-clusterrole",
311 Verbs: []string{"get"},
312 Resources: []ResourceOptions{
313 {
314 Resource: "invalid-resource",
315 },
316 },
317 },
318 },
319 expectErr: true,
320 },
321 "test-resource-name-with-multiple-resources": {
322 clusterRoleOptions: &CreateClusterRoleOptions{
323 CreateRoleOptions: &CreateRoleOptions{
324 Name: "my-clusterrole",
325 Verbs: []string{"get"},
326 Resources: []ResourceOptions{
327 {
328 Resource: "pods",
329 },
330 {
331 Resource: "deployments",
332 Group: "extensions",
333 },
334 },
335 },
336 },
337 expectErr: false,
338 },
339 "test-valid-case": {
340 clusterRoleOptions: &CreateClusterRoleOptions{
341 CreateRoleOptions: &CreateRoleOptions{
342 Name: "role-binder",
343 Verbs: []string{"get", "list", "bind"},
344 Resources: []ResourceOptions{
345 {
346 Resource: "roles",
347 Group: "rbac.authorization.k8s.io",
348 },
349 },
350 },
351 },
352 expectErr: false,
353 },
354 "test-valid-case-with-subresource": {
355 clusterRoleOptions: &CreateClusterRoleOptions{
356 CreateRoleOptions: &CreateRoleOptions{
357 Name: "my-clusterrole",
358 Verbs: []string{"get", "list"},
359 Resources: []ResourceOptions{
360 {
361 Resource: "replicasets",
362 SubResource: "scale",
363 },
364 },
365 },
366 },
367 expectErr: false,
368 },
369 "test-valid-case-with-additional-resource": {
370 clusterRoleOptions: &CreateClusterRoleOptions{
371 CreateRoleOptions: &CreateRoleOptions{
372 Name: "my-clusterrole",
373 Verbs: []string{"impersonate"},
374 Resources: []ResourceOptions{
375 {
376 Resource: "userextras",
377 SubResource: "scopes",
378 Group: "authentication.k8s.io",
379 },
380 },
381 },
382 },
383 expectErr: false,
384 },
385 "test-invalid-empty-non-resource-url": {
386 clusterRoleOptions: &CreateClusterRoleOptions{
387 CreateRoleOptions: &CreateRoleOptions{
388 Name: "my-clusterrole",
389 Verbs: []string{"create"},
390 },
391 NonResourceURLs: []string{""},
392 },
393 expectErr: true,
394 },
395 "test-invalid-non-resource-url": {
396 clusterRoleOptions: &CreateClusterRoleOptions{
397 CreateRoleOptions: &CreateRoleOptions{
398 Name: "my-clusterrole",
399 Verbs: []string{"create"},
400 },
401 NonResourceURLs: []string{"logs"},
402 },
403 expectErr: true,
404 },
405 "test-invalid-non-resource-url-with-*": {
406 clusterRoleOptions: &CreateClusterRoleOptions{
407 CreateRoleOptions: &CreateRoleOptions{
408 Name: "my-clusterrole",
409 Verbs: []string{"create"},
410 },
411 NonResourceURLs: []string{"/logs/*/"},
412 },
413 expectErr: true,
414 },
415 "test-invalid-non-resource-url-with-multiple-*": {
416 clusterRoleOptions: &CreateClusterRoleOptions{
417 CreateRoleOptions: &CreateRoleOptions{
418 Name: "my-clusterrole",
419 Verbs: []string{"create"},
420 },
421 NonResourceURLs: []string{"/logs*/*"},
422 },
423 expectErr: true,
424 },
425 "test-invalid-verb-for-non-resource-url": {
426 clusterRoleOptions: &CreateClusterRoleOptions{
427 CreateRoleOptions: &CreateRoleOptions{
428 Name: "my-clusterrole",
429 Verbs: []string{"create"},
430 },
431 NonResourceURLs: []string{"/logs/"},
432 },
433 expectErr: true,
434 },
435 "test-resource-and-non-resource-url-specified-together": {
436 clusterRoleOptions: &CreateClusterRoleOptions{
437 CreateRoleOptions: &CreateRoleOptions{
438 Name: "my-clusterrole",
439 Verbs: []string{"get"},
440 Resources: []ResourceOptions{
441 {
442 Resource: "replicasets",
443 SubResource: "scale",
444 },
445 },
446 },
447 NonResourceURLs: []string{"/logs/", "/logs/*"},
448 },
449 expectErr: false,
450 },
451 "test-aggregation-rule-with-verb": {
452 clusterRoleOptions: &CreateClusterRoleOptions{
453 CreateRoleOptions: &CreateRoleOptions{
454 Name: "my-clusterrole",
455 Verbs: []string{"get"},
456 },
457 AggregationRule: map[string]string{"foo-key": "foo-vlue"},
458 },
459 expectErr: true,
460 },
461 "test-aggregation-rule-with-resource": {
462 clusterRoleOptions: &CreateClusterRoleOptions{
463 CreateRoleOptions: &CreateRoleOptions{
464 Name: "my-clusterrole",
465 Resources: []ResourceOptions{
466 {
467 Resource: "replicasets",
468 SubResource: "scale",
469 },
470 },
471 },
472 AggregationRule: map[string]string{"foo-key": "foo-vlue"},
473 },
474 expectErr: true,
475 },
476 "test-aggregation-rule-with-no-resource-url": {
477 clusterRoleOptions: &CreateClusterRoleOptions{
478 CreateRoleOptions: &CreateRoleOptions{
479 Name: "my-clusterrole",
480 },
481 NonResourceURLs: []string{"/logs/"},
482 AggregationRule: map[string]string{"foo-key": "foo-vlue"},
483 },
484 expectErr: true,
485 },
486 "test-aggregation-rule": {
487 clusterRoleOptions: &CreateClusterRoleOptions{
488 CreateRoleOptions: &CreateRoleOptions{
489 Name: "my-clusterrole",
490 },
491 AggregationRule: map[string]string{"foo-key": "foo-vlue"},
492 },
493 expectErr: false,
494 },
495 }
496
497 for name, test := range tests {
498 t.Run(name, func(t *testing.T) {
499 test.clusterRoleOptions.IOStreams = genericiooptions.NewTestIOStreamsDiscard()
500 var err error
501 test.clusterRoleOptions.Mapper, err = tf.ToRESTMapper()
502 if err != nil {
503 t.Fatal(err)
504 }
505 err = test.clusterRoleOptions.Validate()
506 if test.expectErr && err == nil {
507 t.Errorf("%s: expect error happens, but validate passes.", name)
508 }
509 if !test.expectErr && err != nil {
510 t.Errorf("%s: unexpected error: %v", name, err)
511 }
512 })
513 }
514 }
515
View as plain text