Skip to content

Commit 4c15bea

Browse files
Aman-Coolk8s-ci-robot
authored andcommitted
test: add integration test for external CRD deletion recovery
Verify that when a KRO-managed CRD is deleted externally, the CRD metadata watch detects the deletion event and triggers reconciliation of the owning RGD, which recreates the CRD via crdManager.Ensure().
1 parent 8e004e2 commit 4c15bea

File tree

1 file changed

+81
-0
lines changed

1 file changed

+81
-0
lines changed

test/integration/suites/core/crd_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,5 +476,86 @@ var _ = Describe("CRD", func() {
476476

477477
Expect(env.Client.Delete(ctx, rgd)).To(Succeed())
478478
})
479+
480+
It("should detect externally deleted CRD and recreate it", func(ctx SpecContext) {
481+
rgdName := "test-crd-ext-deletion"
482+
rgd := generator.NewResourceGraphDefinition(rgdName,
483+
generator.WithSchema(
484+
"TestExtDeletion", "v1alpha1",
485+
map[string]interface{}{
486+
"field1": "string",
487+
"field2": "integer | default=42",
488+
},
489+
nil,
490+
),
491+
generator.WithResource("res1", map[string]interface{}{
492+
"apiVersion": "v1",
493+
"kind": "ConfigMap",
494+
"metadata": map[string]interface{}{
495+
"name": "${schema.spec.field1}",
496+
},
497+
"data": map[string]interface{}{
498+
"key": "value",
499+
"key2": "${string(schema.spec.field2)}",
500+
},
501+
}, nil, nil),
502+
)
503+
504+
Expect(env.Client.Create(ctx, rgd)).To(Succeed())
505+
506+
// Wait for RGD to become Active and CRD to be created
507+
crdName := "testextdeletions.kro.run"
508+
crd := &apiextensionsv1.CustomResourceDefinition{}
509+
Eventually(func(g Gomega, ctx SpecContext) {
510+
err := env.Client.Get(ctx, types.NamespacedName{Name: rgd.Name}, rgd)
511+
g.Expect(err).ToNot(HaveOccurred())
512+
g.Expect(rgd.Status.State).To(Equal(krov1alpha1.ResourceGraphDefinitionStateActive))
513+
514+
err = env.Client.Get(ctx, types.NamespacedName{Name: crdName}, crd)
515+
g.Expect(err).ToNot(HaveOccurred())
516+
g.Expect(metadata.IsKROOwned(&crd.ObjectMeta)).To(BeTrue())
517+
g.Expect(crd.Labels[metadata.ResourceGraphDefinitionNameLabel]).To(Equal(rgdName))
518+
}, 10*time.Second, time.Second).WithContext(ctx).Should(Succeed())
519+
520+
// Record the original CRD UID so we can verify it gets recreated
521+
// (not just the old one surviving deletion).
522+
originalUID := crd.UID
523+
524+
// Externally delete the managed CRD to simulate an out-of-band deletion.
525+
Expect(env.Client.Delete(ctx, crd)).To(Succeed())
526+
527+
// The controller's CRD metadata watch should detect the deletion event and
528+
// trigger a reconciliation of the owning RGD, which calls crdManager.Ensure()
529+
// to recreate the CRD.
530+
//
531+
// Without the fix (DeleteFunc returning false in the CRD watch predicate),
532+
// this would timeout because the delete event would be silently filtered
533+
// out, leaving the CRD permanently deleted.
534+
recreatedCRD := &apiextensionsv1.CustomResourceDefinition{}
535+
Eventually(func(g Gomega, ctx SpecContext) {
536+
err := env.Client.Get(ctx, types.NamespacedName{Name: crdName}, recreatedCRD)
537+
g.Expect(err).ToNot(HaveOccurred())
538+
539+
// A different UID proves this is a new object, not the original
540+
g.Expect(recreatedCRD.UID).NotTo(Equal(originalUID))
541+
542+
g.Expect(metadata.IsKROOwned(&recreatedCRD.ObjectMeta)).To(BeTrue())
543+
g.Expect(recreatedCRD.Labels[metadata.ResourceGraphDefinitionNameLabel]).To(Equal(rgdName))
544+
545+
schemaProps := recreatedCRD.Spec.Versions[0].Schema.OpenAPIV3Schema.Properties["spec"].Properties
546+
g.Expect(schemaProps["field1"].Type).To(Equal("string"))
547+
g.Expect(schemaProps["field2"].Type).To(Equal("integer"))
548+
g.Expect(schemaProps["field2"].Default.Raw).To(Equal([]byte("42")))
549+
}, 30*time.Second, 2*time.Second).WithContext(ctx).Should(Succeed())
550+
551+
// Verify the RGD recovers to Active state after CRD recreation
552+
Eventually(func(g Gomega, ctx SpecContext) {
553+
err := env.Client.Get(ctx, types.NamespacedName{Name: rgd.Name}, rgd)
554+
g.Expect(err).ToNot(HaveOccurred())
555+
g.Expect(rgd.Status.State).To(Equal(krov1alpha1.ResourceGraphDefinitionStateActive))
556+
}, 10*time.Second, time.Second).WithContext(ctx).Should(Succeed())
557+
558+
Expect(env.Client.Delete(ctx, rgd)).To(Succeed())
559+
})
479560
})
480561
})

0 commit comments

Comments
 (0)