Skip to content

Commit 71fcf83

Browse files
committed
test: add e2e test
Signed-off-by: Zhiying Lin <zhiyingl456@gmail.com>
1 parent 2fd4460 commit 71fcf83

File tree

1 file changed

+163
-0
lines changed

1 file changed

+163
-0
lines changed

test/e2e/mixed_placement_test.go

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ import (
2424
. "github.com/onsi/gomega"
2525
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2626
"k8s.io/apimachinery/pkg/types"
27+
"k8s.io/utils/ptr"
2728

29+
clusterv1beta1 "github.com/kubefleet-dev/kubefleet/apis/cluster/v1beta1"
2830
placementv1beta1 "github.com/kubefleet-dev/kubefleet/apis/placement/v1beta1"
2931
"github.com/kubefleet-dev/kubefleet/pkg/controllers/workapplier"
3032
)
@@ -373,3 +375,164 @@ var _ = Describe("mixed ClusterResourcePlacement and ResourcePlacement negative
373375
})
374376
})
375377
})
378+
379+
var _ = Describe("mixed ClusterResourcePlacement and ResourcePlacement positive test cases", Label("resourceplacement"), func() {
380+
crpName := fmt.Sprintf(crpNameTemplate, GinkgoParallelProcess())
381+
rpName := fmt.Sprintf(rpNameTemplate, GinkgoParallelProcess())
382+
workNamespaceName := fmt.Sprintf(workNamespaceNameTemplate, GinkgoParallelProcess())
383+
wantSelectedClusters := []string{memberCluster3WestProdName}
384+
wantUnscheduledClusters := []string{memberCluster1EastProdName, memberCluster2EastCanaryName}
385+
386+
Context("coupling CRP and RP using cluster labeling", Ordered, func() {
387+
BeforeAll(func() {
388+
By("creating work resources")
389+
createWorkResources()
390+
})
391+
392+
AfterAll(func() {
393+
ensureRPAndRelatedResourcesDeleted(types.NamespacedName{Name: rpName, Namespace: workNamespaceName}, allMemberClusters)
394+
ensureCRPAndRelatedResourcesDeleted(crpName, allMemberClusters)
395+
396+
By("removing labels from member clusters")
397+
Eventually(func() error {
398+
for _, clusterName := range wantSelectedClusters {
399+
mc := &clusterv1beta1.MemberCluster{}
400+
if err := hubClient.Get(ctx, types.NamespacedName{Name: clusterName}, mc); err != nil {
401+
return fmt.Errorf("failed to get member cluster %s: %w", clusterName, err)
402+
}
403+
404+
if mc.Labels != nil {
405+
delete(mc.Labels, workNamespaceName)
406+
if err := hubClient.Update(ctx, mc); err != nil {
407+
return fmt.Errorf("failed to update member cluster %s: %w", clusterName, err)
408+
}
409+
}
410+
}
411+
return nil
412+
}, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to remove labels from member clusters")
413+
})
414+
415+
It("picking N clusters with no affinities/topology spread constraints (pick by cluster names in alphanumeric order)", func() {
416+
crp := &placementv1beta1.ClusterResourcePlacement{
417+
ObjectMeta: metav1.ObjectMeta{
418+
Name: crpName,
419+
// Add a custom finalizer; this would allow us to better observe
420+
// the behavior of the controllers.
421+
Finalizers: []string{customDeletionBlockerFinalizer},
422+
},
423+
Spec: placementv1beta1.PlacementSpec{
424+
ResourceSelectors: namespaceOnlySelector(),
425+
Policy: &placementv1beta1.PlacementPolicy{
426+
PlacementType: placementv1beta1.PickNPlacementType,
427+
NumberOfClusters: ptr.To(int32(1)),
428+
},
429+
Strategy: placementv1beta1.RolloutStrategy{
430+
Type: placementv1beta1.RollingUpdateRolloutStrategyType,
431+
RollingUpdate: &placementv1beta1.RollingUpdateConfig{
432+
UnavailablePeriodSeconds: ptr.To(2),
433+
},
434+
},
435+
},
436+
}
437+
Expect(hubClient.Create(ctx, crp)).To(Succeed(), "Failed to create CRP %s", crpName)
438+
})
439+
440+
It("should update CRP status as expected", func() {
441+
crpStatusUpdatedActual := crpStatusUpdatedActual(workNamespaceIdentifiers(), wantSelectedClusters, nil, "0")
442+
Eventually(crpStatusUpdatedActual, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to update CRP status as expected")
443+
})
444+
445+
It("add labels to member clusters based on CRP placement decisions", func() {
446+
Eventually(func() error {
447+
// Add labels to the selected clusters
448+
for _, clusterName := range wantSelectedClusters {
449+
mc := &clusterv1beta1.MemberCluster{}
450+
if err := hubClient.Get(ctx, types.NamespacedName{Name: clusterName}, mc); err != nil {
451+
return fmt.Errorf("failed to get member cluster %s: %w", clusterName, err)
452+
}
453+
454+
if mc.Labels == nil {
455+
mc.Labels = make(map[string]string)
456+
}
457+
mc.Labels[workNamespaceName] = "true"
458+
459+
if err := hubClient.Update(ctx, mc); err != nil {
460+
return fmt.Errorf("failed to update member cluster %s: %w", clusterName, err)
461+
}
462+
}
463+
return nil
464+
}, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to add labels to member clusters")
465+
})
466+
467+
It("should create an RP with pickN policy using cluster labels", func() {
468+
rp := &placementv1beta1.ResourcePlacement{
469+
ObjectMeta: metav1.ObjectMeta{
470+
Name: rpName,
471+
Namespace: workNamespaceName,
472+
Finalizers: []string{customDeletionBlockerFinalizer},
473+
},
474+
Spec: placementv1beta1.PlacementSpec{
475+
ResourceSelectors: configMapSelector(),
476+
Policy: &placementv1beta1.PlacementPolicy{
477+
PlacementType: placementv1beta1.PickNPlacementType,
478+
NumberOfClusters: ptr.To(int32(3)),
479+
Affinity: &placementv1beta1.Affinity{
480+
ClusterAffinity: &placementv1beta1.ClusterAffinity{
481+
RequiredDuringSchedulingIgnoredDuringExecution: &placementv1beta1.ClusterSelector{
482+
ClusterSelectorTerms: []placementv1beta1.ClusterSelectorTerm{
483+
{
484+
LabelSelector: &metav1.LabelSelector{
485+
MatchLabels: map[string]string{
486+
workNamespaceName: "true",
487+
},
488+
},
489+
},
490+
},
491+
},
492+
},
493+
},
494+
},
495+
},
496+
}
497+
Expect(hubClient.Create(ctx, rp)).To(Succeed(), "Failed to create RP %s", rpName)
498+
})
499+
500+
It("should update RP status as expected", func() {
501+
rpStatusUpdatedActual := func() error {
502+
rpKey := types.NamespacedName{Name: rpName, Namespace: workNamespaceName}
503+
return customizedPlacementStatusUpdatedActual(rpKey, appConfigMapIdentifiers(), wantSelectedClusters, wantUnscheduledClusters, "0", true)()
504+
}
505+
Eventually(rpStatusUpdatedActual, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to update RP status as expected")
506+
})
507+
508+
It("should place resources on the picked clusters", func() {
509+
resourcePlacedActual := workNamespaceAndConfigMapPlacedOnClusterActual(memberCluster3WestProd)
510+
Eventually(resourcePlacedActual, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to place resources on the picked clusters")
511+
})
512+
513+
It("update RP to pick 1 cluster instead of 3", func() {
514+
Eventually(func() error {
515+
rp := &placementv1beta1.ResourcePlacement{}
516+
if err := hubClient.Get(ctx, types.NamespacedName{Name: rpName, Namespace: workNamespaceName}, rp); err != nil {
517+
return fmt.Errorf("failed to get RP %s: %w", rpName, err)
518+
}
519+
520+
rp.Spec.Policy.NumberOfClusters = ptr.To(int32(1))
521+
if err := hubClient.Update(ctx, rp); err != nil {
522+
return fmt.Errorf("failed to update RP %s: %w", rpName, err)
523+
}
524+
return nil
525+
}, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to update RP to pick 1 cluster")
526+
})
527+
528+
It("should update RP status as expected", func() {
529+
rpStatusUpdatedActual := rpStatusUpdatedActual(appConfigMapIdentifiers(), wantSelectedClusters, nil, "0")
530+
Eventually(rpStatusUpdatedActual, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to update RP status as expected")
531+
})
532+
533+
It("should still place resources only on the selected cluster", func() {
534+
resourcePlacedActual := workNamespaceAndConfigMapPlacedOnClusterActual(memberCluster3WestProd)
535+
Eventually(resourcePlacedActual, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to place resources on the selected cluster after update")
536+
})
537+
})
538+
})

0 commit comments

Comments
 (0)