k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/integration/garbagecollector/garbage_collector_test.go (about) 1 /* 2 Copyright 2015 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 garbagecollector 18 19 import ( 20 "context" 21 "fmt" 22 "strconv" 23 "strings" 24 "sync" 25 "testing" 26 "time" 27 28 v1 "k8s.io/api/core/v1" 29 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 30 apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" 31 apiextensionstestserver "k8s.io/apiextensions-apiserver/test/integration/fixtures" 32 apierrors "k8s.io/apimachinery/pkg/api/errors" 33 "k8s.io/apimachinery/pkg/api/meta" 34 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 35 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 36 "k8s.io/apimachinery/pkg/runtime/schema" 37 "k8s.io/apimachinery/pkg/types" 38 "k8s.io/apimachinery/pkg/util/wait" 39 "k8s.io/apiserver/pkg/storage/names" 40 cacheddiscovery "k8s.io/client-go/discovery/cached/memory" 41 "k8s.io/client-go/dynamic" 42 "k8s.io/client-go/informers" 43 clientset "k8s.io/client-go/kubernetes" 44 "k8s.io/client-go/metadata" 45 "k8s.io/client-go/metadata/metadatainformer" 46 "k8s.io/client-go/restmapper" 47 "k8s.io/client-go/tools/cache" 48 "k8s.io/controller-manager/pkg/informerfactory" 49 "k8s.io/klog/v2" 50 kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing" 51 "k8s.io/kubernetes/pkg/controller/garbagecollector" 52 "k8s.io/kubernetes/test/integration" 53 "k8s.io/kubernetes/test/integration/framework" 54 "k8s.io/kubernetes/test/utils/ktesting" 55 "k8s.io/utils/ptr" 56 ) 57 58 func getForegroundOptions() metav1.DeleteOptions { 59 policy := metav1.DeletePropagationForeground 60 return metav1.DeleteOptions{PropagationPolicy: &policy} 61 } 62 63 func getOrphanOptions() metav1.DeleteOptions { 64 var trueVar = true 65 return metav1.DeleteOptions{OrphanDependents: &trueVar} 66 } 67 68 func getPropagateOrphanOptions() metav1.DeleteOptions { 69 policy := metav1.DeletePropagationOrphan 70 return metav1.DeleteOptions{PropagationPolicy: &policy} 71 } 72 73 func getNonOrphanOptions() metav1.DeleteOptions { 74 var falseVar = false 75 return metav1.DeleteOptions{OrphanDependents: &falseVar} 76 } 77 78 const garbageCollectedPodName = "test.pod.1" 79 const independentPodName = "test.pod.2" 80 const oneValidOwnerPodName = "test.pod.3" 81 const toBeDeletedRCName = "test.rc.1" 82 const remainingRCName = "test.rc.2" 83 84 func newPod(podName, podNamespace string, ownerReferences []metav1.OwnerReference) *v1.Pod { 85 for i := 0; i < len(ownerReferences); i++ { 86 if len(ownerReferences[i].Kind) == 0 { 87 ownerReferences[i].Kind = "ReplicationController" 88 } 89 ownerReferences[i].APIVersion = "v1" 90 } 91 return &v1.Pod{ 92 TypeMeta: metav1.TypeMeta{ 93 Kind: "Pod", 94 APIVersion: "v1", 95 }, 96 ObjectMeta: metav1.ObjectMeta{ 97 Name: podName, 98 Namespace: podNamespace, 99 OwnerReferences: ownerReferences, 100 }, 101 Spec: v1.PodSpec{ 102 Containers: []v1.Container{ 103 { 104 Name: "fake-name", 105 Image: "fakeimage", 106 }, 107 }, 108 }, 109 } 110 } 111 112 func newOwnerRC(name, namespace string) *v1.ReplicationController { 113 return &v1.ReplicationController{ 114 TypeMeta: metav1.TypeMeta{ 115 Kind: "ReplicationController", 116 APIVersion: "v1", 117 }, 118 ObjectMeta: metav1.ObjectMeta{ 119 Namespace: namespace, 120 Name: name, 121 }, 122 Spec: v1.ReplicationControllerSpec{ 123 Selector: map[string]string{"name": "test"}, 124 Template: &v1.PodTemplateSpec{ 125 ObjectMeta: metav1.ObjectMeta{ 126 Labels: map[string]string{"name": "test"}, 127 }, 128 Spec: v1.PodSpec{ 129 Containers: []v1.Container{ 130 { 131 Name: "fake-name", 132 Image: "fakeimage", 133 }, 134 }, 135 }, 136 }, 137 }, 138 } 139 } 140 141 func newCRDInstance(definition *apiextensionsv1.CustomResourceDefinition, namespace, name string) *unstructured.Unstructured { 142 return &unstructured.Unstructured{ 143 Object: map[string]interface{}{ 144 "kind": definition.Spec.Names.Kind, 145 "apiVersion": definition.Spec.Group + "/" + definition.Spec.Versions[0].Name, 146 "metadata": map[string]interface{}{ 147 "name": name, 148 "namespace": namespace, 149 }, 150 }, 151 } 152 } 153 154 func newConfigMap(namespace, name string) *v1.ConfigMap { 155 return &v1.ConfigMap{ 156 TypeMeta: metav1.TypeMeta{ 157 Kind: "ConfigMap", 158 APIVersion: "v1", 159 }, 160 ObjectMeta: metav1.ObjectMeta{ 161 Namespace: namespace, 162 Name: name, 163 }, 164 } 165 } 166 167 func link(t *testing.T, owner, dependent metav1.Object) { 168 ownerType, err := meta.TypeAccessor(owner) 169 if err != nil { 170 t.Fatalf("failed to get type info for %#v: %v", owner, err) 171 } 172 ref := metav1.OwnerReference{ 173 Kind: ownerType.GetKind(), 174 APIVersion: ownerType.GetAPIVersion(), 175 Name: owner.GetName(), 176 UID: owner.GetUID(), 177 } 178 dependent.SetOwnerReferences(append(dependent.GetOwnerReferences(), ref)) 179 } 180 181 func createRandomCustomResourceDefinition( 182 t *testing.T, apiExtensionClient apiextensionsclientset.Interface, 183 dynamicClient dynamic.Interface, 184 namespace string, 185 ) (*apiextensionsv1.CustomResourceDefinition, dynamic.ResourceInterface) { 186 // Create a random custom resource definition and ensure it's available for 187 // use. 188 definition := apiextensionstestserver.NewRandomNameV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped) 189 190 definition, err := apiextensionstestserver.CreateNewV1CustomResourceDefinition(definition, apiExtensionClient, dynamicClient) 191 if err != nil { 192 t.Fatalf("failed to create CustomResourceDefinition: %v", err) 193 } 194 195 // Get a client for the custom resource. 196 gvr := schema.GroupVersionResource{Group: definition.Spec.Group, Version: definition.Spec.Versions[0].Name, Resource: definition.Spec.Names.Plural} 197 198 resourceClient := dynamicClient.Resource(gvr).Namespace(namespace) 199 200 return definition, resourceClient 201 } 202 203 type testContext struct { 204 logger klog.Logger 205 tearDown func() 206 gc *garbagecollector.GarbageCollector 207 clientSet clientset.Interface 208 apiExtensionClient apiextensionsclientset.Interface 209 dynamicClient dynamic.Interface 210 metadataClient metadata.Interface 211 startGC func(workers int) 212 // syncPeriod is how often the GC started with startGC will be resynced. 213 syncPeriod time.Duration 214 } 215 216 // if workerCount > 0, will start the GC, otherwise it's up to the caller to Run() the GC. 217 func setup(t *testing.T, workerCount int) *testContext { 218 return setupWithServer(t, kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd()), workerCount) 219 } 220 221 func setupWithServer(t *testing.T, result *kubeapiservertesting.TestServer, workerCount int) *testContext { 222 clientSet, err := clientset.NewForConfig(result.ClientConfig) 223 if err != nil { 224 t.Fatalf("error creating clientset: %v", err) 225 } 226 227 // Helpful stuff for testing CRD. 228 apiExtensionClient, err := apiextensionsclientset.NewForConfig(result.ClientConfig) 229 if err != nil { 230 t.Fatalf("error creating extension clientset: %v", err) 231 } 232 // CreateCRDUsingRemovedAPI wants to use this namespace for verifying 233 // namespace-scoped CRD creation. 234 createNamespaceOrDie("aval", clientSet, t) 235 236 discoveryClient := cacheddiscovery.NewMemCacheClient(clientSet.Discovery()) 237 restMapper := restmapper.NewDeferredDiscoveryRESTMapper(discoveryClient) 238 restMapper.Reset() 239 config := *result.ClientConfig 240 metadataClient, err := metadata.NewForConfig(&config) 241 if err != nil { 242 t.Fatalf("failed to create metadataClient: %v", err) 243 } 244 dynamicClient, err := dynamic.NewForConfig(&config) 245 if err != nil { 246 t.Fatalf("failed to create dynamicClient: %v", err) 247 } 248 sharedInformers := informers.NewSharedInformerFactory(clientSet, 0) 249 metadataInformers := metadatainformer.NewSharedInformerFactory(metadataClient, 0) 250 251 tCtx := ktesting.Init(t) 252 logger := tCtx.Logger() 253 alwaysStarted := make(chan struct{}) 254 close(alwaysStarted) 255 gc, err := garbagecollector.NewGarbageCollector( 256 tCtx, 257 clientSet, 258 metadataClient, 259 restMapper, 260 garbagecollector.DefaultIgnoredResources(), 261 informerfactory.NewInformerFactory(sharedInformers, metadataInformers), 262 alwaysStarted, 263 ) 264 if err != nil { 265 t.Fatalf("failed to create garbage collector: %v", err) 266 } 267 268 tearDown := func() { 269 tCtx.Cancel("tearing down") 270 result.TearDownFn() 271 } 272 syncPeriod := 5 * time.Second 273 startGC := func(workers int) { 274 go wait.Until(func() { 275 // Resetting the REST mapper will also invalidate the underlying discovery 276 // client. This is a leaky abstraction and assumes behavior about the REST 277 // mapper, but we'll deal with it for now. 278 restMapper.Reset() 279 }, syncPeriod, tCtx.Done()) 280 go gc.Run(tCtx, workers) 281 go gc.Sync(tCtx, clientSet.Discovery(), syncPeriod) 282 } 283 284 if workerCount > 0 { 285 startGC(workerCount) 286 } 287 288 return &testContext{ 289 logger: logger, 290 tearDown: tearDown, 291 gc: gc, 292 clientSet: clientSet, 293 apiExtensionClient: apiExtensionClient, 294 dynamicClient: dynamicClient, 295 metadataClient: metadataClient, 296 startGC: startGC, 297 syncPeriod: syncPeriod, 298 } 299 } 300 301 func createNamespaceOrDie(name string, c clientset.Interface, t *testing.T) *v1.Namespace { 302 ns := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: name}} 303 if _, err := c.CoreV1().Namespaces().Create(context.TODO(), ns, metav1.CreateOptions{}); err != nil { 304 t.Fatalf("failed to create namespace: %v", err) 305 } 306 falseVar := false 307 _, err := c.CoreV1().ServiceAccounts(ns.Name).Create(context.TODO(), &v1.ServiceAccount{ 308 ObjectMeta: metav1.ObjectMeta{Name: "default"}, 309 AutomountServiceAccountToken: &falseVar, 310 }, metav1.CreateOptions{}) 311 if err != nil { 312 t.Fatalf("failed to create service account: %v", err) 313 } 314 return ns 315 } 316 317 func deleteNamespaceOrDie(name string, c clientset.Interface, t *testing.T) { 318 zero := int64(0) 319 background := metav1.DeletePropagationBackground 320 err := c.CoreV1().Namespaces().Delete(context.TODO(), name, metav1.DeleteOptions{GracePeriodSeconds: &zero, PropagationPolicy: &background}) 321 if err != nil { 322 t.Fatalf("failed to delete namespace %q: %v", name, err) 323 } 324 } 325 326 func TestCrossNamespaceReferencesWithWatchCache(t *testing.T) { 327 testCrossNamespaceReferences(t, true) 328 } 329 func TestCrossNamespaceReferencesWithoutWatchCache(t *testing.T) { 330 testCrossNamespaceReferences(t, false) 331 } 332 333 func testCrossNamespaceReferences(t *testing.T, watchCache bool) { 334 var ( 335 workers = 5 336 validChildrenCount = 10 337 namespaceB = "b" 338 namespaceA = "a" 339 ) 340 341 // Start the server 342 testServer := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{fmt.Sprintf("--watch-cache=%v", watchCache)}, framework.SharedEtcd()) 343 defer func() { 344 if testServer != nil { 345 testServer.TearDownFn() 346 } 347 }() 348 clientSet, err := clientset.NewForConfig(testServer.ClientConfig) 349 if err != nil { 350 t.Fatalf("error creating clientset: %v", err) 351 } 352 353 createNamespaceOrDie(namespaceB, clientSet, t) 354 parent, err := clientSet.CoreV1().ConfigMaps(namespaceB).Create(context.TODO(), &v1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "parent"}}, metav1.CreateOptions{}) 355 if err != nil { 356 t.Fatal(err) 357 } 358 for i := 0; i < validChildrenCount; i++ { 359 _, err := clientSet.CoreV1().Secrets(namespaceB).Create(context.TODO(), &v1.Secret{ObjectMeta: metav1.ObjectMeta{GenerateName: "child-", OwnerReferences: []metav1.OwnerReference{ 360 {Name: "parent", Kind: "ConfigMap", APIVersion: "v1", UID: parent.UID, Controller: ptr.To(false)}, 361 }}}, metav1.CreateOptions{}) 362 if err != nil { 363 t.Fatal(err) 364 } 365 } 366 367 createNamespaceOrDie(namespaceA, clientSet, t) 368 369 // Construct invalid owner references: 370 invalidOwnerReferences := []metav1.OwnerReference{} 371 for i := 0; i < 25; i++ { 372 invalidOwnerReferences = append(invalidOwnerReferences, metav1.OwnerReference{Name: "invalid", UID: types.UID(fmt.Sprintf("invalid-%d", i)), APIVersion: "test/v1", Kind: fmt.Sprintf("invalid%d", i)}) 373 } 374 invalidOwnerReferences = append(invalidOwnerReferences, metav1.OwnerReference{Name: "invalid", UID: parent.UID, APIVersion: "v1", Kind: "Pod", Controller: ptr.To(false)}) 375 376 for i := 0; i < workers; i++ { 377 _, err := clientSet.CoreV1().ConfigMaps(namespaceA).Create(context.TODO(), &v1.ConfigMap{ObjectMeta: metav1.ObjectMeta{GenerateName: "invalid-child-", OwnerReferences: invalidOwnerReferences}}, metav1.CreateOptions{}) 378 if err != nil { 379 t.Fatal(err) 380 } 381 _, err = clientSet.CoreV1().Secrets(namespaceA).Create(context.TODO(), &v1.Secret{ObjectMeta: metav1.ObjectMeta{GenerateName: "invalid-child-a-", OwnerReferences: invalidOwnerReferences}}, metav1.CreateOptions{}) 382 if err != nil { 383 t.Fatal(err) 384 } 385 _, err = clientSet.CoreV1().Secrets(namespaceA).Create(context.TODO(), &v1.Secret{ 386 ObjectMeta: metav1.ObjectMeta{ 387 Labels: map[string]string{"single-bad-reference": "true"}, 388 GenerateName: "invalid-child-b-", 389 OwnerReferences: []metav1.OwnerReference{{Name: "invalid", UID: parent.UID, APIVersion: "v1", Kind: "Pod", Controller: ptr.To(false)}}, 390 }, 391 }, metav1.CreateOptions{}) 392 if err != nil { 393 t.Fatal(err) 394 } 395 } 396 397 // start GC with existing objects in place to simulate controller-manager restart 398 ctx := setupWithServer(t, testServer, workers) 399 defer ctx.tearDown() 400 testServer = nil 401 402 // Wait for the invalid children to be garbage collected 403 if err := wait.PollImmediate(time.Second, 10*time.Second, func() (bool, error) { 404 children, err := clientSet.CoreV1().Secrets(namespaceA).List(context.TODO(), metav1.ListOptions{LabelSelector: "single-bad-reference=true"}) 405 if err != nil { 406 return false, err 407 } 408 if len(children.Items) > 0 { 409 t.Logf("expected 0 invalid children, got %d, will wait and relist", len(children.Items)) 410 return false, nil 411 } 412 return true, nil 413 }); err != nil && err != wait.ErrWaitTimeout { 414 t.Error(err) 415 } 416 417 // Wait for a little while to make sure they didn't trigger deletion of the valid children 418 if err := wait.Poll(time.Second, 5*time.Second, func() (bool, error) { 419 children, err := clientSet.CoreV1().Secrets(namespaceB).List(context.TODO(), metav1.ListOptions{}) 420 if err != nil { 421 return false, err 422 } 423 if len(children.Items) != validChildrenCount { 424 return false, fmt.Errorf("expected %d valid children, got %d", validChildrenCount, len(children.Items)) 425 } 426 return false, nil 427 }); err != nil && err != wait.ErrWaitTimeout { 428 t.Error(err) 429 } 430 431 if !ctx.gc.GraphHasUID(parent.UID) { 432 t.Errorf("valid parent UID no longer exists in the graph") 433 } 434 435 // Now that our graph has correct data in it, add a new invalid child and see if it gets deleted 436 invalidChild, err := clientSet.CoreV1().Secrets(namespaceA).Create(context.TODO(), &v1.Secret{ 437 ObjectMeta: metav1.ObjectMeta{ 438 GenerateName: "invalid-child-c-", 439 OwnerReferences: []metav1.OwnerReference{{Name: "invalid", UID: parent.UID, APIVersion: "v1", Kind: "Pod", Controller: ptr.To(false)}}, 440 }, 441 }, metav1.CreateOptions{}) 442 if err != nil { 443 t.Fatal(err) 444 } 445 // Wait for the invalid child to be garbage collected 446 if err := wait.PollImmediate(time.Second, 10*time.Second, func() (bool, error) { 447 _, err := clientSet.CoreV1().Secrets(namespaceA).Get(context.TODO(), invalidChild.Name, metav1.GetOptions{}) 448 if apierrors.IsNotFound(err) { 449 return true, nil 450 } 451 if err != nil { 452 return false, err 453 } 454 t.Logf("%s remains, waiting for deletion", invalidChild.Name) 455 return false, nil 456 }); err != nil { 457 t.Fatal(err) 458 } 459 } 460 461 // This test simulates the cascading deletion. 462 func TestCascadingDeletion(t *testing.T) { 463 ctx := setup(t, 5) 464 defer ctx.tearDown() 465 466 gc, clientSet := ctx.gc, ctx.clientSet 467 468 ns := createNamespaceOrDie("gc-cascading-deletion", clientSet, t) 469 defer deleteNamespaceOrDie(ns.Name, clientSet, t) 470 471 rcClient := clientSet.CoreV1().ReplicationControllers(ns.Name) 472 podClient := clientSet.CoreV1().Pods(ns.Name) 473 474 toBeDeletedRC, err := rcClient.Create(context.TODO(), newOwnerRC(toBeDeletedRCName, ns.Name), metav1.CreateOptions{}) 475 if err != nil { 476 t.Fatalf("Failed to create replication controller: %v", err) 477 } 478 remainingRC, err := rcClient.Create(context.TODO(), newOwnerRC(remainingRCName, ns.Name), metav1.CreateOptions{}) 479 if err != nil { 480 t.Fatalf("Failed to create replication controller: %v", err) 481 } 482 483 rcs, err := rcClient.List(context.TODO(), metav1.ListOptions{}) 484 if err != nil { 485 t.Fatalf("Failed to list replication controllers: %v", err) 486 } 487 if len(rcs.Items) != 2 { 488 t.Fatalf("Expect only 2 replication controller") 489 } 490 491 // this pod should be cascadingly deleted. 492 pod := newPod(garbageCollectedPodName, ns.Name, []metav1.OwnerReference{{UID: toBeDeletedRC.ObjectMeta.UID, Name: toBeDeletedRCName}}) 493 _, err = podClient.Create(context.TODO(), pod, metav1.CreateOptions{}) 494 if err != nil { 495 t.Fatalf("Failed to create Pod: %v", err) 496 } 497 498 // this pod shouldn't be cascadingly deleted, because it has a valid reference. 499 pod = newPod(oneValidOwnerPodName, ns.Name, []metav1.OwnerReference{ 500 {UID: toBeDeletedRC.ObjectMeta.UID, Name: toBeDeletedRCName}, 501 {UID: remainingRC.ObjectMeta.UID, Name: remainingRCName}, 502 }) 503 _, err = podClient.Create(context.TODO(), pod, metav1.CreateOptions{}) 504 if err != nil { 505 t.Fatalf("Failed to create Pod: %v", err) 506 } 507 508 // this pod shouldn't be cascadingly deleted, because it doesn't have an owner. 509 pod = newPod(independentPodName, ns.Name, []metav1.OwnerReference{}) 510 _, err = podClient.Create(context.TODO(), pod, metav1.CreateOptions{}) 511 if err != nil { 512 t.Fatalf("Failed to create Pod: %v", err) 513 } 514 515 // set up watch 516 pods, err := podClient.List(context.TODO(), metav1.ListOptions{}) 517 if err != nil { 518 t.Fatalf("Failed to list pods: %v", err) 519 } 520 if len(pods.Items) != 3 { 521 t.Fatalf("Expect only 3 pods") 522 } 523 // delete one of the replication controller 524 if err := rcClient.Delete(context.TODO(), toBeDeletedRCName, getNonOrphanOptions()); err != nil { 525 t.Fatalf("failed to delete replication controller: %v", err) 526 } 527 // sometimes the deletion of the RC takes long time to be observed by 528 // the gc, so wait for the garbage collector to observe the deletion of 529 // the toBeDeletedRC 530 if err := wait.Poll(1*time.Second, 60*time.Second, func() (bool, error) { 531 return !gc.GraphHasUID(toBeDeletedRC.ObjectMeta.UID), nil 532 }); err != nil { 533 t.Fatal(err) 534 } 535 if err := integration.WaitForPodToDisappear(podClient, garbageCollectedPodName, 1*time.Second, 30*time.Second); err != nil { 536 t.Fatalf("expect pod %s to be garbage collected, got err= %v", garbageCollectedPodName, err) 537 } 538 // checks the garbage collect doesn't delete pods it shouldn't delete. 539 if _, err := podClient.Get(context.TODO(), independentPodName, metav1.GetOptions{}); err != nil { 540 t.Fatal(err) 541 } 542 if _, err := podClient.Get(context.TODO(), oneValidOwnerPodName, metav1.GetOptions{}); err != nil { 543 t.Fatal(err) 544 } 545 } 546 547 // This test simulates the case where an object is created with an owner that 548 // doesn't exist. It verifies the GC will delete such an object. 549 func TestCreateWithNonExistentOwner(t *testing.T) { 550 ctx := setup(t, 5) 551 defer ctx.tearDown() 552 553 clientSet := ctx.clientSet 554 555 ns := createNamespaceOrDie("gc-non-existing-owner", clientSet, t) 556 defer deleteNamespaceOrDie(ns.Name, clientSet, t) 557 558 podClient := clientSet.CoreV1().Pods(ns.Name) 559 560 pod := newPod(garbageCollectedPodName, ns.Name, []metav1.OwnerReference{{UID: "doesn't matter", Name: toBeDeletedRCName}}) 561 _, err := podClient.Create(context.TODO(), pod, metav1.CreateOptions{}) 562 if err != nil { 563 t.Fatalf("Failed to create Pod: %v", err) 564 } 565 566 // set up watch 567 pods, err := podClient.List(context.TODO(), metav1.ListOptions{}) 568 if err != nil { 569 t.Fatalf("Failed to list pods: %v", err) 570 } 571 if len(pods.Items) > 1 { 572 t.Fatalf("Unexpected pod list: %v", pods.Items) 573 } 574 // wait for the garbage collector to delete the pod 575 if err := integration.WaitForPodToDisappear(podClient, garbageCollectedPodName, 1*time.Second, 30*time.Second); err != nil { 576 t.Fatalf("expect pod %s to be garbage collected, got err= %v", garbageCollectedPodName, err) 577 } 578 } 579 580 func setupRCsPods(t *testing.T, gc *garbagecollector.GarbageCollector, clientSet clientset.Interface, nameSuffix, namespace string, initialFinalizers []string, options metav1.DeleteOptions, wg *sync.WaitGroup, rcUIDs chan types.UID, errs chan string) { 581 defer wg.Done() 582 rcClient := clientSet.CoreV1().ReplicationControllers(namespace) 583 podClient := clientSet.CoreV1().Pods(namespace) 584 // create rc. 585 rcName := "test.rc." + nameSuffix 586 rc := newOwnerRC(rcName, namespace) 587 rc.ObjectMeta.Finalizers = initialFinalizers 588 rc, err := rcClient.Create(context.TODO(), rc, metav1.CreateOptions{}) 589 if err != nil { 590 errs <- fmt.Sprintf("Failed to create replication controller: %v", err) 591 return 592 } 593 rcUIDs <- rc.ObjectMeta.UID 594 // create pods. 595 var podUIDs []types.UID 596 for j := 0; j < 3; j++ { 597 podName := "test.pod." + nameSuffix + "-" + strconv.Itoa(j) 598 pod := newPod(podName, namespace, []metav1.OwnerReference{{UID: rc.ObjectMeta.UID, Name: rc.ObjectMeta.Name}}) 599 createdPod, err := podClient.Create(context.TODO(), pod, metav1.CreateOptions{}) 600 if err != nil { 601 errs <- fmt.Sprintf("Failed to create Pod: %v", err) 602 return 603 } 604 podUIDs = append(podUIDs, createdPod.ObjectMeta.UID) 605 } 606 orphan := false 607 switch { 608 case options.OrphanDependents == nil && options.PropagationPolicy == nil && len(initialFinalizers) == 0: //nolint:staticcheck // SA1019 Keep testing deprecated OrphanDependents option until it's being removed 609 // if there are no deletion options, the default policy for replication controllers is orphan 610 orphan = true 611 case options.OrphanDependents != nil: //nolint:staticcheck // SA1019 Keep testing deprecated OrphanDependents option until it's being removed 612 // if the deletion options explicitly specify whether to orphan, that controls 613 orphan = *options.OrphanDependents //nolint:staticcheck // SA1019 Keep testing deprecated OrphanDependents option until it's being removed 614 case options.PropagationPolicy != nil: 615 // if the deletion options explicitly specify whether to orphan, that controls 616 orphan = *options.PropagationPolicy == metav1.DeletePropagationOrphan 617 case len(initialFinalizers) != 0 && initialFinalizers[0] == metav1.FinalizerOrphanDependents: 618 // if the orphan finalizer is explicitly added, we orphan 619 orphan = true 620 } 621 // if we intend to orphan the pods, we need wait for the gc to observe the 622 // creation of the pods, otherwise if the deletion of RC is observed before 623 // the creation of the pods, the pods will not be orphaned. 624 if orphan { 625 err := wait.Poll(1*time.Second, 60*time.Second, func() (bool, error) { 626 for _, u := range podUIDs { 627 if !gc.GraphHasUID(u) { 628 return false, nil 629 } 630 } 631 return true, nil 632 }) 633 if err != nil { 634 errs <- fmt.Sprintf("failed to observe the expected pods in the GC graph for rc %s", rcName) 635 return 636 } 637 } 638 // delete the rc 639 if err := rcClient.Delete(context.TODO(), rc.ObjectMeta.Name, options); err != nil { 640 errs <- fmt.Sprintf("failed to delete replication controller: %v", err) 641 return 642 } 643 } 644 645 func verifyRemainingObjects(t *testing.T, clientSet clientset.Interface, namespace string, rcNum, podNum int) (bool, error) { 646 rcClient := clientSet.CoreV1().ReplicationControllers(namespace) 647 podClient := clientSet.CoreV1().Pods(namespace) 648 pods, err := podClient.List(context.TODO(), metav1.ListOptions{}) 649 if err != nil { 650 return false, fmt.Errorf("Failed to list pods: %v", err) 651 } 652 var ret = true 653 if len(pods.Items) != podNum { 654 ret = false 655 t.Logf("expect %d pods, got %d pods", podNum, len(pods.Items)) 656 } 657 rcs, err := rcClient.List(context.TODO(), metav1.ListOptions{}) 658 if err != nil { 659 return false, fmt.Errorf("Failed to list replication controllers: %v", err) 660 } 661 if len(rcs.Items) != rcNum { 662 ret = false 663 t.Logf("expect %d RCs, got %d RCs", rcNum, len(rcs.Items)) 664 } 665 return ret, nil 666 } 667 668 // The stress test is not very stressful, because we need to control the running 669 // time of our pre-submit tests to increase submit-queue throughput. We'll add 670 // e2e tests that put more stress. 671 func TestStressingCascadingDeletion(t *testing.T) { 672 ctx := setup(t, 5) 673 defer ctx.tearDown() 674 675 gc, clientSet := ctx.gc, ctx.clientSet 676 677 ns := createNamespaceOrDie("gc-stressing-cascading-deletion", clientSet, t) 678 defer deleteNamespaceOrDie(ns.Name, clientSet, t) 679 680 const collections = 10 681 var wg sync.WaitGroup 682 wg.Add(collections * 5) 683 rcUIDs := make(chan types.UID, collections*5) 684 errs := make(chan string, 5) 685 for i := 0; i < collections; i++ { 686 // rc is created with empty finalizers, deleted with nil delete options, pods will remain. 687 go setupRCsPods(t, gc, clientSet, "collection1-"+strconv.Itoa(i), ns.Name, []string{}, metav1.DeleteOptions{}, &wg, rcUIDs, errs) 688 // rc is created with the orphan finalizer, deleted with nil options, pods will remain. 689 go setupRCsPods(t, gc, clientSet, "collection2-"+strconv.Itoa(i), ns.Name, []string{metav1.FinalizerOrphanDependents}, metav1.DeleteOptions{}, &wg, rcUIDs, errs) 690 // rc is created with the orphan finalizer, deleted with DeleteOptions.OrphanDependents=false, pods will be deleted. 691 go setupRCsPods(t, gc, clientSet, "collection3-"+strconv.Itoa(i), ns.Name, []string{metav1.FinalizerOrphanDependents}, getNonOrphanOptions(), &wg, rcUIDs, errs) 692 // rc is created with empty finalizers, deleted with DeleteOptions.OrphanDependents=true, pods will remain. 693 go setupRCsPods(t, gc, clientSet, "collection4-"+strconv.Itoa(i), ns.Name, []string{}, getOrphanOptions(), &wg, rcUIDs, errs) 694 // rc is created with empty finalizers, deleted with DeleteOptions.PropagationPolicy=Orphan, pods will remain. 695 go setupRCsPods(t, gc, clientSet, "collection5-"+strconv.Itoa(i), ns.Name, []string{}, getPropagateOrphanOptions(), &wg, rcUIDs, errs) 696 } 697 wg.Wait() 698 close(errs) 699 for errString := range errs { 700 t.Fatalf(errString) 701 } 702 t.Logf("all pods are created, all replications controllers are created then deleted") 703 // wait for the RCs and Pods to reach the expected numbers. 704 if err := wait.Poll(1*time.Second, 300*time.Second, func() (bool, error) { 705 podsInEachCollection := 3 706 // see the comments on the calls to setupRCsPods for details 707 remainingGroups := 4 708 return verifyRemainingObjects(t, clientSet, ns.Name, 0, collections*podsInEachCollection*remainingGroups) 709 }); err != nil { 710 t.Fatal(err) 711 } 712 t.Logf("number of remaining replication controllers and pods are as expected") 713 714 // verify the remaining pods all have "orphan" in their names. 715 podClient := clientSet.CoreV1().Pods(ns.Name) 716 pods, err := podClient.List(context.TODO(), metav1.ListOptions{}) 717 if err != nil { 718 t.Fatal(err) 719 } 720 for _, pod := range pods.Items { 721 if !strings.Contains(pod.ObjectMeta.Name, "collection1-") && !strings.Contains(pod.ObjectMeta.Name, "collection2-") && !strings.Contains(pod.ObjectMeta.Name, "collection4-") && !strings.Contains(pod.ObjectMeta.Name, "collection5-") { 722 t.Errorf("got unexpected remaining pod: %#v", pod) 723 } 724 } 725 726 // verify there is no node representing replication controllers in the gc's graph 727 for i := 0; i < collections; i++ { 728 uid := <-rcUIDs 729 if gc.GraphHasUID(uid) { 730 t.Errorf("Expect all nodes representing replication controllers are removed from the Propagator's graph") 731 } 732 } 733 } 734 735 func TestOrphaning(t *testing.T) { 736 ctx := setup(t, 5) 737 defer ctx.tearDown() 738 739 gc, clientSet := ctx.gc, ctx.clientSet 740 741 ns := createNamespaceOrDie("gc-orphaning", clientSet, t) 742 defer deleteNamespaceOrDie(ns.Name, clientSet, t) 743 744 podClient := clientSet.CoreV1().Pods(ns.Name) 745 rcClient := clientSet.CoreV1().ReplicationControllers(ns.Name) 746 // create the RC with the orphan finalizer set 747 toBeDeletedRC := newOwnerRC(toBeDeletedRCName, ns.Name) 748 toBeDeletedRC, err := rcClient.Create(context.TODO(), toBeDeletedRC, metav1.CreateOptions{}) 749 if err != nil { 750 t.Fatalf("Failed to create replication controller: %v", err) 751 } 752 753 // these pods should be orphaned. 754 var podUIDs []types.UID 755 podsNum := 3 756 for i := 0; i < podsNum; i++ { 757 podName := garbageCollectedPodName + strconv.Itoa(i) 758 pod := newPod(podName, ns.Name, []metav1.OwnerReference{{UID: toBeDeletedRC.ObjectMeta.UID, Name: toBeDeletedRCName}}) 759 createdPod, err := podClient.Create(context.TODO(), pod, metav1.CreateOptions{}) 760 if err != nil { 761 t.Fatalf("Failed to create Pod: %v", err) 762 } 763 podUIDs = append(podUIDs, createdPod.ObjectMeta.UID) 764 } 765 766 // we need wait for the gc to observe the creation of the pods, otherwise if 767 // the deletion of RC is observed before the creation of the pods, the pods 768 // will not be orphaned. 769 err = wait.Poll(1*time.Second, 60*time.Second, func() (bool, error) { 770 for _, u := range podUIDs { 771 if !gc.GraphHasUID(u) { 772 return false, nil 773 } 774 } 775 return true, nil 776 }) 777 if err != nil { 778 t.Fatalf("Failed to observe pods in GC graph for %s: %v", toBeDeletedRC.Name, err) 779 } 780 781 err = rcClient.Delete(context.TODO(), toBeDeletedRCName, getOrphanOptions()) 782 if err != nil { 783 t.Fatalf("Failed to gracefully delete the rc: %v", err) 784 } 785 // verify the toBeDeleteRC is deleted 786 if err := wait.PollImmediate(1*time.Second, 30*time.Second, func() (bool, error) { 787 rcs, err := rcClient.List(context.TODO(), metav1.ListOptions{}) 788 if err != nil { 789 return false, err 790 } 791 if len(rcs.Items) == 0 { 792 t.Logf("Still has %d RCs", len(rcs.Items)) 793 return true, nil 794 } 795 return false, nil 796 }); err != nil { 797 t.Errorf("unexpected error: %v", err) 798 } 799 800 // verify pods don't have the ownerPod as an owner anymore 801 pods, err := podClient.List(context.TODO(), metav1.ListOptions{}) 802 if err != nil { 803 t.Fatalf("Failed to list pods: %v", err) 804 } 805 if len(pods.Items) != podsNum { 806 t.Errorf("Expect %d pod(s), but got %#v", podsNum, pods) 807 } 808 for _, pod := range pods.Items { 809 if len(pod.ObjectMeta.OwnerReferences) != 0 { 810 t.Errorf("pod %s still has non-empty OwnerReferences: %v", pod.ObjectMeta.Name, pod.ObjectMeta.OwnerReferences) 811 } 812 } 813 } 814 815 func TestSolidOwnerDoesNotBlockWaitingOwner(t *testing.T) { 816 ctx := setup(t, 5) 817 defer ctx.tearDown() 818 819 clientSet := ctx.clientSet 820 821 ns := createNamespaceOrDie("gc-foreground1", clientSet, t) 822 defer deleteNamespaceOrDie(ns.Name, clientSet, t) 823 824 podClient := clientSet.CoreV1().Pods(ns.Name) 825 rcClient := clientSet.CoreV1().ReplicationControllers(ns.Name) 826 // create the RC with the orphan finalizer set 827 toBeDeletedRC, err := rcClient.Create(context.TODO(), newOwnerRC(toBeDeletedRCName, ns.Name), metav1.CreateOptions{}) 828 if err != nil { 829 t.Fatalf("Failed to create replication controller: %v", err) 830 } 831 remainingRC, err := rcClient.Create(context.TODO(), newOwnerRC(remainingRCName, ns.Name), metav1.CreateOptions{}) 832 if err != nil { 833 t.Fatalf("Failed to create replication controller: %v", err) 834 } 835 trueVar := true 836 pod := newPod("pod", ns.Name, []metav1.OwnerReference{ 837 {UID: toBeDeletedRC.ObjectMeta.UID, Name: toBeDeletedRC.Name, BlockOwnerDeletion: &trueVar}, 838 {UID: remainingRC.ObjectMeta.UID, Name: remainingRC.Name}, 839 }) 840 _, err = podClient.Create(context.TODO(), pod, metav1.CreateOptions{}) 841 if err != nil { 842 t.Fatalf("Failed to create Pod: %v", err) 843 } 844 845 err = rcClient.Delete(context.TODO(), toBeDeletedRCName, getForegroundOptions()) 846 if err != nil { 847 t.Fatalf("Failed to delete the rc: %v", err) 848 } 849 // verify the toBeDeleteRC is deleted 850 if err := wait.PollImmediate(1*time.Second, 30*time.Second, func() (bool, error) { 851 _, err := rcClient.Get(context.TODO(), toBeDeletedRC.Name, metav1.GetOptions{}) 852 if err != nil { 853 if apierrors.IsNotFound(err) { 854 return true, nil 855 } 856 return false, err 857 } 858 return false, nil 859 }); err != nil { 860 t.Errorf("unexpected error: %v", err) 861 } 862 863 // verify pods don't have the toBeDeleteRC as an owner anymore 864 pod, err = podClient.Get(context.TODO(), "pod", metav1.GetOptions{}) 865 if err != nil { 866 t.Fatalf("Failed to list pods: %v", err) 867 } 868 if len(pod.ObjectMeta.OwnerReferences) != 1 { 869 t.Errorf("expect pod to have only one ownerReference: got %#v", pod.ObjectMeta.OwnerReferences) 870 } else if pod.ObjectMeta.OwnerReferences[0].Name != remainingRC.Name { 871 t.Errorf("expect pod to have an ownerReference pointing to %s, got %#v", remainingRC.Name, pod.ObjectMeta.OwnerReferences) 872 } 873 } 874 875 func TestNonBlockingOwnerRefDoesNotBlock(t *testing.T) { 876 ctx := setup(t, 5) 877 defer ctx.tearDown() 878 879 clientSet := ctx.clientSet 880 881 ns := createNamespaceOrDie("gc-foreground2", clientSet, t) 882 defer deleteNamespaceOrDie(ns.Name, clientSet, t) 883 884 podClient := clientSet.CoreV1().Pods(ns.Name) 885 rcClient := clientSet.CoreV1().ReplicationControllers(ns.Name) 886 // create the RC with the orphan finalizer set 887 toBeDeletedRC, err := rcClient.Create(context.TODO(), newOwnerRC(toBeDeletedRCName, ns.Name), metav1.CreateOptions{}) 888 if err != nil { 889 t.Fatalf("Failed to create replication controller: %v", err) 890 } 891 // BlockingOwnerDeletion is not set 892 pod1 := newPod("pod1", ns.Name, []metav1.OwnerReference{ 893 {UID: toBeDeletedRC.ObjectMeta.UID, Name: toBeDeletedRC.Name}, 894 }) 895 // adding finalizer that no controller handles, so that the pod won't be deleted 896 pod1.ObjectMeta.Finalizers = []string{"x/y"} 897 // BlockingOwnerDeletion is false 898 falseVar := false 899 pod2 := newPod("pod2", ns.Name, []metav1.OwnerReference{ 900 {UID: toBeDeletedRC.ObjectMeta.UID, Name: toBeDeletedRC.Name, BlockOwnerDeletion: &falseVar}, 901 }) 902 // adding finalizer that no controller handles, so that the pod won't be deleted 903 pod2.ObjectMeta.Finalizers = []string{"x/y"} 904 _, err = podClient.Create(context.TODO(), pod1, metav1.CreateOptions{}) 905 if err != nil { 906 t.Fatalf("Failed to create Pod: %v", err) 907 } 908 _, err = podClient.Create(context.TODO(), pod2, metav1.CreateOptions{}) 909 if err != nil { 910 t.Fatalf("Failed to create Pod: %v", err) 911 } 912 913 err = rcClient.Delete(context.TODO(), toBeDeletedRCName, getForegroundOptions()) 914 if err != nil { 915 t.Fatalf("Failed to delete the rc: %v", err) 916 } 917 // verify the toBeDeleteRC is deleted 918 if err := wait.PollImmediate(1*time.Second, 30*time.Second, func() (bool, error) { 919 _, err := rcClient.Get(context.TODO(), toBeDeletedRC.Name, metav1.GetOptions{}) 920 if err != nil { 921 if apierrors.IsNotFound(err) { 922 return true, nil 923 } 924 return false, err 925 } 926 return false, nil 927 }); err != nil { 928 t.Errorf("unexpected error: %v", err) 929 } 930 931 // verify pods are still there 932 pods, err := podClient.List(context.TODO(), metav1.ListOptions{}) 933 if err != nil { 934 t.Fatalf("Failed to list pods: %v", err) 935 } 936 if len(pods.Items) != 2 { 937 t.Errorf("expect there to be 2 pods, got %#v", pods.Items) 938 } 939 } 940 941 func TestDoubleDeletionWithFinalizer(t *testing.T) { 942 // test setup 943 ctx := setup(t, 5) 944 defer ctx.tearDown() 945 clientSet := ctx.clientSet 946 ns := createNamespaceOrDie("gc-double-foreground", clientSet, t) 947 defer deleteNamespaceOrDie(ns.Name, clientSet, t) 948 949 // step 1: creates a pod with a custom finalizer and deletes it, then waits until gc removes its finalizer 950 podClient := clientSet.CoreV1().Pods(ns.Name) 951 pod := newPod("lucy", ns.Name, nil) 952 pod.ObjectMeta.Finalizers = []string{"x/y"} 953 if _, err := podClient.Create(context.TODO(), pod, metav1.CreateOptions{}); err != nil { 954 t.Fatalf("Failed to create pod: %v", err) 955 } 956 if err := podClient.Delete(context.TODO(), pod.Name, getForegroundOptions()); err != nil { 957 t.Fatalf("Failed to delete pod: %v", err) 958 } 959 if err := wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) { 960 returnedPod, err := podClient.Get(context.TODO(), pod.Name, metav1.GetOptions{}) 961 if err != nil { 962 return false, err 963 } 964 if len(returnedPod.Finalizers) != 1 || returnedPod.Finalizers[0] != "x/y" { 965 t.Logf("waiting for pod %q to have only one finalizer %q at step 1, got %v", returnedPod.Name, "x/y", returnedPod.Finalizers) 966 return false, nil 967 } 968 return true, nil 969 }); err != nil { 970 t.Fatalf("Failed waiting for pod to have only one filanizer at step 1, error: %v", err) 971 } 972 973 // step 2: deletes the pod one more time and checks if there's only the custom finalizer left 974 if err := podClient.Delete(context.TODO(), pod.Name, getForegroundOptions()); err != nil { 975 t.Fatalf("Failed to delete pod: %v", err) 976 } 977 if err := wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) { 978 returnedPod, err := podClient.Get(context.TODO(), pod.Name, metav1.GetOptions{}) 979 if err != nil { 980 return false, err 981 } 982 if len(returnedPod.Finalizers) != 1 || returnedPod.Finalizers[0] != "x/y" { 983 t.Logf("waiting for pod %q to have only one finalizer %q at step 2, got %v", returnedPod.Name, "x/y", returnedPod.Finalizers) 984 return false, nil 985 } 986 return true, nil 987 }); err != nil { 988 t.Fatalf("Failed waiting for pod to have only one finalizer at step 2, gc hasn't removed its finalzier?, error: %v", err) 989 } 990 991 // step 3: removes the custom finalizer and checks if the pod was removed 992 patch := []byte(`[{"op":"remove","path":"/metadata/finalizers"}]`) 993 if _, err := podClient.Patch(context.TODO(), pod.Name, types.JSONPatchType, patch, metav1.PatchOptions{}); err != nil { 994 t.Fatalf("Failed to update pod: %v", err) 995 } 996 if err := wait.Poll(1*time.Second, 10*time.Second, func() (bool, error) { 997 _, err := podClient.Get(context.TODO(), pod.Name, metav1.GetOptions{}) 998 return apierrors.IsNotFound(err), nil 999 }); err != nil { 1000 t.Fatalf("Failed waiting for pod %q to be deleted", pod.Name) 1001 } 1002 } 1003 1004 func TestBlockingOwnerRefDoesBlock(t *testing.T) { 1005 ctx := setup(t, 0) 1006 defer ctx.tearDown() 1007 gc, clientSet := ctx.gc, ctx.clientSet 1008 1009 ns := createNamespaceOrDie("foo", clientSet, t) 1010 defer deleteNamespaceOrDie(ns.Name, clientSet, t) 1011 1012 podClient := clientSet.CoreV1().Pods(ns.Name) 1013 rcClient := clientSet.CoreV1().ReplicationControllers(ns.Name) 1014 // create the RC with the orphan finalizer set 1015 toBeDeletedRC, err := rcClient.Create(context.TODO(), newOwnerRC(toBeDeletedRCName, ns.Name), metav1.CreateOptions{}) 1016 if err != nil { 1017 t.Fatalf("Failed to create replication controller: %v", err) 1018 } 1019 trueVar := true 1020 pod := newPod("pod", ns.Name, []metav1.OwnerReference{ 1021 {UID: toBeDeletedRC.ObjectMeta.UID, Name: toBeDeletedRC.Name, BlockOwnerDeletion: &trueVar}, 1022 }) 1023 // adding finalizer that no controller handles, so that the pod won't be deleted 1024 pod.ObjectMeta.Finalizers = []string{"x/y"} 1025 _, err = podClient.Create(context.TODO(), pod, metav1.CreateOptions{}) 1026 if err != nil { 1027 t.Fatalf("Failed to create Pod: %v", err) 1028 } 1029 1030 // this makes sure the garbage collector will have added the pod to its 1031 // dependency graph before handling the foreground deletion of the rc. 1032 ctx.startGC(5) 1033 timeout := make(chan struct{}) 1034 time.AfterFunc(5*time.Second, func() { close(timeout) }) 1035 if !cache.WaitForCacheSync(timeout, func() bool { 1036 return gc.IsSynced(ctx.logger) 1037 }) { 1038 t.Fatalf("failed to wait for garbage collector to be synced") 1039 } 1040 1041 err = rcClient.Delete(context.TODO(), toBeDeletedRCName, getForegroundOptions()) 1042 if err != nil { 1043 t.Fatalf("Failed to delete the rc: %v", err) 1044 } 1045 time.Sleep(15 * time.Second) 1046 // verify the toBeDeleteRC is NOT deleted 1047 _, err = rcClient.Get(context.TODO(), toBeDeletedRC.Name, metav1.GetOptions{}) 1048 if err != nil { 1049 t.Errorf("unexpected error: %v", err) 1050 } 1051 1052 // verify pods are still there 1053 pods, err := podClient.List(context.TODO(), metav1.ListOptions{}) 1054 if err != nil { 1055 t.Fatalf("Failed to list pods: %v", err) 1056 } 1057 if len(pods.Items) != 1 { 1058 t.Errorf("expect there to be 1 pods, got %#v", pods.Items) 1059 } 1060 } 1061 1062 // TestCustomResourceCascadingDeletion ensures the basic cascading delete 1063 // behavior supports custom resources. 1064 func TestCustomResourceCascadingDeletion(t *testing.T) { 1065 ctx := setup(t, 5) 1066 defer ctx.tearDown() 1067 1068 clientSet, apiExtensionClient, dynamicClient := ctx.clientSet, ctx.apiExtensionClient, ctx.dynamicClient 1069 1070 ns := createNamespaceOrDie("crd-cascading", clientSet, t) 1071 1072 definition, resourceClient := createRandomCustomResourceDefinition(t, apiExtensionClient, dynamicClient, ns.Name) 1073 1074 // Create a custom owner resource. 1075 owner := newCRDInstance(definition, ns.Name, names.SimpleNameGenerator.GenerateName("owner")) 1076 owner, err := resourceClient.Create(context.TODO(), owner, metav1.CreateOptions{}) 1077 if err != nil { 1078 t.Fatalf("failed to create owner resource %q: %v", owner.GetName(), err) 1079 } 1080 t.Logf("created owner resource %q", owner.GetName()) 1081 1082 // Create a custom dependent resource. 1083 dependent := newCRDInstance(definition, ns.Name, names.SimpleNameGenerator.GenerateName("dependent")) 1084 link(t, owner, dependent) 1085 1086 dependent, err = resourceClient.Create(context.TODO(), dependent, metav1.CreateOptions{}) 1087 if err != nil { 1088 t.Fatalf("failed to create dependent resource %q: %v", dependent.GetName(), err) 1089 } 1090 t.Logf("created dependent resource %q", dependent.GetName()) 1091 1092 // Delete the owner. 1093 foreground := metav1.DeletePropagationForeground 1094 err = resourceClient.Delete(context.TODO(), owner.GetName(), metav1.DeleteOptions{PropagationPolicy: &foreground}) 1095 if err != nil { 1096 t.Fatalf("failed to delete owner resource %q: %v", owner.GetName(), err) 1097 } 1098 1099 // Ensure the owner is deleted. 1100 if err := wait.Poll(1*time.Second, 60*time.Second, func() (bool, error) { 1101 _, err := resourceClient.Get(context.TODO(), owner.GetName(), metav1.GetOptions{}) 1102 return apierrors.IsNotFound(err), nil 1103 }); err != nil { 1104 t.Fatalf("failed waiting for owner resource %q to be deleted", owner.GetName()) 1105 } 1106 1107 // Ensure the dependent is deleted. 1108 _, err = resourceClient.Get(context.TODO(), dependent.GetName(), metav1.GetOptions{}) 1109 if err == nil { 1110 t.Fatalf("expected dependent %q to be deleted", dependent.GetName()) 1111 } else { 1112 if !apierrors.IsNotFound(err) { 1113 t.Fatalf("unexpected error getting dependent %q: %v", dependent.GetName(), err) 1114 } 1115 } 1116 } 1117 1118 // TestMixedRelationships ensures that owner/dependent relationships work 1119 // between core and custom resources. 1120 // 1121 // TODO: Consider how this could be represented with table-style tests (e.g. a 1122 // before/after expected object graph given a delete operation targeting a 1123 // specific node in the before graph with certain delete options). 1124 func TestMixedRelationships(t *testing.T) { 1125 ctx := setup(t, 5) 1126 defer ctx.tearDown() 1127 1128 clientSet, apiExtensionClient, dynamicClient := ctx.clientSet, ctx.apiExtensionClient, ctx.dynamicClient 1129 1130 ns := createNamespaceOrDie("crd-mixed", clientSet, t) 1131 1132 configMapClient := clientSet.CoreV1().ConfigMaps(ns.Name) 1133 1134 definition, resourceClient := createRandomCustomResourceDefinition(t, apiExtensionClient, dynamicClient, ns.Name) 1135 1136 // Create a custom owner resource. 1137 customOwner, err := resourceClient.Create(context.TODO(), newCRDInstance(definition, ns.Name, names.SimpleNameGenerator.GenerateName("owner")), metav1.CreateOptions{}) 1138 if err != nil { 1139 t.Fatalf("failed to create owner: %v", err) 1140 } 1141 t.Logf("created custom owner %q", customOwner.GetName()) 1142 1143 // Create a core dependent resource. 1144 coreDependent := newConfigMap(ns.Name, names.SimpleNameGenerator.GenerateName("dependent")) 1145 link(t, customOwner, coreDependent) 1146 coreDependent, err = configMapClient.Create(context.TODO(), coreDependent, metav1.CreateOptions{}) 1147 if err != nil { 1148 t.Fatalf("failed to create dependent: %v", err) 1149 } 1150 t.Logf("created core dependent %q", coreDependent.GetName()) 1151 1152 // Create a core owner resource. 1153 coreOwner, err := configMapClient.Create(context.TODO(), newConfigMap(ns.Name, names.SimpleNameGenerator.GenerateName("owner")), metav1.CreateOptions{}) 1154 if err != nil { 1155 t.Fatalf("failed to create owner: %v", err) 1156 } 1157 t.Logf("created core owner %q: %#v", coreOwner.GetName(), coreOwner) 1158 1159 // Create a custom dependent resource. 1160 customDependent := newCRDInstance(definition, ns.Name, names.SimpleNameGenerator.GenerateName("dependent")) 1161 coreOwner.TypeMeta.Kind = "ConfigMap" 1162 coreOwner.TypeMeta.APIVersion = "v1" 1163 link(t, coreOwner, customDependent) 1164 customDependent, err = resourceClient.Create(context.TODO(), customDependent, metav1.CreateOptions{}) 1165 if err != nil { 1166 t.Fatalf("failed to create dependent: %v", err) 1167 } 1168 t.Logf("created custom dependent %q", customDependent.GetName()) 1169 1170 // Delete the custom owner. 1171 foreground := metav1.DeletePropagationForeground 1172 err = resourceClient.Delete(context.TODO(), customOwner.GetName(), metav1.DeleteOptions{PropagationPolicy: &foreground}) 1173 if err != nil { 1174 t.Fatalf("failed to delete owner resource %q: %v", customOwner.GetName(), err) 1175 } 1176 1177 // Ensure the owner is deleted. 1178 if err := wait.Poll(1*time.Second, 60*time.Second, func() (bool, error) { 1179 _, err := resourceClient.Get(context.TODO(), customOwner.GetName(), metav1.GetOptions{}) 1180 return apierrors.IsNotFound(err), nil 1181 }); err != nil { 1182 t.Fatalf("failed waiting for owner resource %q to be deleted", customOwner.GetName()) 1183 } 1184 1185 // Ensure the dependent is deleted. 1186 _, err = resourceClient.Get(context.TODO(), coreDependent.GetName(), metav1.GetOptions{}) 1187 if err == nil { 1188 t.Fatalf("expected dependent %q to be deleted", coreDependent.GetName()) 1189 } else { 1190 if !apierrors.IsNotFound(err) { 1191 t.Fatalf("unexpected error getting dependent %q: %v", coreDependent.GetName(), err) 1192 } 1193 } 1194 1195 // Delete the core owner. 1196 err = configMapClient.Delete(context.TODO(), coreOwner.GetName(), metav1.DeleteOptions{PropagationPolicy: &foreground}) 1197 if err != nil { 1198 t.Fatalf("failed to delete owner resource %q: %v", coreOwner.GetName(), err) 1199 } 1200 1201 // Ensure the owner is deleted. 1202 if err := wait.Poll(1*time.Second, 60*time.Second, func() (bool, error) { 1203 _, err := configMapClient.Get(context.TODO(), coreOwner.GetName(), metav1.GetOptions{}) 1204 return apierrors.IsNotFound(err), nil 1205 }); err != nil { 1206 t.Fatalf("failed waiting for owner resource %q to be deleted", coreOwner.GetName()) 1207 } 1208 1209 // Ensure the dependent is deleted. 1210 _, err = resourceClient.Get(context.TODO(), customDependent.GetName(), metav1.GetOptions{}) 1211 if err == nil { 1212 t.Fatalf("expected dependent %q to be deleted", customDependent.GetName()) 1213 } else { 1214 if !apierrors.IsNotFound(err) { 1215 t.Fatalf("unexpected error getting dependent %q: %v", customDependent.GetName(), err) 1216 } 1217 } 1218 } 1219 1220 // TestCRDDeletionCascading ensures propagating deletion of a custom resource 1221 // definition with an instance that owns a core resource. 1222 func TestCRDDeletionCascading(t *testing.T) { 1223 ctx := setup(t, 5) 1224 defer ctx.tearDown() 1225 1226 clientSet, apiExtensionClient, dynamicClient := ctx.clientSet, ctx.apiExtensionClient, ctx.dynamicClient 1227 1228 ns := createNamespaceOrDie("crd-mixed", clientSet, t) 1229 1230 t.Logf("First pass CRD cascading deletion") 1231 definition, resourceClient := createRandomCustomResourceDefinition(t, apiExtensionClient, dynamicClient, ns.Name) 1232 testCRDDeletion(t, ctx, ns, definition, resourceClient) 1233 1234 t.Logf("Second pass CRD cascading deletion") 1235 accessor := meta.NewAccessor() 1236 accessor.SetResourceVersion(definition, "") 1237 _, err := apiextensionstestserver.CreateNewV1CustomResourceDefinition(definition, apiExtensionClient, dynamicClient) 1238 if err != nil { 1239 t.Fatalf("failed to create CustomResourceDefinition: %v", err) 1240 } 1241 testCRDDeletion(t, ctx, ns, definition, resourceClient) 1242 } 1243 1244 func testCRDDeletion(t *testing.T, ctx *testContext, ns *v1.Namespace, definition *apiextensionsv1.CustomResourceDefinition, resourceClient dynamic.ResourceInterface) { 1245 clientSet, apiExtensionClient := ctx.clientSet, ctx.apiExtensionClient 1246 1247 configMapClient := clientSet.CoreV1().ConfigMaps(ns.Name) 1248 1249 // Create a custom owner resource. 1250 owner, err := resourceClient.Create(context.TODO(), newCRDInstance(definition, ns.Name, names.SimpleNameGenerator.GenerateName("owner")), metav1.CreateOptions{}) 1251 if err != nil { 1252 t.Fatalf("failed to create owner: %v", err) 1253 } 1254 t.Logf("created owner %q", owner.GetName()) 1255 1256 // Create a core dependent resource. 1257 dependent := newConfigMap(ns.Name, names.SimpleNameGenerator.GenerateName("dependent")) 1258 link(t, owner, dependent) 1259 dependent, err = configMapClient.Create(context.TODO(), dependent, metav1.CreateOptions{}) 1260 if err != nil { 1261 t.Fatalf("failed to create dependent: %v", err) 1262 } 1263 t.Logf("created dependent %q", dependent.GetName()) 1264 1265 time.Sleep(ctx.syncPeriod + 5*time.Second) 1266 1267 // Delete the definition, which should cascade to the owner and ultimately its dependents. 1268 if err := apiextensionstestserver.DeleteV1CustomResourceDefinition(definition, apiExtensionClient); err != nil { 1269 t.Fatalf("failed to delete %q: %v", definition.Name, err) 1270 } 1271 1272 // Ensure the owner is deleted. 1273 if err := wait.Poll(1*time.Second, 60*time.Second, func() (bool, error) { 1274 _, err := resourceClient.Get(context.TODO(), owner.GetName(), metav1.GetOptions{}) 1275 return apierrors.IsNotFound(err), nil 1276 }); err != nil { 1277 t.Fatalf("failed waiting for owner %q to be deleted", owner.GetName()) 1278 } 1279 1280 // Ensure the dependent is deleted. 1281 if err := wait.Poll(1*time.Second, 60*time.Second, func() (bool, error) { 1282 _, err := configMapClient.Get(context.TODO(), dependent.GetName(), metav1.GetOptions{}) 1283 return apierrors.IsNotFound(err), nil 1284 }); err != nil { 1285 t.Fatalf("failed waiting for dependent %q (owned by %q) to be deleted", dependent.GetName(), owner.GetName()) 1286 } 1287 }