Skip to content

Commit 2a410d9

Browse files
authored
test: improve the join and leave tests (#73)
1 parent dc3082d commit 2a410d9

File tree

6 files changed

+86
-23
lines changed

6 files changed

+86
-23
lines changed

pkg/scheduler/watchers/membercluster/controller_integration_test.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424

2525
. "github.com/onsi/ginkgo/v2"
2626
. "github.com/onsi/gomega"
27+
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
2728

2829
corev1 "k8s.io/api/core/v1"
2930
"k8s.io/apimachinery/pkg/api/resource"
@@ -512,19 +513,31 @@ var _ = Describe("scheduler member cluster source controller", Serial, Ordered,
512513
})
513514
})
514515

515-
Context("ready cluster has left", func() {
516+
Context("leaving clusters should not trigger rescheduling until the member cluster object is fully deleted", func() {
516517
BeforeAll(func() {
517518
Consistently(noKeyEnqueuedActual, consistentlyDuration, consistentlyInterval).Should(Succeed(), "Workqueue is not empty")
518519

519520
// Retrieve the cluster.
520521
memberCluster := &clusterv1beta1.MemberCluster{}
521522
Expect(hubClient.Get(ctx, types.NamespacedName{Name: clusterName1}, memberCluster)).To(Succeed(), "Failed to get member cluster")
522-
523-
// Update the spec as leave.
523+
// Delete the cluster to simulate leaving which will not delete the MC as it has finalizers.
524524
Expect(hubClient.Delete(ctx, memberCluster)).To(Succeed(), "Failed to delete member cluster")
525525
})
526526

527-
It("should enqueue all CRPs for cluster left (case 1b)", func() {
527+
It("should not enqueue all CRPs for cluster leaving", func() {
528+
Consistently(noKeyEnqueuedActual, consistentlyDuration, consistentlyInterval).Should(Succeed(), "Workqueue is not empty")
529+
})
530+
531+
It("remove the finalizer", func() {
532+
// Retrieve the cluster.
533+
memberCluster := &clusterv1beta1.MemberCluster{}
534+
Expect(hubClient.Get(ctx, types.NamespacedName{Name: clusterName1}, memberCluster)).To(Succeed(), "Failed to get member cluster")
535+
// Remove the finalizer from cluster.
536+
controllerutil.RemoveFinalizer(memberCluster, placementv1beta1.MemberClusterFinalizer)
537+
Expect(hubClient.Update(ctx, memberCluster)).Should(Succeed(), "Failed to update member cluster taints")
538+
})
539+
540+
It("should enqueue all CRPs for cluster left (case 2c)", func() {
528541
Eventually(allKeysEnqueuedActual, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Keys are not enqueued as expected")
529542
Consistently(allKeysEnqueuedActual, consistentlyDuration, consistentlyInterval).Should(Succeed(), "Keys are not enqueued as expected")
530543
})

pkg/scheduler/watchers/membercluster/suite_test.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"k8s.io/klog/v2"
3131
ctrl "sigs.k8s.io/controller-runtime"
3232
"sigs.k8s.io/controller-runtime/pkg/client"
33+
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
3334
"sigs.k8s.io/controller-runtime/pkg/envtest"
3435
"sigs.k8s.io/controller-runtime/pkg/log/zap"
3536
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
@@ -62,11 +63,13 @@ var (
6263

6364
var (
6465
newMemberCluster = func(name string) *clusterv1beta1.MemberCluster {
65-
return &clusterv1beta1.MemberCluster{
66+
memberCluster := &clusterv1beta1.MemberCluster{
6667
ObjectMeta: metav1.ObjectMeta{
6768
Name: name,
6869
},
6970
}
71+
controllerutil.AddFinalizer(memberCluster, placementv1beta1.MemberClusterFinalizer)
72+
return memberCluster
7073
}
7174

7275
newCRP = func(name string, policy *placementv1beta1.PlacementPolicy) *placementv1beta1.ClusterResourcePlacement {
@@ -143,7 +146,9 @@ func setupResources() {
143146
}
144147

145148
var _ = BeforeSuite(func() {
146-
klog.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
149+
logger := zap.New(zap.UseDevMode(true), zap.WriteTo(GinkgoWriter))
150+
klog.SetLogger(logger)
151+
ctrl.SetLogger(logger)
147152

148153
ctx, cancel = context.WithCancel(context.TODO())
149154

pkg/scheduler/watchers/membercluster/watcher.go

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
8080
// a) the cluster's setup (e.g., its labels) or status (e.g., resource/non-resource properties),
8181
// has changed; and/or
8282
// b) an unexpected development (e.g., agents failing, network partition, etc.) has occurred.
83-
// c) the cluster, which may or may not have resources placed on it, has left the fleet (deleting).
83+
// c) the cluster, which may or may not have resources placed on it, has left the fleet (deleted).
8484
//
8585
// Among the cases,
8686
//
@@ -123,9 +123,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
123123
memberClusterGetErr := r.Client.Get(ctx, memberClusterKey, memberCluster)
124124
switch {
125125
case errors.IsNotFound(memberClusterGetErr):
126-
// On very unlikely occasions, it could happen that the member cluster is deleted
127-
// before this controller gets a chance to process it, it may happen when a member cluster
128-
// leaves the fleet. In such cases, this controller will request the scheduler to check
126+
// This could happen when the member cluster is deleted. In this case, controller will request the scheduler to check
129127
// all CRPs just in case.
130128
isMemberClusterMissing = true
131129
case memberClusterGetErr != nil:
@@ -185,10 +183,9 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
185183
return false
186184
},
187185
DeleteFunc: func(e event.DeleteEvent) bool {
188-
// Ignore deletion events; the removal of a cluster is first signaled by adding a deleteTimeStamp,
189-
// which is an update event
190-
klog.V(3).InfoS("Ignoring delete events for member cluster objects", "eventObject", klog.KObj(e.Object))
191-
return false
186+
// We only notify the scheduler when a member cluster is deleted which means the member agent has finished the leaving process.
187+
klog.V(2).InfoS("Member cluster object is deleted", "eventObject", klog.KObj(e.Object))
188+
return true
192189
},
193190
UpdateFunc: func(e event.UpdateEvent) bool {
194191
// Check if the update event is valid.
@@ -208,9 +205,9 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
208205

209206
clusterKObj := klog.KObj(newCluster)
210207
// The cluster is being deleted.
211-
if oldCluster.GetDeletionTimestamp().IsZero() && !newCluster.GetDeletionTimestamp().IsZero() {
212-
klog.V(2).InfoS("A member cluster is leaving the fleet", "memberCluster", clusterKObj)
213-
return true
208+
if !newCluster.GetDeletionTimestamp().IsZero() {
209+
klog.V(2).InfoS("A member cluster is leaving the fleet, ignore until it is left", "memberCluster", clusterKObj)
210+
return false
214211
}
215212

216213
// Capture label changes.

test/e2e/join_and_leave_test.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ var _ = Describe("Test member cluster join and leave flow", Ordered, Serial, fun
7272
},
7373
{
7474
Group: placementv1beta1.GroupVersion.Group,
75-
Kind: "ClusterResourceEnvelope",
75+
Kind: placementv1beta1.ClusterResourceEnvelopeKind,
7676
Version: placementv1beta1.GroupVersion.Version,
7777
Name: testClusterResourceEnvelope.Name,
7878
},
@@ -212,12 +212,20 @@ var _ = Describe("Test member cluster join and leave flow", Ordered, Serial, fun
212212
checkIfAllMemberClustersHaveLeft()
213213
})
214214

215-
It("should update CRP status to not placing any resources since all clusters are left", func() {
215+
It("Should update CRP status to not placing any resources since all clusters are left", func() {
216216
// resourceQuota is enveloped so it's not trackable yet
217217
crpStatusUpdatedActual := customizedCRPStatusUpdatedActual(crpName, wantSelectedResources, nil, nil, "0", false)
218218
Eventually(crpStatusUpdatedActual, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to update CRP status as expected")
219219
})
220220

221+
It("Valdiating if the resources are still on all member clusters", func() {
222+
for idx := range allMemberClusters {
223+
memberCluster := allMemberClusters[idx]
224+
workResourcesPlacedActual := checkAllResourcesPlacement(memberCluster)
225+
Consistently(workResourcesPlacedActual, 3*consistentlyDuration, consistentlyInterval).Should(Succeed(), "Failed to place work resources on member cluster %s", memberCluster.ClusterName)
226+
}
227+
})
228+
221229
It("Should be able to rejoin the cluster", func() {
222230
By("rejoin all the clusters without deleting the CRP")
223231
setAllMemberClustersToJoin()

test/e2e/scheduler_watchers_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,17 @@ var _ = Describe("responding to specific member cluster changes", func() {
589589
Consistently(crpStatusUpdatedActual, consistentlyDuration, consistentlyInterval).Should(Succeed(), "Failed to update CRP status as expected")
590590
})
591591

592+
It("can mark the cluster as leaving", func() {
593+
markMemberClusterAsLeaving(fakeClusterName1ForWatcherTests)
594+
})
595+
596+
It("should not remove the leaving cluster from the scheduling decision", func() {
597+
targetClusterNames := allMemberClusterNames
598+
targetClusterNames = append(targetClusterNames, fakeClusterName1ForWatcherTests)
599+
crpStatusUpdatedActual := crpStatusUpdatedActual(workResourceIdentifiers(), targetClusterNames, nil, "0")
600+
Consistently(crpStatusUpdatedActual, consistentlyDuration, consistentlyInterval).Should(Succeed(), "Failed to update CRP status as expected")
601+
})
602+
592603
It("can mark the cluster as left", func() {
593604
markMemberClusterAsLeft(fakeClusterName1ForWatcherTests)
594605
})
@@ -1515,6 +1526,17 @@ var _ = Describe("responding to specific member cluster changes", func() {
15151526
Consistently(crpStatusUpdatedActual, consistentlyDuration, consistentlyInterval).Should(Succeed(), "Failed to update CRP status as expected")
15161527
})
15171528

1529+
It("can mark the cluster as leaving", func() {
1530+
markMemberClusterAsLeaving(fakeClusterName1ForWatcherTests)
1531+
})
1532+
1533+
It("should not remove the leaving cluster from the scheduling decision", func() {
1534+
targetClusterNames := allMemberClusterNames
1535+
targetClusterNames = append(targetClusterNames, fakeClusterName1ForWatcherTests)
1536+
crpStatusUpdatedActual := crpStatusUpdatedActual(workResourceIdentifiers(), targetClusterNames, nil, "0")
1537+
Consistently(crpStatusUpdatedActual, consistentlyDuration, consistentlyInterval).Should(Succeed(), "Failed to update CRP status as expected")
1538+
})
1539+
15181540
It("can mark the cluster as left", func() {
15191541
markMemberClusterAsLeft(fakeClusterName1ForWatcherTests)
15201542
})

test/e2e/utils_test.go

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,8 @@ func markMemberClusterAsUnhealthy(name string) {
166166
}, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to mark member cluster as unhealthy")
167167
}
168168

169-
// markMemberClusterAsLeft marks the specified member cluster as left.
170-
func markMemberClusterAsLeft(name string) {
169+
// markMemberClusterAsLeaving marks the specified member cluster as leaving.
170+
func markMemberClusterAsLeaving(name string) {
171171
mcObj := &clusterv1beta1.MemberCluster{}
172172
Eventually(func() error {
173173
// Add a custom deletion blocker finalizer to the member cluster.
@@ -177,9 +177,27 @@ func markMemberClusterAsLeft(name string) {
177177

178178
mcObj.Finalizers = append(mcObj.Finalizers, customDeletionBlockerFinalizer)
179179
return hubClient.Update(ctx, mcObj)
180-
}, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to mark member cluster as left")
180+
}, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to add finalizer")
181+
182+
Expect(hubClient.Delete(ctx, mcObj)).To(Succeed(), "Failed to mark member cluster as leaving")
183+
}
184+
185+
// markMemberClusterAsLeft deletes the specified member cluster.
186+
func markMemberClusterAsLeft(name string) {
187+
mcObj := &clusterv1beta1.MemberCluster{}
188+
Eventually(func() error {
189+
// remove finalizer to the member cluster.
190+
if err := hubClient.Get(ctx, types.NamespacedName{Name: name}, mcObj); err != nil {
191+
return err
192+
}
193+
if len(mcObj.Finalizers) > 0 {
194+
mcObj.Finalizers = []string{}
195+
return hubClient.Update(ctx, mcObj)
196+
}
197+
return nil
198+
}, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to remove finalizer")
181199

182-
Expect(hubClient.Delete(ctx, mcObj)).To(Succeed(), "Failed to delete member cluster")
200+
Expect(hubClient.Delete(ctx, mcObj)).To(SatisfyAny(Succeed(), utils.NotFoundMatcher{}), "Failed to delete member cluster")
183201
}
184202

185203
// setAllMemberClustersToJoin creates a MemberCluster object for each member cluster.

0 commit comments

Comments
 (0)