1
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
187
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
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
213 syncPeriod time.Duration
214 }
215
216
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
228 apiExtensionClient, err := apiextensionsclientset.NewForConfig(result.ClientConfig)
229 if err != nil {
230 t.Fatalf("error creating extension clientset: %v", err)
231 }
232
233
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
276
277
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
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
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
398 ctx := setupWithServer(t, testServer, workers)
399 defer ctx.tearDown()
400 testServer = nil
401
402
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
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
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
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
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
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
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
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
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
524 if err := rcClient.Delete(context.TODO(), toBeDeletedRCName, getNonOrphanOptions()); err != nil {
525 t.Fatalf("failed to delete replication controller: %v", err)
526 }
527
528
529
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
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
548
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
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
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
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
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:
609
610 orphan = true
611 case options.OrphanDependents != nil:
612
613 orphan = *options.OrphanDependents
614 case options.PropagationPolicy != nil:
615
616 orphan = *options.PropagationPolicy == metav1.DeletePropagationOrphan
617 case len(initialFinalizers) != 0 && initialFinalizers[0] == metav1.FinalizerOrphanDependents:
618
619 orphan = true
620 }
621
622
623
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
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
669
670
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
687 go setupRCsPods(t, gc, clientSet, "collection1-"+strconv.Itoa(i), ns.Name, []string{}, metav1.DeleteOptions{}, &wg, rcUIDs, errs)
688
689 go setupRCsPods(t, gc, clientSet, "collection2-"+strconv.Itoa(i), ns.Name, []string{metav1.FinalizerOrphanDependents}, metav1.DeleteOptions{}, &wg, rcUIDs, errs)
690
691 go setupRCsPods(t, gc, clientSet, "collection3-"+strconv.Itoa(i), ns.Name, []string{metav1.FinalizerOrphanDependents}, getNonOrphanOptions(), &wg, rcUIDs, errs)
692
693 go setupRCsPods(t, gc, clientSet, "collection4-"+strconv.Itoa(i), ns.Name, []string{}, getOrphanOptions(), &wg, rcUIDs, errs)
694
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
704 if err := wait.Poll(1*time.Second, 300*time.Second, func() (bool, error) {
705 podsInEachCollection := 3
706
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
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
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
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
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
767
768
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
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
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
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
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
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
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
892 pod1 := newPod("pod1", ns.Name, []metav1.OwnerReference{
893 {UID: toBeDeletedRC.ObjectMeta.UID, Name: toBeDeletedRC.Name},
894 })
895
896 pod1.ObjectMeta.Finalizers = []string{"x/y"}
897
898 falseVar := false
899 pod2 := newPod("pod2", ns.Name, []metav1.OwnerReference{
900 {UID: toBeDeletedRC.ObjectMeta.UID, Name: toBeDeletedRC.Name, BlockOwnerDeletion: &falseVar},
901 })
902
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
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
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
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
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
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
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
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
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
1031
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
1047 _, err = rcClient.Get(context.TODO(), toBeDeletedRC.Name, metav1.GetOptions{})
1048 if err != nil {
1049 t.Errorf("unexpected error: %v", err)
1050 }
1051
1052
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
1063
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
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
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
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
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
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
1119
1120
1121
1122
1123
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
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
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
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
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
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
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
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
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
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
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
1221
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
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
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
1268 if err := apiextensionstestserver.DeleteV1CustomResourceDefinition(definition, apiExtensionClient); err != nil {
1269 t.Fatalf("failed to delete %q: %v", definition.Name, err)
1270 }
1271
1272
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
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 }
1288
View as plain text