1
16
17 package gc
18
19 import (
20 "context"
21 "fmt"
22 "strings"
23 "testing"
24
25 appsv1 "k8s.io/api/apps/v1"
26 corev1 "k8s.io/api/core/v1"
27 "k8s.io/apimachinery/pkg/api/meta"
28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29 "k8s.io/apimachinery/pkg/runtime"
30 "k8s.io/apimachinery/pkg/runtime/schema"
31 "k8s.io/apiserver/pkg/admission"
32 "k8s.io/apiserver/pkg/admission/initializer"
33 "k8s.io/apiserver/pkg/authentication/user"
34 "k8s.io/apiserver/pkg/authorization/authorizer"
35 fakediscovery "k8s.io/client-go/discovery/fake"
36 "k8s.io/client-go/restmapper"
37 coretesting "k8s.io/client-go/testing"
38 api "k8s.io/kubernetes/pkg/apis/core"
39 kubeadmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
40 )
41
42 type fakeAuthorizer struct{}
43
44 func (fakeAuthorizer) Authorize(ctx context.Context, a authorizer.Attributes) (authorizer.Decision, string, error) {
45 username := a.GetUser().GetName()
46
47 if username == "non-deleter" {
48 if a.GetVerb() == "delete" {
49 return authorizer.DecisionNoOpinion, "", nil
50 }
51 if a.GetVerb() == "update" && a.GetSubresource() == "finalizers" {
52 return authorizer.DecisionNoOpinion, "", nil
53 }
54 if a.GetAPIGroup() == "*" && a.GetResource() == "*" {
55 return authorizer.DecisionNoOpinion, "", nil
56 }
57 return authorizer.DecisionAllow, "", nil
58 }
59
60 if username == "non-pod-deleter" {
61 if a.GetVerb() == "delete" && a.GetResource() == "pods" {
62 return authorizer.DecisionNoOpinion, "", nil
63 }
64 if a.GetVerb() == "update" && a.GetResource() == "pods" && a.GetSubresource() == "finalizers" {
65 return authorizer.DecisionNoOpinion, "", nil
66 }
67 if a.GetAPIGroup() == "*" && a.GetResource() == "*" {
68 return authorizer.DecisionNoOpinion, "", nil
69 }
70 return authorizer.DecisionAllow, "", nil
71 }
72
73 if username == "non-rc-deleter" {
74 if a.GetVerb() == "delete" && a.GetResource() == "replicationcontrollers" {
75 return authorizer.DecisionNoOpinion, "", nil
76 }
77 if a.GetVerb() == "update" && a.GetResource() == "replicationcontrollers" && a.GetSubresource() == "finalizers" {
78 return authorizer.DecisionNoOpinion, "", nil
79 }
80 if a.GetAPIGroup() == "*" && a.GetResource() == "*" {
81 return authorizer.DecisionNoOpinion, "", nil
82 }
83 return authorizer.DecisionAllow, "", nil
84 }
85
86 if username == "non-node-deleter" {
87 if a.GetVerb() == "delete" && a.GetResource() == "nodes" {
88 return authorizer.DecisionNoOpinion, "", nil
89 }
90 if a.GetVerb() == "update" && a.GetResource() == "nodes" && a.GetSubresource() == "finalizers" {
91 return authorizer.DecisionNoOpinion, "", nil
92 }
93 if a.GetAPIGroup() == "*" && a.GetResource() == "*" {
94 return authorizer.DecisionNoOpinion, "", nil
95 }
96 return authorizer.DecisionAllow, "", nil
97 }
98
99 return authorizer.DecisionAllow, "", nil
100 }
101
102
103 func newGCPermissionsEnforcement() (*gcPermissionsEnforcement, error) {
104
105
106
107 whiteList := []whiteListItem{
108 {
109 groupResource: schema.GroupResource{Resource: "pods"},
110 subresource: "status",
111 },
112 }
113 gcAdmit := &gcPermissionsEnforcement{
114 Handler: admission.NewHandler(admission.Create, admission.Update),
115 whiteList: whiteList,
116 }
117
118 fakeDiscoveryClient := &fakediscovery.FakeDiscovery{Fake: &coretesting.Fake{}}
119 fakeDiscoveryClient.Resources = []*metav1.APIResourceList{
120 {
121 GroupVersion: corev1.SchemeGroupVersion.String(),
122 APIResources: []metav1.APIResource{
123 {Name: "nodes", Namespaced: false, Kind: "Node"},
124 {Name: "pods", Namespaced: true, Kind: "Pod"},
125 {Name: "replicationcontrollers", Namespaced: true, Kind: "ReplicationController"},
126 },
127 },
128 {
129 GroupVersion: appsv1.SchemeGroupVersion.String(),
130 APIResources: []metav1.APIResource{
131 {Name: "daemonsets", Namespaced: true, Kind: "DaemonSet"},
132 },
133 },
134 }
135 restMapperRes, err := restmapper.GetAPIGroupResources(fakeDiscoveryClient)
136 if err != nil {
137 return nil, fmt.Errorf("unexpected error while constructing resource list from fake discovery client: %v", err)
138 }
139 restMapper := restmapper.NewDiscoveryRESTMapper(restMapperRes)
140 genericPluginInitializer := initializer.New(nil, nil, nil, fakeAuthorizer{}, nil, nil, restMapper)
141
142 pluginInitializer := kubeadmission.NewPluginInitializer(nil, nil, nil)
143 initializersChain := admission.PluginInitializers{}
144 initializersChain = append(initializersChain, genericPluginInitializer)
145 initializersChain = append(initializersChain, pluginInitializer)
146
147 initializersChain.Initialize(gcAdmit)
148 return gcAdmit, nil
149 }
150
151 type neverReturningRESTMapper struct{}
152
153 var _ meta.RESTMapper = &neverReturningRESTMapper{}
154
155 func (r *neverReturningRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
156
157 panic("test failed")
158 }
159 func (r *neverReturningRESTMapper) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
160
161 panic("test failed")
162 }
163 func (r *neverReturningRESTMapper) ResourceFor(input schema.GroupVersionResource) (schema.GroupVersionResource, error) {
164
165 panic("test failed")
166 }
167 func (r *neverReturningRESTMapper) ResourcesFor(input schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
168
169 panic("test failed")
170 }
171 func (r *neverReturningRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) {
172
173 panic("test failed")
174 }
175 func (r *neverReturningRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*meta.RESTMapping, error) {
176
177 panic("test failed")
178 }
179 func (r *neverReturningRESTMapper) ResourceSingularizer(resource string) (singular string, err error) {
180
181 panic("test failed")
182 }
183
184 func TestGCAdmission(t *testing.T) {
185 expectNoError := func(err error) bool {
186 return err == nil
187 }
188 expectCantSetOwnerRefError := func(err error) bool {
189 if err == nil {
190 return false
191 }
192 return strings.Contains(err.Error(), "cannot set an ownerRef on a resource you can't delete")
193 }
194 tests := []struct {
195 name string
196 username string
197 resource schema.GroupVersionResource
198 subresource string
199 oldObj runtime.Object
200 newObj runtime.Object
201
202 checkError func(error) bool
203 }{
204 {
205 name: "super-user, create, no objectref change",
206 username: "super",
207 resource: api.SchemeGroupVersion.WithResource("pods"),
208 newObj: &api.Pod{},
209 checkError: expectNoError,
210 },
211 {
212 name: "super-user, create, objectref change",
213 username: "super",
214 resource: api.SchemeGroupVersion.WithResource("pods"),
215 newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
216 checkError: expectNoError,
217 },
218 {
219 name: "non-deleter, create, no objectref change",
220 username: "non-deleter",
221 resource: api.SchemeGroupVersion.WithResource("pods"),
222 newObj: &api.Pod{},
223 checkError: expectNoError,
224 },
225 {
226 name: "non-deleter, create, objectref change",
227 username: "non-deleter",
228 resource: api.SchemeGroupVersion.WithResource("pods"),
229 newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
230 checkError: expectNoError,
231 },
232 {
233 name: "non-pod-deleter, create, no objectref change",
234 username: "non-pod-deleter",
235 resource: api.SchemeGroupVersion.WithResource("pods"),
236 newObj: &api.Pod{},
237 checkError: expectNoError,
238 },
239 {
240 name: "non-pod-deleter, create, objectref change",
241 username: "non-pod-deleter",
242 resource: api.SchemeGroupVersion.WithResource("pods"),
243 newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
244 checkError: expectNoError,
245 },
246 {
247 name: "non-pod-deleter, create, objectref change, but not a pod",
248 username: "non-pod-deleter",
249 resource: api.SchemeGroupVersion.WithResource("not-pods"),
250 newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
251 checkError: expectNoError,
252 },
253
254 {
255 name: "super-user, update, no objectref change",
256 username: "super",
257 resource: api.SchemeGroupVersion.WithResource("pods"),
258 oldObj: &api.Pod{},
259 newObj: &api.Pod{},
260 checkError: expectNoError,
261 },
262 {
263 name: "super-user, update, no objectref change two",
264 username: "super",
265 resource: api.SchemeGroupVersion.WithResource("pods"),
266 oldObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
267 newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
268 checkError: expectNoError,
269 },
270 {
271 name: "super-user, update, objectref change",
272 username: "super",
273 resource: api.SchemeGroupVersion.WithResource("pods"),
274 oldObj: &api.Pod{},
275 newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
276 checkError: expectNoError,
277 },
278 {
279 name: "non-deleter, update, no objectref change",
280 username: "non-deleter",
281 resource: api.SchemeGroupVersion.WithResource("pods"),
282 oldObj: &api.Pod{},
283 newObj: &api.Pod{},
284 checkError: expectNoError,
285 },
286 {
287 name: "non-deleter, update, no objectref change two",
288 username: "non-deleter",
289 resource: api.SchemeGroupVersion.WithResource("pods"),
290 oldObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
291 newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
292 checkError: expectNoError,
293 },
294 {
295 name: "non-deleter, update, objectref change",
296 username: "non-deleter",
297 resource: api.SchemeGroupVersion.WithResource("pods"),
298 oldObj: &api.Pod{},
299 newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
300 checkError: expectCantSetOwnerRefError,
301 },
302 {
303 name: "non-deleter, update, objectref change two",
304 username: "non-deleter",
305 resource: api.SchemeGroupVersion.WithResource("pods"),
306 oldObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
307 newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}, {Name: "second"}}}},
308 checkError: expectCantSetOwnerRefError,
309 },
310 {
311 name: "non-pod-deleter, update, no objectref change",
312 username: "non-pod-deleter",
313 resource: api.SchemeGroupVersion.WithResource("pods"),
314 oldObj: &api.Pod{},
315 newObj: &api.Pod{},
316 checkError: expectNoError,
317 },
318 {
319 name: "non-pod-deleter, update status, objectref change",
320 username: "non-pod-deleter",
321 resource: api.SchemeGroupVersion.WithResource("pods"),
322 subresource: "status",
323 oldObj: &api.Pod{},
324 newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
325 checkError: expectNoError,
326 },
327 {
328 name: "non-pod-deleter, update, objectref change",
329 username: "non-pod-deleter",
330 resource: api.SchemeGroupVersion.WithResource("pods"),
331 oldObj: &api.Pod{},
332 newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
333 checkError: expectCantSetOwnerRefError,
334 },
335 {
336 name: "non-pod-deleter, update, objectref change, but not a pod",
337 username: "non-pod-deleter",
338 resource: api.SchemeGroupVersion.WithResource("not-pods"),
339 oldObj: &api.Pod{},
340 newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
341 checkError: expectNoError,
342 },
343 }
344
345 for _, tc := range tests {
346 t.Run(tc.name, func(t *testing.T) {
347 gcAdmit, err := newGCPermissionsEnforcement()
348 if err != nil {
349 t.Error(err)
350 }
351
352 operation := admission.Create
353 var options runtime.Object = &metav1.CreateOptions{}
354 if tc.oldObj != nil {
355 operation = admission.Update
356 options = &metav1.UpdateOptions{}
357 }
358 user := &user.DefaultInfo{Name: tc.username}
359 attributes := admission.NewAttributesRecord(tc.newObj, tc.oldObj, schema.GroupVersionKind{}, metav1.NamespaceDefault, "foo", tc.resource, tc.subresource, operation, options, false, user)
360
361 err = gcAdmit.Validate(context.TODO(), attributes, nil)
362 if !tc.checkError(err) {
363 t.Errorf("unexpected err: %v", err)
364 }
365 })
366 }
367 }
368
369 func TestBlockOwnerDeletionAdmission(t *testing.T) {
370 podWithOwnerRefs := func(refs ...metav1.OwnerReference) *api.Pod {
371 var refSlice []metav1.OwnerReference
372 refSlice = append(refSlice, refs...)
373
374 return &api.Pod{
375 ObjectMeta: metav1.ObjectMeta{
376 OwnerReferences: refSlice,
377 },
378 }
379 }
380
381 getTrueVar := func() *bool {
382 ret := true
383 return &ret
384 }
385
386 getFalseVar := func() *bool {
387 ret := false
388 return &ret
389 }
390 blockRC1 := metav1.OwnerReference{
391 APIVersion: "v1",
392 Kind: "ReplicationController",
393 Name: "rc1",
394 BlockOwnerDeletion: getTrueVar(),
395 }
396 blockRC2 := metav1.OwnerReference{
397 APIVersion: "v1",
398 Kind: "ReplicationController",
399 Name: "rc2",
400 BlockOwnerDeletion: getTrueVar(),
401 }
402 notBlockRC1 := metav1.OwnerReference{
403 APIVersion: "v1",
404 Kind: "ReplicationController",
405 Name: "rc1",
406 BlockOwnerDeletion: getFalseVar(),
407 }
408 notBlockRC2 := metav1.OwnerReference{
409 APIVersion: "v1",
410 Kind: "ReplicationController",
411 Name: "rc2",
412 BlockOwnerDeletion: getFalseVar(),
413 }
414 nilBlockRC1 := metav1.OwnerReference{
415 APIVersion: "v1",
416 Kind: "ReplicationController",
417 Name: "rc1",
418 }
419 nilBlockRC2 := metav1.OwnerReference{
420 APIVersion: "v1",
421 Kind: "ReplicationController",
422 Name: "rc2",
423 }
424 blockDS1 := metav1.OwnerReference{
425 APIVersion: "apps/v1",
426 Kind: "DaemonSet",
427 Name: "ds1",
428 BlockOwnerDeletion: getTrueVar(),
429 }
430 notBlockDS1 := metav1.OwnerReference{
431 APIVersion: "apps/v1",
432 Kind: "DaemonSet",
433 Name: "ds1",
434 BlockOwnerDeletion: getFalseVar(),
435 }
436 blockNode := metav1.OwnerReference{
437 APIVersion: "v1",
438 Kind: "Node",
439 Name: "node1",
440 BlockOwnerDeletion: getTrueVar(),
441 }
442 notBlockNode := metav1.OwnerReference{
443 APIVersion: "v1",
444 Kind: "Node",
445 Name: "node",
446 BlockOwnerDeletion: getFalseVar(),
447 }
448 nilBlockNode := metav1.OwnerReference{
449 APIVersion: "v1",
450 Kind: "Node",
451 Name: "node",
452 }
453
454 expectNoError := func(err error) bool {
455 return err == nil
456 }
457 expectCantSetBlockOwnerDeletionError := func(err error) bool {
458 if err == nil {
459 return false
460 }
461 return strings.Contains(err.Error(), "cannot set blockOwnerDeletion if an ownerReference refers to a resource you can't set finalizers on")
462 }
463 tests := []struct {
464 name string
465 username string
466 resource schema.GroupVersionResource
467 subresource string
468 oldObj runtime.Object
469 newObj runtime.Object
470 restMapperOverride meta.RESTMapper
471
472 checkError func(error) bool
473 }{
474
475 {
476 name: "super-user, create, no ownerReferences",
477 username: "super",
478 resource: api.SchemeGroupVersion.WithResource("pods"),
479 newObj: podWithOwnerRefs(),
480 checkError: expectNoError,
481 },
482 {
483 name: "super-user, create, all ownerReferences have blockOwnerDeletion=false",
484 username: "super",
485 resource: api.SchemeGroupVersion.WithResource("pods"),
486 newObj: podWithOwnerRefs(notBlockRC1, notBlockRC2),
487 checkError: expectNoError,
488 },
489 {
490 name: "super-user, create, some ownerReferences have blockOwnerDeletion=true",
491 username: "super",
492 resource: api.SchemeGroupVersion.WithResource("pods"),
493 newObj: podWithOwnerRefs(blockRC1, blockRC2, blockNode),
494 checkError: expectNoError,
495 },
496 {
497 name: "super-user, create, some ownerReferences have blockOwnerDeletion=true, hangingRESTMapper",
498 username: "super",
499 resource: api.SchemeGroupVersion.WithResource("pods"),
500 newObj: podWithOwnerRefs(blockRC1, blockRC2, blockNode),
501 restMapperOverride: &neverReturningRESTMapper{},
502 checkError: expectNoError,
503 },
504 {
505 name: "non-rc-deleter, create, no ownerReferences",
506 username: "non-rc-deleter",
507 resource: api.SchemeGroupVersion.WithResource("pods"),
508 newObj: podWithOwnerRefs(),
509 checkError: expectNoError,
510 },
511 {
512 name: "non-rc-deleter, create, all ownerReferences have blockOwnerDeletion=false or nil",
513 username: "non-rc-deleter",
514 resource: api.SchemeGroupVersion.WithResource("pods"),
515 newObj: podWithOwnerRefs(notBlockRC1, nilBlockRC2),
516 checkError: expectNoError,
517 },
518 {
519 name: "non-node-deleter, create, all ownerReferences have blockOwnerDeletion=false",
520 username: "non-node-deleter",
521 resource: api.SchemeGroupVersion.WithResource("pods"),
522 newObj: podWithOwnerRefs(notBlockNode),
523 checkError: expectNoError,
524 },
525 {
526 name: "non-rc-deleter, create, some ownerReferences have blockOwnerDeletion=true",
527 username: "non-rc-deleter",
528 resource: api.SchemeGroupVersion.WithResource("pods"),
529 newObj: podWithOwnerRefs(blockRC1, notBlockRC2),
530 checkError: expectCantSetBlockOwnerDeletionError,
531 },
532 {
533 name: "non-rc-deleter, create, some ownerReferences have blockOwnerDeletion=true, but are pointing to daemonset",
534 username: "non-rc-deleter",
535 resource: api.SchemeGroupVersion.WithResource("pods"),
536 newObj: podWithOwnerRefs(blockDS1),
537 checkError: expectNoError,
538 },
539 {
540 name: "non-node-deleter, create, some ownerReferences have blockOwnerDeletion=true",
541 username: "non-node-deleter",
542 resource: api.SchemeGroupVersion.WithResource("pods"),
543 newObj: podWithOwnerRefs(blockNode),
544 checkError: expectCantSetBlockOwnerDeletionError,
545 },
546
547 {
548 name: "super-user, update, no ownerReferences change blockOwnerDeletion",
549 username: "super",
550 resource: api.SchemeGroupVersion.WithResource("pods"),
551 oldObj: podWithOwnerRefs(nilBlockRC1, nilBlockNode),
552 newObj: podWithOwnerRefs(notBlockRC1, notBlockNode),
553 checkError: expectNoError,
554 },
555 {
556 name: "super-user, update, some ownerReferences change to blockOwnerDeletion=true",
557 username: "super",
558 resource: api.SchemeGroupVersion.WithResource("pods"),
559 oldObj: podWithOwnerRefs(notBlockRC1, notBlockNode),
560 newObj: podWithOwnerRefs(blockRC1, blockNode),
561 checkError: expectNoError,
562 },
563 {
564 name: "super-user, update, add new ownerReferences with blockOwnerDeletion=true",
565 username: "super",
566 resource: api.SchemeGroupVersion.WithResource("pods"),
567 oldObj: podWithOwnerRefs(),
568 newObj: podWithOwnerRefs(blockRC1, blockNode),
569 checkError: expectNoError,
570 },
571 {
572 name: "non-rc-deleter, update, no ownerReferences change blockOwnerDeletion",
573 username: "non-rc-deleter",
574 resource: api.SchemeGroupVersion.WithResource("pods"),
575 oldObj: podWithOwnerRefs(nilBlockRC1),
576 newObj: podWithOwnerRefs(notBlockRC1),
577 checkError: expectNoError,
578 },
579 {
580 name: "non-rc-deleter, update, some ownerReferences change from blockOwnerDeletion=false to true",
581 username: "non-rc-deleter",
582 resource: api.SchemeGroupVersion.WithResource("pods"),
583 oldObj: podWithOwnerRefs(notBlockRC1),
584 newObj: podWithOwnerRefs(blockRC1),
585 checkError: expectCantSetBlockOwnerDeletionError,
586 },
587 {
588 name: "non-rc-deleter, update, some ownerReferences change from blockOwnerDeletion=nil to true",
589 username: "non-rc-deleter",
590 resource: api.SchemeGroupVersion.WithResource("pods"),
591 oldObj: podWithOwnerRefs(nilBlockRC1),
592 newObj: podWithOwnerRefs(blockRC1),
593 checkError: expectCantSetBlockOwnerDeletionError,
594 },
595 {
596 name: "non-node-deleter, update, some ownerReferences change from blockOwnerDeletion=nil to true",
597 username: "non-node-deleter",
598 resource: api.SchemeGroupVersion.WithResource("pods"),
599 oldObj: podWithOwnerRefs(nilBlockNode),
600 newObj: podWithOwnerRefs(blockNode),
601 checkError: expectCantSetBlockOwnerDeletionError,
602 },
603 {
604 name: "non-rc-deleter, update, some ownerReferences change from blockOwnerDeletion=true to false",
605 username: "non-rc-deleter",
606 resource: api.SchemeGroupVersion.WithResource("pods"),
607 oldObj: podWithOwnerRefs(blockRC1),
608 newObj: podWithOwnerRefs(notBlockRC1),
609 checkError: expectNoError,
610 },
611 {
612 name: "non-node-deleter, update, some ownerReferences change from blockOwnerDeletion=true to false",
613 username: "non-node-deleter",
614 resource: api.SchemeGroupVersion.WithResource("pods"),
615 oldObj: podWithOwnerRefs(blockNode),
616 newObj: podWithOwnerRefs(notBlockNode),
617 checkError: expectNoError,
618 },
619 {
620 name: "non-rc-deleter, update, some ownerReferences change blockOwnerDeletion, but all such references are to daemonset",
621 username: "non-rc-deleter",
622 resource: api.SchemeGroupVersion.WithResource("pods"),
623 oldObj: podWithOwnerRefs(notBlockDS1),
624 newObj: podWithOwnerRefs(blockDS1),
625 checkError: expectNoError,
626 },
627 {
628 name: "non-rc-deleter, update, add new ownerReferences with blockOwnerDeletion=nil or false",
629 username: "non-rc-deleter",
630 resource: api.SchemeGroupVersion.WithResource("pods"),
631 oldObj: podWithOwnerRefs(),
632 newObj: podWithOwnerRefs(notBlockRC1, nilBlockRC2),
633 checkError: expectNoError,
634 },
635 {
636 name: "non-rc-deleter, update, add new ownerReferences with blockOwnerDeletion=true",
637 username: "non-rc-deleter",
638 resource: api.SchemeGroupVersion.WithResource("pods"),
639 oldObj: podWithOwnerRefs(),
640 newObj: podWithOwnerRefs(blockRC1),
641 checkError: expectCantSetBlockOwnerDeletionError,
642 },
643 {
644 name: "non-rc-deleter, update, add new ownerReferences with blockOwnerDeletion=true, but the references are to daemonset",
645 username: "non-rc-deleter",
646 resource: api.SchemeGroupVersion.WithResource("pods"),
647 oldObj: podWithOwnerRefs(),
648 newObj: podWithOwnerRefs(blockDS1),
649 checkError: expectNoError,
650 },
651 {
652 name: "non-node-deleter, update, add ownerReferences with blockOwnerDeletion=true",
653 username: "non-node-deleter",
654 resource: api.SchemeGroupVersion.WithResource("pods"),
655 oldObj: podWithOwnerRefs(),
656 newObj: podWithOwnerRefs(blockNode),
657 checkError: expectCantSetBlockOwnerDeletionError,
658 },
659 }
660
661 for _, tc := range tests {
662 t.Run(tc.name, func(t *testing.T) {
663 gcAdmit, err := newGCPermissionsEnforcement()
664 if err != nil {
665 t.Fatal(err)
666 }
667 if tc.restMapperOverride != nil {
668 gcAdmit.restMapper = tc.restMapperOverride
669 }
670
671 operation := admission.Create
672 var options runtime.Object = &metav1.CreateOptions{}
673 if tc.oldObj != nil {
674 operation = admission.Update
675 options = &metav1.UpdateOptions{}
676 }
677 user := &user.DefaultInfo{Name: tc.username}
678 attributes := admission.NewAttributesRecord(tc.newObj, tc.oldObj, schema.GroupVersionKind{}, metav1.NamespaceDefault, "foo", tc.resource, tc.subresource, operation, options, false, user)
679
680 err = gcAdmit.Validate(context.TODO(), attributes, nil)
681 if !tc.checkError(err) {
682 t.Fatal(err)
683 }
684 })
685 }
686 }
687
View as plain text