diff --git a/api/v1alpha1/argorollouts_types.go b/api/v1alpha1/argorollouts_types.go index d2731b2..8307166 100644 --- a/api/v1alpha1/argorollouts_types.go +++ b/api/v1alpha1/argorollouts_types.go @@ -64,8 +64,10 @@ type Plugin struct { } type Plugins struct { + // TrafficManagement holds a list of traffic management plugins used to control traffic routing during rollouts. TrafficManagement []Plugin `json:"trafficManagement,omitempty"` - Metric []Plugin `json:"metric,omitempty"` + // Metric holds a list of metric plugins used to gather and report metrics during rollouts. + Metric []Plugin `json:"metric,omitempty"` } // ArgoRolloutsNodePlacementSpec is used to specify NodeSelector and Tolerations for Rollouts workloads diff --git a/config/crd/bases/argoproj.io_rolloutmanagers.yaml b/config/crd/bases/argoproj.io_rolloutmanagers.yaml index b5d7d37..cd4bd22 100644 --- a/config/crd/bases/argoproj.io_rolloutmanagers.yaml +++ b/config/crd/bases/argoproj.io_rolloutmanagers.yaml @@ -287,8 +287,12 @@ spec: type: array type: object plugins: + description: Plugins specify the traffic and metric plugins in Argo + Rollout properties: metric: + description: Metric holds a list of metric plugins used to gather + and report metrics during rollouts. items: properties: location: @@ -303,6 +307,8 @@ spec: type: object type: array trafficManagement: + description: TrafficManagement holds a list of traffic management + plugins used to control traffic routing during rollouts. items: properties: location: diff --git a/controllers/configmap.go b/controllers/configmap.go index 3add7e2..5dd3bad 100644 --- a/controllers/configmap.go +++ b/controllers/configmap.go @@ -2,6 +2,7 @@ package rollouts import ( "context" + "sort" "fmt" "reflect" @@ -12,6 +13,7 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" @@ -62,10 +64,17 @@ func (r *RolloutManagerReconciler) reconcileConfigMap(ctx context.Context, cr ro } } - // Convert traffic plugins map to slice + // Sort trafficRouterPluginsMap keys for deterministic ordering + trafficRouterPluginKeys := make([]string, 0, len(trafficRouterPluginsMap)) + for key := range trafficRouterPluginsMap { + trafficRouterPluginKeys = append(trafficRouterPluginKeys, key) + } + sort.Strings(trafficRouterPluginKeys) + + // Convert trafficRouterPluginsMap to sorted slice trafficRouterPlugins := make([]pluginItem, 0, len(trafficRouterPluginsMap)) - for _, plugin := range trafficRouterPluginsMap { - trafficRouterPlugins = append(trafficRouterPlugins, plugin) + for _, key := range trafficRouterPluginKeys { + trafficRouterPlugins = append(trafficRouterPlugins, trafficRouterPluginsMap[key]) } // Append metric plugins specified in RolloutManager CR @@ -81,10 +90,17 @@ func (r *RolloutManagerReconciler) reconcileConfigMap(ctx context.Context, cr ro } } - // Convert metric plugins map to slice + // Sort metricPluginsMap keys for deterministic ordering + metricPluginKeys := make([]string, 0, len(metricPluginsMap)) + for key := range metricPluginsMap { + metricPluginKeys = append(metricPluginKeys, key) + } + sort.Strings(metricPluginKeys) + + // Convert metricPluginsMap to sorted slice metricPlugins := make([]pluginItem, 0, len(metricPluginsMap)) - for _, plugin := range metricPluginsMap { - metricPlugins = append(metricPlugins, plugin) + for _, key := range metricPluginKeys { + metricPlugins = append(metricPlugins, metricPluginsMap[key]) } desiredTrafficRouterPluginString, err := yaml.Marshal(trafficRouterPlugins) @@ -149,6 +165,10 @@ func (r *RolloutManagerReconciler) reconcileConfigMap(ctx context.Context, cr ro func (r *RolloutManagerReconciler) restartRolloutsPod(ctx context.Context, namespace string) error { deployment := &appsv1.Deployment{} if err := r.Client.Get(ctx, types.NamespacedName{Name: DefaultArgoRolloutsResourceName, Namespace: namespace}, deployment); err != nil { + if errors.IsNotFound(err) { + // If Deployment isn't found, return nil as there is no child pod to restart + return nil + } return fmt.Errorf("failed to get deployment: %w", err) } @@ -164,12 +184,14 @@ func (r *RolloutManagerReconciler) restartRolloutsPod(ctx context.Context, names for i := range podList.Items { pod := podList.Items[i] log.Info("Deleting Rollouts Pod", "podName", pod.Name) - if err := r.Client.Delete(ctx, &pod); err != nil { - if errors.IsNotFound(err) { - log.Info(fmt.Sprintf("Pod %s already deleted", pod.Name)) - continue + if pod.ObjectMeta.DeletionTimestamp == nil { + if err := r.Client.Delete(ctx, &pod); err != nil { + if errors.IsNotFound(err) { + log.Info(fmt.Sprintf("Pod %s already deleted", pod.Name)) + continue + } + return fmt.Errorf("failed to delete Rollouts Pod %s: %w", pod.Name, err) } - return fmt.Errorf("failed to delete Rollouts Pod %s: %w", pod.Name, err) } log.Info("Rollouts Pod deleted successfully", "podName", pod.Name) } diff --git a/controllers/configmap_test.go b/controllers/configmap_test.go index 9375b1e..5f5f91b 100644 --- a/controllers/configmap_test.go +++ b/controllers/configmap_test.go @@ -6,6 +6,10 @@ import ( "github.com/argoproj-labs/argo-rollouts-manager/api/v1alpha1" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "sigs.k8s.io/controller-runtime/pkg/client" + + appsv1 "k8s.io/api/apps/v1" + v1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -15,6 +19,7 @@ var _ = Describe("ConfigMap Test", func() { var a v1alpha1.RolloutManager var r *RolloutManagerReconciler var sa *corev1.ServiceAccount + var existingDeployment *v1.Deployment const trafficrouterPluginLocation = "https://custom-traffic-plugin-location" const metricPluginLocation = "https://custom-metric-plugin-location" @@ -33,7 +38,7 @@ var _ = Describe("ConfigMap Test", func() { } Expect(r.Client.Create(ctx, sa)).To(Succeed()) - existingDeployment := deploymentCR(DefaultArgoRolloutsResourceName, a.Namespace, DefaultArgoRolloutsResourceName, []string{"plugin-bin-test", "tmp-test"}, "linux-test", sa.Name, a) + existingDeployment = deploymentCR(DefaultArgoRolloutsResourceName, a.Namespace, DefaultArgoRolloutsResourceName, []string{"plugin-bin-test", "tmp-test"}, "linux-test", sa.Name, a) Expect(r.Client.Create(ctx, existingDeployment)).To(Succeed()) }) @@ -67,83 +72,10 @@ var _ = Describe("ConfigMap Test", func() { }) - // Commented out because we are overwriting user-defined plugin values in the ConfigMap - // with the plugins defined in the CR. This will be removed once the PR has been reviewed. - - /*It("verifies that the config map reconciler will not overwrite a custom plugin that is added to the ConfigMap by the user", func() { - - // By("creating a ConfigMap containing default Openshift") - expectedConfigMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: DefaultRolloutsConfigMapName, - Namespace: a.Namespace, - Labels: map[string]string{ - "app.kubernetes.io/name": DefaultRolloutsConfigMapName, - }, - }, - } - - By("calling reconcileConfigMap, which will add the default plugin to the ConfigMap") - Expect(r.reconcileConfigMap(ctx, a)).To(Succeed()) - - By("fetching the ConfigMap") - fetchedConfigMap := &corev1.ConfigMap{} - Expect(fetchObject(ctx, r.Client, a.Namespace, expectedConfigMap.Name, fetchedConfigMap)).To(Succeed()) - Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).To(ContainSubstring(OpenShiftRolloutPluginName)) - Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).ToNot(ContainSubstring("test/plugin")) - - By("adding a new trafficRouter plugin to test the update plugin logic") - trafficRouterPlugins := []pluginItem{ - { - Name: "test/plugin", - Location: "https://test-path", - }, - } - - newConfigMap := fetchedConfigMap.DeepCopy() - { - pluginString, err := yaml.Marshal(trafficRouterPlugins) - Expect(err).ToNot(HaveOccurred()) - - newConfigMap.Data = map[string]string{ - TrafficRouterPluginConfigMapKey: string(pluginString), - } - } - - By("updating the ConfigMap to contain only a user provided plugin") - Expect(r.Client.Update(ctx, newConfigMap)).To(Succeed()) - - By("calling reconcileConfigMap") - Expect(r.reconcileConfigMap(ctx, a)).To(Succeed()) - - By("verifying that when ConfigMap is reconciled, it contains both plugins") - - Expect(fetchObject(ctx, r.Client, a.Namespace, expectedConfigMap.Name, fetchedConfigMap)).To(Succeed()) - Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).To(ContainSubstring("test/plugin")) - Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).To(ContainSubstring(OpenShiftRolloutPluginName)) - Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).To(ContainSubstring(r.OpenShiftRoutePluginLocation)) - - By("calling reconcileConfigMap again, to verify nothing changes when reconcile is called again") - Expect(r.reconcileConfigMap(ctx, a)).To(Succeed()) - - Expect(fetchObject(ctx, r.Client, a.Namespace, expectedConfigMap.Name, fetchedConfigMap)).To(Succeed()) - Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).To(ContainSubstring("test/plugin")) - Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).To(ContainSubstring(OpenShiftRolloutPluginName)) - Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).To(ContainSubstring(r.OpenShiftRoutePluginLocation)) - - // overriding this value with new test url to verify whether it updated the existing configMap with the new url - r.OpenShiftRoutePluginLocation = "test-updated-url" - - By("calling reconcileConfigMap") - Expect(r.reconcileConfigMap(ctx, a)).To(Succeed()) - - Expect(fetchObject(ctx, r.Client, a.Namespace, expectedConfigMap.Name, fetchedConfigMap)).To(Succeed()) - Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).To(ContainSubstring("test/plugin")) - Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).To(ContainSubstring(OpenShiftRolloutPluginName)) - Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).To(ContainSubstring("test-updated-url")) - }) */ - It("verifies traffic and metric plugin creation/modification and ensures OpenShiftRolloutPlugin existence", func() { + By("Add a pod that matches the deployment's selector") + addTestPodToFakeClient(r, a.Namespace, existingDeployment) + expectedConfigMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: DefaultRolloutsConfigMapName, @@ -158,6 +90,8 @@ var _ = Describe("ConfigMap Test", func() { {Name: "custom-metric-plugin", Location: metricPluginLocation, SHA256: "sha256-test"}, } + Expect(r.Client.Update(ctx, &a)).To(Succeed()) + By("Call reconcileConfigMap") Expect(r.reconcileConfigMap(ctx, a)).To(Succeed()) @@ -187,6 +121,8 @@ var _ = Describe("ConfigMap Test", func() { {Name: "custom-metric-plugin", Location: updatedPluginLocation, SHA256: "sha256-test"}, } + Expect(r.Client.Update(ctx, &a)).To(Succeed()) + By("Call reconcileConfigMap again after update") Expect(r.reconcileConfigMap(ctx, a)).To(Succeed()) @@ -215,7 +151,59 @@ var _ = Describe("ConfigMap Test", func() { }, } - By("Call reconcileConfigMap again after update") + Expect(r.Client.Update(ctx, &a)).To(Succeed()) + + By("Calling reconcileConfigMap again after the attempt to update OpenShiftRolloutPlugin") Expect(r.reconcileConfigMap(ctx, a)).ToNot(Succeed(), "the plugin %s cannot be modified or added through the RolloutManager CR", OpenShiftRolloutPluginName) + + By("Remove plugins from RolloutManager spec should remove plugins from ConfigMap") + a.Spec.Plugins.TrafficManagement = nil + a.Spec.Plugins.Metric = nil + + Expect(r.Client.Update(ctx, &a)).To(Succeed()) + + By("Call reconcileConfigMap after plugins are removed") + Expect(r.reconcileConfigMap(ctx, a)).To(Succeed()) + + By("Verify that the fetched ConfigMap contains OpenShiftRolloutPlugin after removing plugins from CR") + Expect(fetchedConfigMap.Name).To(Equal(expectedConfigMap.Name)) + Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).To(ContainSubstring(OpenShiftRolloutPluginName)) + Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).To(ContainSubstring(r.OpenShiftRoutePluginLocation)) + + By("Fetched ConfigMap after removing plugins") + Expect(fetchObject(ctx, r.Client, a.Namespace, expectedConfigMap.Name, fetchedConfigMap)).To(Succeed()) + + By("Verify that the ConfigMap no longer contains removed plugins") + Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).NotTo(ContainSubstring("custom-traffic-plugin")) + Expect(fetchedConfigMap.Data[MetricPluginConfigMapKey]).NotTo(ContainSubstring("custom-metric-plugin")) + + By("Verify that the pod has been deleted after the above update.") + rolloutsPodList := &corev1.PodList{} + err := r.Client.List(context.TODO(), rolloutsPodList, client.InNamespace(a.Namespace), client.MatchingLabels(existingDeployment.Spec.Selector.MatchLabels)) + Expect(err).NotTo(HaveOccurred()) + Expect(len(rolloutsPodList.Items)).To(BeNumerically("==", 0)) }) }) + +func addTestPodToFakeClient(r *RolloutManagerReconciler, namespace string, deployment *appsv1.Deployment) { + // Create a test pod with labels that match the deployment's selector + testPod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-rollouts-pod", + Namespace: namespace, + Labels: deployment.Spec.Selector.MatchLabels, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: DefaultArgoRolloutsResourceName, + Image: "argoproj/argo-rollouts:latest", + }, + }, + }, + } + + // Add the pod to the fake client + err := r.Client.Create(context.TODO(), testPod) + Expect(err).ToNot(HaveOccurred()) +} diff --git a/tests/e2e/cluster-scoped/cluster_scoped_rollouts_test.go b/tests/e2e/cluster-scoped/cluster_scoped_rollouts_test.go index 7a11a67..027ca5f 100644 --- a/tests/e2e/cluster-scoped/cluster_scoped_rollouts_test.go +++ b/tests/e2e/cluster-scoped/cluster_scoped_rollouts_test.go @@ -2,7 +2,6 @@ package e2e import ( "context" - "strings" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -14,7 +13,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/argoproj-labs/argo-rollouts-manager/api/v1alpha1" rmv1alpha1 "github.com/argoproj-labs/argo-rollouts-manager/api/v1alpha1" controllers "github.com/argoproj-labs/argo-rollouts-manager/controllers" @@ -365,86 +363,6 @@ var _ = Describe("Cluster-scoped RolloutManager tests", func() { Eventually(clusterRoleAdmin, "1m", "1s").ShouldNot((k8s.ExistByName(k8sClient))) Eventually(clusterRoleView, "1m", "1s").ShouldNot((k8s.ExistByName(k8sClient))) Eventually(clusterRoleEdit, "1m", "1s").ShouldNot((k8s.ExistByName(k8sClient))) - }) - - It("Should add, update, and remove traffic and metric plugins through RolloutManager CR", func() { - rolloutsManager := v1alpha1.RolloutManager{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-rollouts-manager", - Namespace: fixture.TestE2ENamespace, - }, - Spec: v1alpha1.RolloutManagerSpec{ - NamespaceScoped: false, - Plugins: v1alpha1.Plugins{ - TrafficManagement: []v1alpha1.Plugin{ - { - Name: "argoproj-labs/gatewayAPI", - Location: "https://github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/releases/download/v0.0.1/gateway-api-plugin-darwin-arm64"}, - }, - Metric: []v1alpha1.Plugin{ - { - Name: "prometheus", - Location: "https://github.com/argoproj-labs/sample-rollouts-metric-plugin/releases/download/v0.0.3/metric-plugin-linux-amd64", - SHA256: "08f588b1c799a37bbe8d0fc74cc1b1492dd70b2c", - }}, - }, - }, - } - - Expect(k8sClient.Create(ctx, &rolloutsManager)).To(Succeed()) - - By("Verify that RolloutManager is successfully created.") - Eventually(rolloutsManager, "1m", "1s").Should(rmFixture.HavePhase(rmv1alpha1.PhaseAvailable)) - - By("Verify traffic and metric plugin is added to ConfigMap") - configMap := corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: controllers.DefaultRolloutsConfigMapName, Namespace: rolloutsManager.Namespace}, - } - - Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(&configMap), &configMap)).To(Succeed()) - Expect(configMap.Data[controllers.TrafficRouterPluginConfigMapKey]).To(ContainSubstring(rolloutsManager.Spec.Plugins.TrafficManagement[0].Name)) - Expect(configMap.Data[controllers.TrafficRouterPluginConfigMapKey]).To(ContainSubstring(rolloutsManager.Spec.Plugins.TrafficManagement[0].Location)) - - Expect(configMap.Data[controllers.MetricPluginConfigMapKey]).To(ContainSubstring(rolloutsManager.Spec.Plugins.Metric[0].Name)) - Expect(configMap.Data[controllers.MetricPluginConfigMapKey]).To(ContainSubstring(rolloutsManager.Spec.Plugins.Metric[0].Location)) - - By("Update traffic and metric plugins") - Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(&rolloutsManager), &rolloutsManager)).To(Succeed()) - - rolloutsManager.Spec.Plugins.TrafficManagement[0].Location = "https://test-update-traffic-plugin" - rolloutsManager.Spec.Plugins.Metric[0].Location = "https://test-update-metric-plugin" - - Expect(k8sClient.Update(ctx, &rolloutsManager)).To(Succeed()) - Eventually(rolloutsManager, "1m", "1s").Should(rmFixture.HavePhase(rmv1alpha1.PhaseAvailable)) - - By("Verify traffic and metric plugin is updated in ConfigMap") - Eventually(func() bool { - if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(&configMap), &configMap); err != nil { - return false - } - return strings.Contains(configMap.Data[controllers.TrafficRouterPluginConfigMapKey], rolloutsManager.Spec.Plugins.TrafficManagement[0].Name) && - strings.Contains(configMap.Data[controllers.TrafficRouterPluginConfigMapKey], rolloutsManager.Spec.Plugins.TrafficManagement[0].Location) && - strings.Contains(configMap.Data[controllers.MetricPluginConfigMapKey], rolloutsManager.Spec.Plugins.Metric[0].Name) && - strings.Contains(configMap.Data[controllers.MetricPluginConfigMapKey], rolloutsManager.Spec.Plugins.Metric[0].Location) - }, "1m", "1s").Should(BeTrue()) - - By("Remove traffic and metric plugins") - Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(&rolloutsManager), &rolloutsManager)).To(Succeed()) - - By("Remove plugins from RolloutManager CR") - rolloutsManager.Spec.Plugins.TrafficManagement = []v1alpha1.Plugin{} - rolloutsManager.Spec.Plugins.Metric = []v1alpha1.Plugin{} - Expect(k8sClient.Update(ctx, &rolloutsManager)).To(Succeed()) - Eventually(rolloutsManager, "1m", "1s").Should(rmFixture.HavePhase(rmv1alpha1.PhaseAvailable)) - - By("Verify that traffic and metric plugins are removed from ConfigMap") - Eventually(func() bool { - if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(&configMap), &configMap); err != nil { - return false - } - return !strings.Contains(configMap.Data[controllers.TrafficRouterPluginConfigMapKey], "gatewayAPI") && - !strings.Contains(configMap.Data[controllers.MetricPluginConfigMapKey], "prometheus") - }, "1m", "1s").Should(BeTrue()) }) }) diff --git a/tests/e2e/rollout_tests_all.go b/tests/e2e/rollout_tests_all.go index 8e6052f..502e57a 100644 --- a/tests/e2e/rollout_tests_all.go +++ b/tests/e2e/rollout_tests_all.go @@ -3,6 +3,7 @@ package e2e import ( "context" "fmt" + "strings" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -595,5 +596,98 @@ func RunRolloutsTests(namespaceScopedParam bool) { }) }) + + It("Should add, update, and remove traffic and metric plugins through RolloutManager CR", func() { + rolloutsManager := rolloutsmanagerv1alpha1.RolloutManager{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-rollouts-manager", + Namespace: fixture.TestE2ENamespace, + }, + Spec: rolloutsmanagerv1alpha1.RolloutManagerSpec{ + NamespaceScoped: false, + Plugins: rolloutsmanagerv1alpha1.Plugins{ + TrafficManagement: []rolloutsmanagerv1alpha1.Plugin{ + { + Name: "argoproj-labs/gatewayAPI", + Location: "https://github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/releases/download/v0.4.0/gateway-api-plugin-linux-amd64"}, + }, + Metric: []rolloutsmanagerv1alpha1.Plugin{ + { + Name: "prometheus", + Location: "https://github.com/argoproj-labs/sample-rollouts-metric-plugin/releases/download/v0.0.3/metric-plugin-linux-amd64", + SHA256: "08f588b1c799a37bbe8d0fc74cc1b1492dd70b2c", + }}, + }, + }, + } + + Expect(k8sClient.Create(ctx, &rolloutsManager)).To(Succeed()) + + By("Verify that RolloutManager is successfully created.") + Eventually(rolloutsManager, "1m", "1s").Should(rolloutManagerFixture.HavePhase(rolloutsmanagerv1alpha1.PhaseAvailable)) + + By("Verify traffic and metric plugin is added to ConfigMap") + configMap := corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: controllers.DefaultRolloutsConfigMapName, Namespace: rolloutsManager.Namespace}, + } + + Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(&configMap), &configMap)).To(Succeed()) + Expect(configMap.Data[controllers.TrafficRouterPluginConfigMapKey]).To(ContainSubstring(rolloutsManager.Spec.Plugins.TrafficManagement[0].Name)) + Expect(configMap.Data[controllers.TrafficRouterPluginConfigMapKey]).To(ContainSubstring(rolloutsManager.Spec.Plugins.TrafficManagement[0].Location)) + Expect(configMap.Data[controllers.MetricPluginConfigMapKey]).To(ContainSubstring(rolloutsManager.Spec.Plugins.Metric[0].Name)) + Expect(configMap.Data[controllers.MetricPluginConfigMapKey]).To(ContainSubstring(rolloutsManager.Spec.Plugins.Metric[0].Location)) + + By("Update traffic and metric plugins") + Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(&rolloutsManager), &rolloutsManager)).To(Succeed()) + + rolloutsManager.Spec.Plugins.TrafficManagement[0].Location = "https://test-update-traffic-plugin" + rolloutsManager.Spec.Plugins.Metric[0].Location = "https://test-update-metric-plugin" + + By("Get existing Rollouts Pod(s) before update") + var oldPods corev1.PodList + Expect(k8sClient.List(ctx, &oldPods, client.InNamespace(rolloutsManager.Namespace), client.MatchingLabels{"app.kubernetes.io/name": "argo-rollouts"})).To(Succeed()) + + Expect(k8sClient.Update(ctx, &rolloutsManager)).To(Succeed()) + Eventually(rolloutsManager, "1m", "1s").Should(rolloutManagerFixture.HavePhase(rolloutsmanagerv1alpha1.PhaseAvailable)) + + By("Verify traffic and metric plugin is updated in ConfigMap") + Eventually(func() bool { + if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(&configMap), &configMap); err != nil { + return false + } + return strings.Contains(configMap.Data[controllers.TrafficRouterPluginConfigMapKey], rolloutsManager.Spec.Plugins.TrafficManagement[0].Name) && + strings.Contains(configMap.Data[controllers.TrafficRouterPluginConfigMapKey], rolloutsManager.Spec.Plugins.TrafficManagement[0].Location) && + strings.Contains(configMap.Data[controllers.MetricPluginConfigMapKey], rolloutsManager.Spec.Plugins.Metric[0].Name) && + strings.Contains(configMap.Data[controllers.MetricPluginConfigMapKey], rolloutsManager.Spec.Plugins.Metric[0].Location) + }, "1m", "1s").Should(BeTrue()) + + By("Remove traffic and metric plugins") + Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(&rolloutsManager), &rolloutsManager)).To(Succeed()) + + By("Remove plugins from RolloutManager CR") + rolloutsManager.Spec.Plugins.TrafficManagement = []rolloutsmanagerv1alpha1.Plugin{} + rolloutsManager.Spec.Plugins.Metric = []rolloutsmanagerv1alpha1.Plugin{} + Expect(k8sClient.Update(ctx, &rolloutsManager)).To(Succeed()) + Eventually(rolloutsManager, "1m", "1s").Should(rolloutManagerFixture.HavePhase(rolloutsmanagerv1alpha1.PhaseAvailable)) + + By("Get existing Rollouts Pod(s) after update") + var newPods corev1.PodList + Expect(k8sClient.List(ctx, &newPods, client.InNamespace(rolloutsManager.Namespace), client.MatchingLabels{"app.kubernetes.io/name": "argo-rollouts"})).To(Succeed()) + + By("Verify that traffic and metric plugins are removed from ConfigMap") + Eventually(func() bool { + if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(&configMap), &configMap); err != nil { + return false + } + return !strings.Contains(configMap.Data[controllers.TrafficRouterPluginConfigMapKey], "gatewayAPI") && + !strings.Contains(configMap.Data[controllers.MetricPluginConfigMapKey], "prometheus") + }, "1m", "1s").Should(BeTrue()) + + By("Verify Rollouts Pod is restarted") + Expect(newPods.Items).To(HaveLen(1)) // Ensure a new Pod is created + Expect(oldPods.Items).To(HaveLen(1)) // Ensure there was an old Pod + Expect(newPods.Items[0].Name).NotTo(Equal(oldPods.Items[0].Name)) // Ensure the Pod names are different + }) + }) }