Skip to content

Commit 28e2501

Browse files
committed
add unit tests for the mcp controller (part 2)
1 parent 1ac6790 commit 28e2501

File tree

2 files changed

+179
-8
lines changed

2 files changed

+179
-8
lines changed

internal/controllers/managedcontrolplane/controller.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,12 @@ func (r *ManagedControlPlaneReconciler) handleDelete(ctx context.Context, mcp *c
353353
}
354354
}
355355
createCon(corev2alpha1.ConditionMeta, metav1.ConditionTrue, "", "MCP finalizer removed")
356+
if len(mcp.Finalizers) == 0 {
357+
// if we just removed the last finalizer on the MCP
358+
// (which should usually be the case, unless something external added one)
359+
// the MCP is now gone and updating the status will fail
360+
rr.Object = nil
361+
}
356362

357363
return rr
358364
}

internal/controllers/managedcontrolplane/controller_test.go

Lines changed: 173 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ func defaultTestSetup(testDirPathSegments ...string) (*managedcontrolplane.Manag
8181

8282
var _ = 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

Comments
 (0)