diff --git a/test/e2e/framework/deployment.go b/test/e2e/framework/deployment.go index f94f50a59..a6f518336 100644 --- a/test/e2e/framework/deployment.go +++ b/test/e2e/framework/deployment.go @@ -28,6 +28,36 @@ func CreateDeployment(client kubernetes.Interface, deployment *appsv1.Deployment }) } +// UpdateDeploymentPaused update deployment's paused. +func UpdateDeploymentPaused(client kubernetes.Interface, deployment *appsv1.Deployment, paused bool) { + ginkgo.By(fmt.Sprintf("Update Deployment(%s/%s)", deployment.Namespace, deployment.Name), func() { + gomega.Eventually(func() error { + deploy, err := client.AppsV1().Deployments(deployment.Namespace).Get(context.TODO(), deployment.Name, metav1.GetOptions{}) + if err != nil { + return err + } + deploy.Spec.Paused = paused + _, err = client.AppsV1().Deployments(deploy.Namespace).Update(context.TODO(), deploy, metav1.UpdateOptions{}) + return err + }, pollTimeout, pollInterval).ShouldNot(gomega.HaveOccurred()) + }) +} + +// UpdateDeploymentStatus updates the Deployment status. +func UpdateDeploymentStatus(client kubernetes.Interface, deployment *appsv1.Deployment) { + ginkgo.By(fmt.Sprintf("Update Deployment(%s/%s) status", deployment.Namespace, deployment.Name), func() { + gomega.Eventually(func() error { + deploy, err := client.AppsV1().Deployments(deployment.Namespace).Get(context.TODO(), deployment.Name, metav1.GetOptions{}) + if err != nil { + return err + } + deploy.Status = deployment.Status + _, err = client.AppsV1().Deployments(deploy.Namespace).UpdateStatus(context.TODO(), deploy, metav1.UpdateOptions{}) + return err + }, pollTimeout, pollInterval).ShouldNot(gomega.HaveOccurred()) + }) +} + // RemoveDeployment delete Deployment. func RemoveDeployment(client kubernetes.Interface, namespace, name string) { ginkgo.By(fmt.Sprintf("Removing Deployment(%s/%s)", namespace, name), func() { @@ -92,9 +122,13 @@ func WaitDeploymentDisappearOnClusters(clusters []string, namespace, name string // UpdateDeploymentReplicas update deployment's replicas. func UpdateDeploymentReplicas(client kubernetes.Interface, deployment *appsv1.Deployment, replicas int32) { ginkgo.By(fmt.Sprintf("Updating Deployment(%s/%s)'s replicas to %d", deployment.Namespace, deployment.Name, replicas), func() { - deployment.Spec.Replicas = &replicas gomega.Eventually(func() error { - _, err := client.AppsV1().Deployments(deployment.Namespace).Update(context.TODO(), deployment, metav1.UpdateOptions{}) + deploy, err := client.AppsV1().Deployments(deployment.Namespace).Get(context.TODO(), deployment.Name, metav1.GetOptions{}) + if err != nil { + return err + } + deploy.Spec.Replicas = &replicas + _, err = client.AppsV1().Deployments(deploy.Namespace).Update(context.TODO(), deploy, metav1.UpdateOptions{}) return err }, pollTimeout, pollInterval).ShouldNot(gomega.HaveOccurred()) }) @@ -103,9 +137,13 @@ func UpdateDeploymentReplicas(client kubernetes.Interface, deployment *appsv1.De // UpdateDeploymentAnnotations update deployment's annotations. func UpdateDeploymentAnnotations(client kubernetes.Interface, deployment *appsv1.Deployment, annotations map[string]string) { ginkgo.By(fmt.Sprintf("Updating Deployment(%s/%s)'s annotations to %v", deployment.Namespace, deployment.Name, annotations), func() { - deployment.Annotations = annotations gomega.Eventually(func() error { - _, err := client.AppsV1().Deployments(deployment.Namespace).Update(context.TODO(), deployment, metav1.UpdateOptions{}) + deploy, err := client.AppsV1().Deployments(deployment.Namespace).Get(context.TODO(), deployment.Name, metav1.GetOptions{}) + if err != nil { + return err + } + deploy.Annotations = annotations + _, err = client.AppsV1().Deployments(deploy.Namespace).Update(context.TODO(), deploy, metav1.UpdateOptions{}) return err }, pollTimeout, pollInterval).ShouldNot(gomega.HaveOccurred()) }) @@ -114,9 +152,13 @@ func UpdateDeploymentAnnotations(client kubernetes.Interface, deployment *appsv1 // UpdateDeploymentVolumes update Deployment's volumes. func UpdateDeploymentVolumes(client kubernetes.Interface, deployment *appsv1.Deployment, volumes []corev1.Volume) { ginkgo.By(fmt.Sprintf("Updating Deployment(%s/%s)'s volumes", deployment.Namespace, deployment.Name), func() { - deployment.Spec.Template.Spec.Volumes = volumes gomega.Eventually(func() error { - _, err := client.AppsV1().Deployments(deployment.Namespace).Update(context.TODO(), deployment, metav1.UpdateOptions{}) + deploy, err := client.AppsV1().Deployments(deployment.Namespace).Get(context.TODO(), deployment.Name, metav1.GetOptions{}) + if err != nil { + return err + } + deploy.Spec.Template.Spec.Volumes = volumes + _, err = client.AppsV1().Deployments(deploy.Namespace).Update(context.TODO(), deploy, metav1.UpdateOptions{}) return err }, pollTimeout, pollInterval).ShouldNot(gomega.HaveOccurred()) }) @@ -125,9 +167,13 @@ func UpdateDeploymentVolumes(client kubernetes.Interface, deployment *appsv1.Dep // UpdateDeploymentServiceAccountName update Deployment's serviceAccountName. func UpdateDeploymentServiceAccountName(client kubernetes.Interface, deployment *appsv1.Deployment, serviceAccountName string) { ginkgo.By(fmt.Sprintf("Updating Deployment(%s/%s)'s serviceAccountName", deployment.Namespace, deployment.Name), func() { - deployment.Spec.Template.Spec.ServiceAccountName = serviceAccountName gomega.Eventually(func() error { - _, err := client.AppsV1().Deployments(deployment.Namespace).Update(context.TODO(), deployment, metav1.UpdateOptions{}) + deploy, err := client.AppsV1().Deployments(deployment.Namespace).Get(context.TODO(), deployment.Name, metav1.GetOptions{}) + if err != nil { + return err + } + deploy.Spec.Template.Spec.ServiceAccountName = serviceAccountName + _, err = client.AppsV1().Deployments(deploy.Namespace).Update(context.TODO(), deploy, metav1.UpdateOptions{}) return err }, pollTimeout, pollInterval).ShouldNot(gomega.HaveOccurred()) }) diff --git a/test/e2e/resourceinterpreter_test.go b/test/e2e/resourceinterpreter_test.go index c327323ff..dc6b79975 100644 --- a/test/e2e/resourceinterpreter_test.go +++ b/test/e2e/resourceinterpreter_test.go @@ -422,4 +422,190 @@ end`, } }) }) + + ginkgo.Context("InterpreterOperation Retain testing", func() { + var waitTime time.Duration + var updatedPaused bool + + ginkgo.BeforeEach(func() { + waitTime = 5 * time.Second + updatedPaused = true + customization = testhelper.NewResourceInterpreterCustomization( + "interpreter-customization"+rand.String(RandomStrLength), + configv1alpha1.CustomizationTarget{ + APIVersion: "apps/v1", + Kind: "Deployment", + }, + configv1alpha1.CustomizationRules{ + Retention: &configv1alpha1.LocalValueRetention{ + LuaScript: ` +function Retain(desiredObj, observedObj) + desiredObj.spec.paused = observedObj.spec.paused + return desiredObj +end`, + }, + }) + }) + + ginkgo.It("Retain testing", func() { + ginkgo.By("update deployment's spec.paused to true", func() { + clusterClient := framework.GetClusterClient(targetCluster) + gomega.Expect(clusterClient).ShouldNot(gomega.BeNil()) + var memberDeploy *appsv1.Deployment + framework.WaitDeploymentPresentOnClusterFitWith(targetCluster, deployment.Namespace, deployment.Name, + func(deployment *appsv1.Deployment) bool { + memberDeploy = deployment + return true + }) + framework.UpdateDeploymentPaused(clusterClient, memberDeploy, updatedPaused) + }) + + // Wait executeController to reconcile then check if it is retained + time.Sleep(waitTime) + ginkgo.By("check if deployment's spec.paused is retained", func() { + gomega.Eventually(func(g gomega.Gomega) bool { + var memberDeployment *appsv1.Deployment + framework.WaitDeploymentPresentOnClusterFitWith(targetCluster, deployment.Namespace, deployment.Name, + func(deployment *appsv1.Deployment) bool { + memberDeployment = deployment + return true + }) + return memberDeployment.Spec.Paused + }, pollTimeout, pollInterval).Should(gomega.Equal(updatedPaused)) + }) + }) + }) + + ginkgo.Context("InterpreterOperation AggregateStatus testing", func() { + ginkgo.BeforeEach(func() { + customization = testhelper.NewResourceInterpreterCustomization( + "interpreter-customization"+rand.String(RandomStrLength), + configv1alpha1.CustomizationTarget{ + APIVersion: "apps/v1", + Kind: "Deployment", + }, + configv1alpha1.CustomizationRules{ + StatusAggregation: &configv1alpha1.StatusAggregation{ + LuaScript: ` +function AggregateStatus(desiredObj, statusItems) + if statusItems == nil then + return desiredObj + end + if desiredObj.status == nil then + desiredObj.status = {} + end + replicas = 0 + for i = 1, #statusItems do + if statusItems[i].status ~= nil and statusItems[i].status.replicas ~= nil then + replicas = replicas + statusItems[i].status.replicas + 1 + end + end + desiredObj.status.replicas = replicas + return desiredObj +end`, + }, + }) + }) + ginkgo.It("AggregateStatus testing", func() { + ginkgo.By("check whether the deployment status can be correctly collected", func() { + // Only in the current case, a special example is constructed to distinguish the build-in logic. + wantedReplicas := *deployment.Spec.Replicas + 1 + gomega.Eventually(func() bool { + var currentDeployment *appsv1.Deployment + framework.WaitDeploymentGetByClientFitWith(kubeClient, deployment.Namespace, deployment.Name, func(deployment *appsv1.Deployment) bool { + currentDeployment = deployment + return true + }) + klog.Infof("deployment(%s/%s) replicas: %d, wanted replicas: %d", deployment.Namespace, deployment.Name, currentDeployment.Status.Replicas, wantedReplicas) + return currentDeployment.Status.Replicas == wantedReplicas + }, pollTimeout, pollInterval).Should(gomega.BeTrue()) + }) + }) + }) + + ginkgo.Context("InterpreterOperation InterpretStatus testing", func() { + ginkgo.BeforeEach(func() { + customization = testhelper.NewResourceInterpreterCustomization( + "interpreter-customization"+rand.String(RandomStrLength), + configv1alpha1.CustomizationTarget{ + APIVersion: "apps/v1", + Kind: "Deployment", + }, + configv1alpha1.CustomizationRules{ + StatusReflection: &configv1alpha1.StatusReflection{ + LuaScript: ` +function ReflectStatus (observedObj) + if observedObj.status == nil then + return nil + end + return observedObj.status +end`, + }, + }) + }) + ginkgo.It("InterpretStatus testing", func() { + gomega.Eventually(func(g gomega.Gomega) bool { + deploy, err := kubeClient.AppsV1().Deployments(deployment.Namespace).Get(context.TODO(), deployment.Name, metav1.GetOptions{}) + g.Expect(err).NotTo(gomega.HaveOccurred()) + return deploy.Status.ReadyReplicas == *deploy.Spec.Replicas + }, pollTimeout, pollInterval).Should(gomega.BeTrue()) + + }) + }) + + ginkgo.Context("InterpreterOperation InterpretHealth testing", func() { + ginkgo.BeforeEach(func() { + customization = testhelper.NewResourceInterpreterCustomization( + "interpreter-customization"+rand.String(RandomStrLength), + configv1alpha1.CustomizationTarget{ + APIVersion: "apps/v1", + Kind: "Deployment", + }, + configv1alpha1.CustomizationRules{ + HealthInterpretation: &configv1alpha1.HealthInterpretation{ + LuaScript: ` +function InterpretHealth(observedObj) + return observedObj.status.readyReplicas == observedObj.spec.replicas +end `, + }, + }) + }) + ginkgo.It("InterpretHealth testing", func() { + resourceBindingName := names.GenerateBindingName(deployment.Kind, deployment.Name) + SetReadyReplicas := func(readyReplicas int32) { + clusterClient := framework.GetClusterClient(targetCluster) + gomega.Expect(clusterClient).ShouldNot(gomega.BeNil()) + var memberDeployment *appsv1.Deployment + framework.WaitDeploymentPresentOnClusterFitWith(targetCluster, deployment.Namespace, deployment.Name, + func(deployment *appsv1.Deployment) bool { + memberDeployment = deployment + return true + }) + memberDeployment.Status.ReadyReplicas = readyReplicas + framework.UpdateDeploymentStatus(clusterClient, memberDeployment) + } + + CheckResult := func(result workv1alpha2.ResourceHealth) interface{} { + return func(g gomega.Gomega) (bool, error) { + rb, err := karmadaClient.WorkV1alpha2().ResourceBindings(deployment.Namespace).Get(context.TODO(), resourceBindingName, metav1.GetOptions{}) + g.Expect(err).NotTo(gomega.HaveOccurred()) + if len(rb.Status.AggregatedStatus) != 1 { + return false, nil + } + for _, status := range rb.Status.AggregatedStatus { + klog.Infof("resourceBinding(%s/%s) on cluster %s got %s, want %s ", deployment.Namespace, resourceBindingName, status.ClusterName, status.Health, result) + if status.Health != result { + return false, nil + } + } + return true, nil + } + } + + ginkgo.By("deployment healthy", func() { + SetReadyReplicas(*deployment.Spec.Replicas) + gomega.Eventually(CheckResult(workv1alpha2.ResourceHealthy), pollTimeout, pollInterval).Should(gomega.BeTrue()) + }) + }) + }) })