diff --git a/pkg/controllers/rollout/controller.go b/pkg/controllers/rollout/controller.go index 3dfdf0356..d2b1674d0 100644 --- a/pkg/controllers/rollout/controller.go +++ b/pkg/controllers/rollout/controller.go @@ -521,6 +521,7 @@ func calculateMaxToAdd(crp *fleetv1beta1.ClusterResourcePlacement, targetNumber upperBoundReadyNumber, "maxNumberOfBindingsToAdd", maxNumberToAdd) return maxNumberToAdd } + func (r *Reconciler) calculateRealTarget(crp *fleetv1beta1.ClusterResourcePlacement, schedulerTargetedBinds []*fleetv1beta1.ClusterResourceBinding) int { crpKObj := klog.KObj(crp) // calculate the target number of bindings diff --git a/test/e2e/actuals_test.go b/test/e2e/actuals_test.go index a9698c4e0..dddf87dd4 100644 --- a/test/e2e/actuals_test.go +++ b/test/e2e/actuals_test.go @@ -420,7 +420,7 @@ func resourcePlacementRolloutCompletedConditions(generation int64, resourceIsTra } } -func resourcePlacementRolloutFailedConditions(generation int64) []metav1.Condition { +func resourcePlacementScheduleFailedConditions(generation int64) []metav1.Condition { return []metav1.Condition{ { Type: string(placementv1beta1.ResourceScheduledConditionType), @@ -689,7 +689,7 @@ func customizedCRPStatusUpdatedActual(crpName string, } for i := 0; i < len(wantUnselectedClusters); i++ { wantPlacementStatus = append(wantPlacementStatus, placementv1beta1.ResourcePlacementStatus{ - Conditions: resourcePlacementRolloutFailedConditions(crp.Generation), + Conditions: resourcePlacementScheduleFailedConditions(crp.Generation), }) } diff --git a/test/e2e/rollout_test.go b/test/e2e/rollout_test.go index 1db441dfa..e1f117049 100644 --- a/test/e2e/rollout_test.go +++ b/test/e2e/rollout_test.go @@ -472,6 +472,122 @@ var _ = Describe("placing wrapped resources using a CRP", Ordered, func() { }) }) + FContext("Test a CRP place workload successful and update it to be failed and then delete the resource snapshot,"+ + "rollout should eventually be successful after we correct the image", Ordered, func() { + crpName := fmt.Sprintf(crpNameTemplate, GinkgoParallelProcess()) + workNamespace := appNamespace() + var wantSelectedResources []placementv1beta1.ResourceIdentifier + var testDeployment appv1.Deployment + + BeforeAll(func() { + // Create the test resources. + readDeploymentTestManifest(&testDeployment) + wantSelectedResources = []placementv1beta1.ResourceIdentifier{ + { + Kind: utils.NamespaceKind, + Name: workNamespace.Name, + Version: corev1.SchemeGroupVersion.Version, + }, + { + Group: appv1.SchemeGroupVersion.Group, + Version: appv1.SchemeGroupVersion.Version, + Kind: utils.DeploymentKind, + Name: testDeployment.Name, + Namespace: workNamespace.Name, + }, + } + }) + + It("create the deployment resource in the namespace", func() { + Expect(hubClient.Create(ctx, &workNamespace)).To(Succeed(), "Failed to create namespace %s", workNamespace.Name) + testDeployment.Namespace = workNamespace.Name + Expect(hubClient.Create(ctx, &testDeployment)).To(Succeed(), "Failed to create test deployment %s", testDeployment.Name) + }) + + It("create the CRP that select the namespace", func() { + crp := buildCRPForSafeRollout() + crp.Spec.RevisionHistoryLimit = ptr.To(int32(1)) + Expect(hubClient.Create(ctx, crp)).To(Succeed(), "Failed to create CRP") + }) + + It("should update CRP status as expected", func() { + crpStatusUpdatedActual := crpStatusUpdatedActual(wantSelectedResources, allMemberClusterNames, nil, "0") + Eventually(crpStatusUpdatedActual, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to update CRP status as expected") + }) + + It("should place the resources on all member clusters", func() { + for idx := range allMemberClusters { + memberCluster := allMemberClusters[idx] + workResourcesPlacedActual := waitForDeploymentPlacementToReady(memberCluster, &testDeployment) + Eventually(workResourcesPlacedActual, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to place work resources on member cluster %s", memberCluster.ClusterName) + } + }) + + It("change the image name in deployment, to make it unavailable", func() { + Eventually(func() error { + var dep appv1.Deployment + err := hubClient.Get(ctx, types.NamespacedName{Name: testDeployment.Name, Namespace: testDeployment.Namespace}, &dep) + if err != nil { + return err + } + dep.Spec.Template.Spec.Containers[0].Image = randomImageName + return hubClient.Update(ctx, &dep) + }, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to change the image name in deployment") + }) + + It("should update CRP status on deployment failed as expected", func() { + failedDeploymentResourceIdentifier := placementv1beta1.ResourceIdentifier{ + Group: appv1.SchemeGroupVersion.Group, + Version: appv1.SchemeGroupVersion.Version, + Kind: utils.DeploymentKind, + Name: testDeployment.Name, + Namespace: testDeployment.Namespace, + } + crpStatusActual := safeRolloutWorkloadCRPStatusUpdatedActual(wantSelectedResources, failedDeploymentResourceIdentifier, allMemberClusterNames, "1", 2) + Eventually(crpStatusActual, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to update CRP status as expected") + }) + + It("change the image name in deployment, to roll over the resourcesnapshot", func() { + crsList := &placementv1beta1.ClusterResourceSnapshotList{} + Expect(hubClient.List(ctx, crsList, client.MatchingLabels{placementv1beta1.CRPTrackingLabel: crpName})).Should(Succeed(), "Failed to list the resourcesnapshot") + Expect(len(crsList.Items) == 1).Should(BeTrue()) + oldCRS := crsList.Items[0].Name + Expect(hubClient.Get(ctx, types.NamespacedName{Name: testDeployment.Name, Namespace: testDeployment.Namespace}, &testDeployment)).Should(Succeed(), "Failed to get deployment") + testDeployment.Spec.Template.Spec.Containers[0].Image = "extra-snapshot" + Expect(hubClient.Update(ctx, &testDeployment)).Should(Succeed(), "Failed to change the image name in deployment") + // wait for the new resourcesnapshot to be created + Eventually(func() bool { + Expect(hubClient.List(ctx, crsList, client.MatchingLabels{placementv1beta1.CRPTrackingLabel: crpName})).Should(Succeed(), "Failed to list the resourcesnapshot") + Expect(len(crsList.Items) == 1).Should(BeTrue()) + return crsList.Items[0].Name != oldCRS + }, eventuallyDuration, eventuallyInterval).Should(BeTrue(), "Failed to remove the old resourcensnapshot") + }) + + It("change the image name in deployment, to make it available again", func() { + Eventually(func() error { + err := hubClient.Get(ctx, types.NamespacedName{Name: testDeployment.Name, Namespace: testDeployment.Namespace}, &testDeployment) + if err != nil { + return err + } + testDeployment.Spec.Template.Spec.Containers[0].Image = "1.26.2" + return hubClient.Update(ctx, &testDeployment) + }, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to change the image name in deployment") + }) + + It("should place the resources on all member clusters", func() { + for idx := range allMemberClusters { + memberCluster := allMemberClusters[idx] + workResourcesPlacedActual := waitForDeploymentPlacementToReady(memberCluster, &testDeployment) + Eventually(workResourcesPlacedActual, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to place work resources on member cluster %s", memberCluster.ClusterName) + } + }) + + AfterAll(func() { + // Remove the custom deletion blocker finalizer from the CRP. + ensureCRPAndRelatedResourcesDeletion(crpName, allMemberClusters) + }) + }) + Context("Test a CRP place workload objects successfully, don't block rollout based on job availability", Ordered, func() { crpName := fmt.Sprintf(crpNameTemplate, GinkgoParallelProcess()) workNamespace := appNamespace()