@@ -81,7 +81,7 @@ func defaultTestSetup(testDirPathSegments ...string) (*managedcontrolplane.Manag
8181
8282var _ = Describe ("ManagedControlPlane Controller" , func () {
8383
84- It ("should correctly handle create and update operations for the MCP" , func () {
84+ It ("should correctly handle the creation, update, and deletion flow for MCP resources " , func () {
8585 rec , env := defaultTestSetup ("testdata" , "test-01" )
8686
8787 mcp := & corev2alpha1.ManagedControlPlane {}
@@ -122,7 +122,7 @@ var _ = Describe("ManagedControlPlane Controller", func() {
122122 ))
123123
124124 // fake ClusterRequest ready status
125- By ("faking ClusterRequest readiness" )
125+ By ("fake: ClusterRequest readiness" )
126126 cr .Status .Phase = commonapi .StatusPhaseReady
127127 Expect (env .Client (platform ).Status ().Update (env .Ctx , cr )).To (Succeed ())
128128
@@ -169,9 +169,9 @@ var _ = Describe("ManagedControlPlane Controller", func() {
169169 }
170170
171171 // fake AccessRequest ready status
172- By ("faking AccessRequest readiness" )
172+ By ("fake: AccessRequest readiness" )
173173 for _ , oidc := range oidcProviders {
174- By ("faking AccessRequest readiness for oidc provider: " + oidc .Name )
174+ By ("fake: AccessRequest readiness for oidc provider: " + oidc .Name )
175175 ar := & clustersv1alpha1.AccessRequest {}
176176 ar .SetName (ctrlutils .K8sNameHash (mcp .Name , oidc .Name ))
177177 ar .SetNamespace (platformNamespace )
@@ -228,16 +228,16 @@ var _ = Describe("ManagedControlPlane Controller", func() {
228228 By ("=== UPDATE ===" )
229229
230230 // change the rolebindings in the MCP spec and remove one OIDC provider
231- By ("update MCP spec" )
231+ By ("updating MCP spec" )
232232 mcp .Spec .IAM .RoleBindings = mcp .Spec .IAM .RoleBindings [:len (mcp .Spec .IAM .RoleBindings )- 1 ]
233233 removedOIDCProviderName := mcp .Spec .IAM .OIDCProviders [len (mcp .Spec .IAM .OIDCProviders )- 1 ].Name
234234 toBeRemovedSecretName := mcp .Status .Access [removedOIDCProviderName ].Name
235235 mcp .Spec .IAM .OIDCProviders = mcp .Spec .IAM .OIDCProviders [:len (mcp .Spec .IAM .OIDCProviders )- 1 ]
236236 Expect (env .Client (onboarding ).Update (env .Ctx , mcp )).To (Succeed ())
237237
238- By ("add finalizers to AccessRequests" )
238+ By ("fake: adding finalizers to AccessRequests" )
239239 for _ , oidc := range oidcProviders {
240- By ("adding finalizer to AccessRequest for oidc provider: " + oidc .Name )
240+ By ("fake: adding finalizer to AccessRequest for oidc provider: " + oidc .Name )
241241 ar := & clustersv1alpha1.AccessRequest {}
242242 ar .SetName (ctrlutils .K8sNameHash (mcp .Name , oidc .Name ))
243243 ar .SetNamespace (platformNamespace )
@@ -302,7 +302,7 @@ var _ = Describe("ManagedControlPlane Controller", func() {
302302 Expect (ar .GetDeletionTimestamp ().IsZero ()).To (BeFalse ())
303303
304304 // remove dummy finalizer from AccessRequest belonging to the removed OIDC provider
305- By ("removing dummy finalizer from AccessRequest for removed OIDC provider: " + removedOIDCProviderName )
305+ By ("fake: removing dummy finalizer from AccessRequest for removed OIDC provider: " + removedOIDCProviderName )
306306 controllerutil .RemoveFinalizer (ar , "dummy" )
307307 Expect (env .Client (platform ).Update (env .Ctx , ar )).To (Succeed ())
308308
@@ -337,6 +337,171 @@ var _ = Describe("ManagedControlPlane Controller", func() {
337337 WithType (corev2alpha1 .ConditionPrefixOIDCAccessReady + removedOIDCProviderName ),
338338 ),
339339 ))
340+
341+ By ("=== DELETE ===" )
342+
343+ // fake some more ClusterRequests
344+ By ("fake: some more ClusterRequests" )
345+ cr2 := & clustersv1alpha1.ClusterRequest {}
346+ cr2 .SetName ("cr2" )
347+ cr2 .SetNamespace (platformNamespace )
348+ cr2 .Finalizers = []string {"dummy" }
349+ Expect (env .Client (platform ).Create (env .Ctx , cr2 )).To (Succeed ())
350+ cr3 := & clustersv1alpha1.ClusterRequest {}
351+ cr3 .SetName ("cr3" )
352+ cr3 .SetNamespace (platformNamespace )
353+ cr3 .Finalizers = []string {"dummy" }
354+ Expect (env .Client (platform ).Create (env .Ctx , cr3 )).To (Succeed ())
355+ mcp .Finalizers = append (mcp .Finalizers , corev2alpha1 .ClusterRequestFinalizerPrefix + cr2 .Name , corev2alpha1 .ClusterRequestFinalizerPrefix + cr3 .Name )
356+ Expect (env .Client (onboarding ).Update (env .Ctx , mcp )).To (Succeed ())
357+
358+ // put a finalizer on the MCP cr
359+ cr .Finalizers = append (cr .Finalizers , "dummy" )
360+ Expect (env .Client (platform ).Update (env .Ctx , cr )).To (Succeed ())
361+
362+ // delete the MCP
363+ By ("deleting the MCP" )
364+ Expect (env .Client (onboarding ).Delete (env .Ctx , mcp )).To (Succeed ())
365+
366+ // reconcile the MCP
367+ // expected outcome:
368+ // - all service resources that depend on the MCP have a deletion timestamp
369+ // - the MCP conditions reflect that it is waiting for services to be deleted
370+ // - neither ClusterRequests nor AccessRequests have deletion timestamps
371+ // - the MCP should be requeued with a short requeueAfter duration
372+ By ("first MCP reconciliation after delete" )
373+ res = env .ShouldReconcile (mcpRec , testutils .RequestFromObject (mcp ))
374+ Expect (env .Client (onboarding ).Get (env .Ctx , client .ObjectKeyFromObject (mcp ), mcp )).To (Succeed ())
375+ Expect (res .RequeueAfter ).To (BeNumerically (">" , 0 ))
376+ Expect (res .RequeueAfter ).To (BeNumerically ("<" , 1 * time .Minute ))
377+ // TODO
378+
379+ // remove service finalizers
380+ By ("fake: removing service finalizers" )
381+ // TODO
382+
383+ // reconcile the MCP again
384+ // expected outcome:
385+ // - all AccessRequests have deletion timestamps
386+ // - all access secrets have been deleted
387+ // - the MCP conditions reflect that it is waiting for AccessRequests to be deleted
388+ // - no ClusterRequests should have deletion timestamps
389+ // - the MCP should be requeued with a short requeueAfter duration
390+ By ("second MCP reconciliation after delete" )
391+ res = env .ShouldReconcile (mcpRec , testutils .RequestFromObject (mcp ))
392+ Expect (env .Client (onboarding ).Get (env .Ctx , client .ObjectKeyFromObject (mcp ), mcp )).To (Succeed ())
393+ Expect (res .RequeueAfter ).To (BeNumerically (">" , 0 ))
394+ Expect (res .RequeueAfter ).To (BeNumerically ("<" , 1 * time .Minute ))
395+ Expect (mcp .Status .Conditions ).To (ContainElements (
396+ MatchCondition (TestCondition ().
397+ WithType (corev2alpha1 .ConditionClusterRequestReady ).
398+ WithStatus (metav1 .ConditionTrue )),
399+ MatchCondition (TestCondition ().
400+ WithType (corev2alpha1 .ConditionAllAccessReady ).
401+ WithStatus (metav1 .ConditionFalse ).
402+ WithReason (cconst .ReasonWaitingForAccessRequest )),
403+ ))
404+ for _ , oidc := range oidcProviders {
405+ By ("verifying AccessRequest and access secret deletion status for oidc provider: " + oidc .Name )
406+ ar := & clustersv1alpha1.AccessRequest {}
407+ ar .SetName (ctrlutils .K8sNameHash (mcp .Name , oidc .Name ))
408+ ar .SetNamespace (platformNamespace )
409+ Expect (env .Client (platform ).Get (env .Ctx , client .ObjectKeyFromObject (ar ), ar )).To (Succeed ())
410+ Expect (ar .DeletionTimestamp .IsZero ()).To (BeFalse ())
411+ Expect (mcp .Status .Conditions ).To (ContainElements (
412+ MatchCondition (TestCondition ().
413+ WithType (corev2alpha1 .ConditionPrefixOIDCAccessReady + oidc .Name ).
414+ WithStatus (metav1 .ConditionFalse ).
415+ WithReason (cconst .ReasonWaitingForAccessRequest ),
416+ ),
417+ ))
418+ }
419+ Expect (mcp .Status .Access ).To (BeEmpty ())
420+ secs := & corev1.SecretList {}
421+ Expect (env .Client (onboarding ).List (env .Ctx , secs , client .InNamespace (mcp .Namespace ))).To (Succeed ())
422+ Expect (secs .Items ).To (BeEmpty ())
423+ for _ , obj := range []client.Object {cr , cr2 , cr3 } {
424+ By ("verifying that ClusterRequest has not been deleted: " + obj .GetName ())
425+ Expect (env .Client (platform ).Get (env .Ctx , client .ObjectKeyFromObject (obj ), obj )).To (Succeed ())
426+ Expect (obj .GetDeletionTimestamp ().IsZero ()).To (BeTrue ())
427+ }
428+
429+ // remove AccessRequest finalizers
430+ By ("fake: removing AccessRequest finalizers" )
431+ for _ , oidc := range oidcProviders {
432+ By ("fake: removing finalizer from AccessRequest for oidc provider: " + oidc .Name )
433+ ar := & clustersv1alpha1.AccessRequest {}
434+ ar .SetName (ctrlutils .K8sNameHash (mcp .Name , oidc .Name ))
435+ ar .SetNamespace (platformNamespace )
436+ Expect (env .Client (platform ).Get (env .Ctx , client .ObjectKeyFromObject (ar ), ar )).To (Succeed ())
437+ controllerutil .RemoveFinalizer (ar , "dummy" )
438+ Expect (env .Client (platform ).Update (env .Ctx , ar )).To (Succeed ())
439+ }
440+
441+ // reconcile the MCP again
442+ // expected outcome:
443+ // - cr2 and cr3 have deletion timestamps, cr has not
444+ // - the AccessRequests are deleted
445+ // - the MCP has a condition stating that it is waiting for the ClusterRequests to be deleted
446+ // - the MCP should be requeued with a short requeueAfter duration
447+ By ("third MCP reconciliation after delete" )
448+ res = env .ShouldReconcile (mcpRec , testutils .RequestFromObject (mcp ))
449+ Expect (env .Client (onboarding ).Get (env .Ctx , client .ObjectKeyFromObject (mcp ), mcp )).To (Succeed ())
450+ Expect (res .RequeueAfter ).To (BeNumerically (">" , 0 ))
451+ Expect (res .RequeueAfter ).To (BeNumerically ("<" , 1 * time .Minute ))
452+ By ("verifying ClusterRequest deletion status" )
453+ for _ , obj := range []client.Object {cr , cr2 , cr3 } {
454+ Expect (env .Client (platform ).Get (env .Ctx , client .ObjectKeyFromObject (obj ), obj )).To (Succeed ())
455+ }
456+ Expect (cr .GetDeletionTimestamp ().IsZero ()).To (BeTrue (), "ClusterRequest should not be marked for deletion" )
457+ Expect (cr2 .GetDeletionTimestamp ().IsZero ()).To (BeFalse ())
458+ Expect (cr3 .GetDeletionTimestamp ().IsZero ()).To (BeFalse ())
459+ By ("verifying AccessRequest deletion" )
460+ for _ , oidc := range oidcProviders {
461+ By ("verifying AccessRequest deletion for oidc provider: " + oidc .Name )
462+ ar := & clustersv1alpha1.AccessRequest {}
463+ ar .SetName (ctrlutils .K8sNameHash (mcp .Name , oidc .Name ))
464+ ar .SetNamespace (platformNamespace )
465+ Expect (env .Client (platform ).Get (env .Ctx , client .ObjectKeyFromObject (ar ), ar )).To (MatchError (apierrors .IsNotFound , "IsNotFound" ))
466+ }
467+
468+ // remove finalizers from cr2 and cr3
469+ By ("fake: removing finalizers from additional ClusterRequests" )
470+ controllerutil .RemoveFinalizer (cr2 , "dummy" )
471+ Expect (env .Client (platform ).Update (env .Ctx , cr2 )).To (Succeed ())
472+ controllerutil .RemoveFinalizer (cr3 , "dummy" )
473+ Expect (env .Client (platform ).Update (env .Ctx , cr3 )).To (Succeed ())
474+
475+ // reconcile the MCP again
476+ // expected outcome:
477+ // - cr2 and cr3 have been deleted
478+ // - cr has a deletion timestamp
479+ // - the MCP has a condition stating that it is waiting for the ClusterRequest to be deleted
480+ // - the MCP should be requeued with a short requeueAfter duration
481+ By ("fourth MCP reconciliation after delete" )
482+ res = env .ShouldReconcile (mcpRec , testutils .RequestFromObject (mcp ))
483+ Expect (env .Client (onboarding ).Get (env .Ctx , client .ObjectKeyFromObject (mcp ), mcp )).To (Succeed ())
484+ Expect (res .RequeueAfter ).To (BeNumerically (">" , 0 ))
485+ Expect (res .RequeueAfter ).To (BeNumerically ("<" , 1 * time .Minute ))
486+ By ("verifying ClusterRequest deletion status" )
487+ Expect (env .Client (platform ).Get (env .Ctx , client .ObjectKeyFromObject (cr ), cr )).To (Succeed ())
488+ Expect (cr .GetDeletionTimestamp ().IsZero ()).To (BeFalse (), "ClusterRequest should be marked for deletion" )
489+
490+ // remove finalizer from cr
491+ By ("fake: removing finalizer from primary ClusterRequest" )
492+ controllerutil .RemoveFinalizer (cr , "dummy" )
493+ Expect (env .Client (platform ).Update (env .Ctx , cr )).To (Succeed ())
494+
495+ // reconcile the MCP again
496+ // expected outcome:
497+ // - cr has been deleted
498+ // - mcp has been deleted
499+ // - the MCP should not be requeued
500+ By ("fifth MCP reconciliation after delete" )
501+ res = env .ShouldReconcile (mcpRec , testutils .RequestFromObject (mcp ))
502+ Expect (env .Client (onboarding ).Get (env .Ctx , client .ObjectKeyFromObject (mcp ), mcp )).To (MatchError (apierrors .IsNotFound , "IsNotFound" ))
503+ Expect (res .RequeueAfter ).To (BeNumerically ("~" , int64 (rec .Config .ReconcileMCPEveryXDays )* 24 * int64 (time .Hour ), int64 (time .Second )))
504+ Expect (env .Client (platform ).Get (env .Ctx , client .ObjectKeyFromObject (cr ), cr )).To (MatchError (apierrors .IsNotFound , "IsNotFound" ))
340505 })
341506
342507})
0 commit comments