k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/plugin/pkg/admission/gc/gc_admission_test.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 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 apiserveradmission "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 39 api "k8s.io/kubernetes/pkg/apis/core" 40 controlplaneadmission "k8s.io/kubernetes/pkg/controlplane/apiserver/admission" 41 ) 42 43 type fakeAuthorizer struct{} 44 45 func (fakeAuthorizer) Authorize(ctx context.Context, a authorizer.Attributes) (authorizer.Decision, string, error) { 46 username := a.GetUser().GetName() 47 48 if username == "non-deleter" { 49 if a.GetVerb() == "delete" { 50 return authorizer.DecisionNoOpinion, "", nil 51 } 52 if a.GetVerb() == "update" && a.GetSubresource() == "finalizers" { 53 return authorizer.DecisionNoOpinion, "", nil 54 } 55 if a.GetAPIGroup() == "*" && a.GetResource() == "*" { // this user does not have full rights 56 return authorizer.DecisionNoOpinion, "", nil 57 } 58 return authorizer.DecisionAllow, "", nil 59 } 60 61 if username == "non-pod-deleter" { 62 if a.GetVerb() == "delete" && a.GetResource() == "pods" { 63 return authorizer.DecisionNoOpinion, "", nil 64 } 65 if a.GetVerb() == "update" && a.GetResource() == "pods" && a.GetSubresource() == "finalizers" { 66 return authorizer.DecisionNoOpinion, "", nil 67 } 68 if a.GetAPIGroup() == "*" && a.GetResource() == "*" { // this user does not have full rights 69 return authorizer.DecisionNoOpinion, "", nil 70 } 71 return authorizer.DecisionAllow, "", nil 72 } 73 74 if username == "non-rc-deleter" { 75 if a.GetVerb() == "delete" && a.GetResource() == "replicationcontrollers" { 76 return authorizer.DecisionNoOpinion, "", nil 77 } 78 if a.GetVerb() == "update" && a.GetResource() == "replicationcontrollers" && a.GetSubresource() == "finalizers" { 79 return authorizer.DecisionNoOpinion, "", nil 80 } 81 if a.GetAPIGroup() == "*" && a.GetResource() == "*" { // this user does not have full rights 82 return authorizer.DecisionNoOpinion, "", nil 83 } 84 return authorizer.DecisionAllow, "", nil 85 } 86 87 if username == "non-node-deleter" { 88 if a.GetVerb() == "delete" && a.GetResource() == "nodes" { 89 return authorizer.DecisionNoOpinion, "", nil 90 } 91 if a.GetVerb() == "update" && a.GetResource() == "nodes" && a.GetSubresource() == "finalizers" { 92 return authorizer.DecisionNoOpinion, "", nil 93 } 94 if a.GetAPIGroup() == "*" && a.GetResource() == "*" { // this user does not have full rights 95 return authorizer.DecisionNoOpinion, "", nil 96 } 97 return authorizer.DecisionAllow, "", nil 98 } 99 100 return authorizer.DecisionAllow, "", nil 101 } 102 103 // newGCPermissionsEnforcement returns the admission controller configured for testing. 104 func newGCPermissionsEnforcement() (*gcPermissionsEnforcement, error) { 105 // the pods/status endpoint is ignored by this plugin since old kubelets 106 // corrupt them. the pod status strategy ensures status updates cannot mutate 107 // ownerRef. 108 whiteList := []whiteListItem{ 109 { 110 groupResource: schema.GroupResource{Resource: "pods"}, 111 subresource: "status", 112 }, 113 } 114 gcAdmit := &gcPermissionsEnforcement{ 115 Handler: apiserveradmission.NewHandler(apiserveradmission.Create, apiserveradmission.Update), 116 whiteList: whiteList, 117 } 118 119 fakeDiscoveryClient := &fakediscovery.FakeDiscovery{Fake: &coretesting.Fake{}} 120 fakeDiscoveryClient.Resources = []*metav1.APIResourceList{ 121 { 122 GroupVersion: corev1.SchemeGroupVersion.String(), 123 APIResources: []metav1.APIResource{ 124 {Name: "nodes", Namespaced: false, Kind: "Node"}, 125 {Name: "pods", Namespaced: true, Kind: "Pod"}, 126 {Name: "replicationcontrollers", Namespaced: true, Kind: "ReplicationController"}, 127 }, 128 }, 129 { 130 GroupVersion: appsv1.SchemeGroupVersion.String(), 131 APIResources: []metav1.APIResource{ 132 {Name: "daemonsets", Namespaced: true, Kind: "DaemonSet"}, 133 }, 134 }, 135 } 136 restMapperRes, err := restmapper.GetAPIGroupResources(fakeDiscoveryClient) 137 if err != nil { 138 return nil, fmt.Errorf("unexpected error while constructing resource list from fake discovery client: %v", err) 139 } 140 restMapper := restmapper.NewDiscoveryRESTMapper(restMapperRes) 141 genericPluginInitializer := initializer.New(nil, nil, nil, fakeAuthorizer{}, nil, nil, restMapper) 142 pluginInitializer := controlplaneadmission.NewPluginInitializer(nil, nil) 143 initializersChain := apiserveradmission.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 // this ok because if the test works, this method should never be called. 157 panic("test failed") 158 } 159 func (r *neverReturningRESTMapper) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) { 160 // this ok because if the test works, this method should never be called. 161 panic("test failed") 162 } 163 func (r *neverReturningRESTMapper) ResourceFor(input schema.GroupVersionResource) (schema.GroupVersionResource, error) { 164 // this ok because if the test works, this method should never be called. 165 panic("test failed") 166 } 167 func (r *neverReturningRESTMapper) ResourcesFor(input schema.GroupVersionResource) ([]schema.GroupVersionResource, error) { 168 // this ok because if the test works, this method should never be called. 169 panic("test failed") 170 } 171 func (r *neverReturningRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) { 172 // this ok because if the test works, this method should never be called. 173 panic("test failed") 174 } 175 func (r *neverReturningRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*meta.RESTMapping, error) { 176 // this ok because if the test works, this method should never be called. 177 panic("test failed") 178 } 179 func (r *neverReturningRESTMapper) ResourceSingularizer(resource string) (singular string, err error) { 180 // this ok because if the test works, this method should never be called. 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 := apiserveradmission.Create 353 var options runtime.Object = &metav1.CreateOptions{} 354 if tc.oldObj != nil { 355 operation = apiserveradmission.Update 356 options = &metav1.UpdateOptions{} 357 } 358 user := &user.DefaultInfo{Name: tc.username} 359 attributes := apiserveradmission.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 // cases for create 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 // cases are for update 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 := apiserveradmission.Create 672 var options runtime.Object = &metav1.CreateOptions{} 673 if tc.oldObj != nil { 674 operation = apiserveradmission.Update 675 options = &metav1.UpdateOptions{} 676 } 677 user := &user.DefaultInfo{Name: tc.username} 678 attributes := apiserveradmission.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 }