From ac766918763d38cfa9550648276b541fe038e42b Mon Sep 17 00:00:00 2001 From: Alka Kumari Date: Fri, 10 Jul 2026 16:53:08 +0530 Subject: [PATCH] added E2E for role ownership validation Signed-off-by: Alka Kumari --- .../1-125_validate_role_ownership.go | 253 +++++++++++++ ...alidate_role_ownership_agent_agent_test.go | 248 +++++++++++++ ...ate_role_ownership_agent_principal_test.go | 344 ++++++++++++++++++ ...5_validate_role_ownership_image_updater.go | 189 ++++++++++ 4 files changed, 1034 insertions(+) create mode 100644 test/openshift/e2e/ginkgo/sequential/1-125_validate_role_ownership.go create mode 100644 test/openshift/e2e/ginkgo/sequential/1-125_validate_role_ownership_agent_agent_test.go create mode 100644 test/openshift/e2e/ginkgo/sequential/1-125_validate_role_ownership_agent_principal_test.go create mode 100644 test/openshift/e2e/ginkgo/sequential/1-125_validate_role_ownership_image_updater.go diff --git a/test/openshift/e2e/ginkgo/sequential/1-125_validate_role_ownership.go b/test/openshift/e2e/ginkgo/sequential/1-125_validate_role_ownership.go new file mode 100644 index 00000000000..614ce2fd224 --- /dev/null +++ b/test/openshift/e2e/ginkgo/sequential/1-125_validate_role_ownership.go @@ -0,0 +1,253 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package sequential + +import ( + "context" + + argov1beta1api "github.com/argoproj-labs/argocd-operator/api/v1beta1" + argocdFixture "github.com/argoproj-labs/argocd-operator/tests/ginkgo/fixture/argocd" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture" + deplFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/deployment" + k8sFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/k8s" + fixtureUtils "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/utils" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var _ = Describe("GitOps Operator Parallel E2E Tests", func() { + + Context("1-125_validate_role_ownership", func() { + + var ( + ctx context.Context + k8sClient client.Client + ) + const ( + applicationControllerClusterRoleName = "openshift-gitops-openshift-gitops-argocd-application-controller" + applicationSetControllerClusterRoleName = "openshift-gitops-openshift-gitops-argocd-applicationset-controller" + serverClusterRoleName = "openshift-gitops-openshift-gitops-argocd-server" + applicationControllerClusterRoleBindingName = "openshift-gitops-openshift-gitops-argocd-application-controller" + applicationSetControllerClusterRoleBindingName = "openshift-gitops-openshift-gitops-argocd-applicationset-controller" + serverClusterRoleBindingName = "openshift-gitops-openshift-gitops-argocd-server" + ) + + BeforeEach(func() { + fixture.EnsureParallelCleanSlate() + k8sClient, _ = fixtureUtils.GetE2ETestKubeClient() + ctx = context.Background() + }) + + It("validates that the role bug is fixed", func() { + + By("checking that the default ClusterRole and clusterroleBinding for the ArgoCD Application Controller and Server exists") + defaultControllerClusterRole := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: applicationControllerClusterRoleName, + }, + } + defaultApplicationSetControllerClusterRole := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: applicationSetControllerClusterRoleName, + }, + } + defaultServerClusterRole := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: serverClusterRoleName, + }, + } + defaultControllerClusterRoleBinding := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: applicationControllerClusterRoleBindingName, + }, + } + defaultApplicationSetControllerClusterRoleBinding := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: applicationSetControllerClusterRoleBindingName, + }, + } + defaultServerClusterRoleBinding := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: serverClusterRoleBindingName, + }, + } + Eventually(defaultControllerClusterRole).Should(k8sFixture.ExistByName()) + Eventually(defaultApplicationSetControllerClusterRole).Should(k8sFixture.ExistByName()) + Eventually(defaultServerClusterRole).Should(k8sFixture.ExistByName()) + Eventually(defaultControllerClusterRoleBinding).Should(k8sFixture.ExistByName()) + Eventually(defaultApplicationSetControllerClusterRoleBinding).Should(k8sFixture.ExistByName()) + Eventually(defaultServerClusterRoleBinding).Should(k8sFixture.ExistByName()) + + By("fetching initial UID of the clusterrole") + initialControllerUid := defaultControllerClusterRole.GetUID() + initialApplicationSetControllerUid := defaultApplicationSetControllerClusterRole.GetUID() + initialServerUid := defaultServerClusterRole.GetUID() + initialControllerRoleBindingUid := defaultControllerClusterRoleBinding.GetUID() + initialApplicationSetControllerRoleBindingUid := defaultApplicationSetControllerClusterRoleBinding.GetUID() + initialServerRoleBindingUid := defaultServerClusterRoleBinding.GetUID() + + defaultArgocd := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{ + Name: "openshift-gitops", + Namespace: "openshift-gitops", + }, + } + + Eventually(defaultArgocd, "5m", "5s").Should(argocdFixture.BeAvailable()) + argocdFixture.Update(defaultArgocd, func(ac *argov1beta1api.ArgoCD) { + ac.Spec.ImageUpdater = argov1beta1api.ArgoCDImageUpdaterSpec{ + Env: []corev1.EnvVar{ + { + Name: "IMAGE_UPDATER_WATCH_NAMESPACES", + Value: "*", + }, + }, + Enabled: true, + } + }) + By("waiting for ArgoCD CR to be reconciled and the instance to be ready") + Eventually(defaultArgocd, "5m", "5s").Should(argocdFixture.BeAvailable()) + + By("verifying image updater workload has started argocd-image-updater-controller") + depl := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "openshift-gitops-argocd-image-updater-controller", Namespace: "openshift-gitops"}} + Eventually(depl, "2m", "5s").Should(k8sFixture.ExistByName(), "Deployment openshift-gitops-argocd-image-updater-controller did not exist within timeout") + Eventually(depl, "2m", "5s").Should(deplFixture.HaveReplicas(1), "Deployment openshift-gitops-argocd-image-updater-controller did not have correct replicas within timeout") + Eventually(depl, "3m", "5s").Should(deplFixture.HaveReadyReplicas(1), "Deployment openshift-gitops-argocd-image-updater-controller was not ready within timeout") + + defaultImageUpdaterClusterRole := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "openshift-gitops-openshift-gitops-argocd-image-updater-controller", + }, + } + defaultImageUpdaterClusterRoleBinding := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "openshift-gitops-openshift-gitops-argocd-image-updater-controller", + }, + } + Eventually(defaultImageUpdaterClusterRole).Should(k8sFixture.ExistByName()) + Eventually(defaultImageUpdaterClusterRoleBinding).Should(k8sFixture.ExistByName()) + + initialImageUpdaterClusterRoleUid := defaultImageUpdaterClusterRole.GetUID() + inititalImageUpdaterClusterRoleBindingUid := defaultImageUpdaterClusterRoleBinding.GetUID() + + By("creating new ArgoCD instance to trigger the check") + ns, nsCleanup := fixture.CreateNamespaceWithCleanupFunc("gitops") + defer nsCleanup() + + argoCD := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{ + Name: "openshift-gitops-openshift", + Namespace: ns.Name, + }, + Spec: argov1beta1api.ArgoCDSpec{ + ImageUpdater: argov1beta1api.ArgoCDImageUpdaterSpec{ + Env: []corev1.EnvVar{ + { + Name: "IMAGE_UPDATER_LOGLEVEL", + Value: "trace", + }, + }, + Enabled: true, + }, + }, + } + Expect(k8sClient.Create(ctx, argoCD)).To(Succeed()) + + Eventually(argoCD, "5m", "5s").Should(argocdFixture.BeAvailable()) + + By("checking that the default ClusterRole for the ArgoCD Application Controller still exists") + newControllerClusterRole := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: applicationControllerClusterRoleName, + }, + } + newApplicationSetControllerClusterRole := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: applicationSetControllerClusterRoleName, + }, + } + newServerClusterRole := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: serverClusterRoleName, + }, + } + newControllerClusterRoleBinding := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: applicationControllerClusterRoleBindingName, + }, + } + newApplicationSetControllerClusterRoleBinding := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: applicationSetControllerClusterRoleBindingName, + }, + } + newServerClusterRoleBinding := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: serverClusterRoleBindingName, + }, + } + newImageUpdaterClusterRole := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "openshift-gitops-openshift-gitops-argocd-image-updater-controller", + }, + } + newImageUpdaterClusterRoleBinding := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "openshift-gitops-openshift-gitops-argocd-image-updater-controller", + }, + } + + Eventually(newControllerClusterRole).Should(k8sFixture.ExistByName()) + Eventually(newApplicationSetControllerClusterRole).Should(k8sFixture.ExistByName()) + Eventually(newServerClusterRole).Should(k8sFixture.ExistByName()) + Eventually(newImageUpdaterClusterRole).Should(k8sFixture.ExistByName()) + + Eventually(newControllerClusterRoleBinding).Should(k8sFixture.ExistByName()) + Eventually(newApplicationSetControllerClusterRoleBinding).Should(k8sFixture.ExistByName()) + Eventually(newServerClusterRoleBinding).Should(k8sFixture.ExistByName()) + Eventually(newImageUpdaterClusterRoleBinding).Should(k8sFixture.ExistByName()) + + By("fetching UID of the clusterrole after reconciliation") + afterControllerReconcileUid := newControllerClusterRole.GetUID() + afterApplicationSetControllerReconcileUid := newApplicationSetControllerClusterRole.GetUID() + afterServerReconcileUid := newServerClusterRole.GetUID() + afterImageUpdaterReconcileUid := newImageUpdaterClusterRole.GetUID() + + afterControllerRoleBindingReconcileUid := newControllerClusterRoleBinding.GetUID() + afterApplicationSetControllerRoleBindingReconcileUid := newApplicationSetControllerClusterRoleBinding.GetUID() + afterServerRoleBindingReconcileUid := newServerClusterRoleBinding.GetUID() + afterImageUpdaterRoleBindingReconcileUid := newImageUpdaterClusterRoleBinding.GetUID() + + By("comparing the UID to check if the ClusterRole was recreated") + Expect(initialControllerUid).To(Equal(afterControllerReconcileUid), "the ClusterRole was recreated") + Expect(initialApplicationSetControllerUid).To(Equal(afterApplicationSetControllerReconcileUid), "the ClusterRole was recreated") + Expect(initialServerUid).To(Equal(afterServerReconcileUid), "the ClusterRole was recreated") + Expect(initialImageUpdaterClusterRoleUid).To(Equal(afterImageUpdaterReconcileUid), "the ClusterRole was recreated") + + Expect(initialControllerRoleBindingUid).To(Equal(afterControllerRoleBindingReconcileUid), "the ClusterRoleBinding was recreated") + Expect(initialApplicationSetControllerRoleBindingUid).To(Equal(afterApplicationSetControllerRoleBindingReconcileUid), "the ClusterRoleBinding was recreated") + Expect(initialServerRoleBindingUid).To(Equal(afterServerRoleBindingReconcileUid), "the ClusterRoleBinding was recreated") + Expect(inititalImageUpdaterClusterRoleBindingUid).To(Equal(afterImageUpdaterRoleBindingReconcileUid), "the ClusterRoleBinding was recreated") + + }) + + }) +}) diff --git a/test/openshift/e2e/ginkgo/sequential/1-125_validate_role_ownership_agent_agent_test.go b/test/openshift/e2e/ginkgo/sequential/1-125_validate_role_ownership_agent_agent_test.go new file mode 100644 index 00000000000..f130eb3befe --- /dev/null +++ b/test/openshift/e2e/ginkgo/sequential/1-125_validate_role_ownership_agent_agent_test.go @@ -0,0 +1,248 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sequential + +import ( + "context" + "fmt" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + "sigs.k8s.io/controller-runtime/pkg/client" + + argov1beta1api "github.com/argoproj-labs/argocd-operator/api/v1beta1" + "github.com/argoproj-labs/argocd-operator/common" + "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture" + argocdFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/argocd" + k8sFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/k8s" + fixtureUtils "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/utils" +) + +var _ = Describe("GitOps Operator Sequential E2E Tests", func() { + + const ( + clusterRoleName = "argocd-agent-argocd-agent-agent-agent" + clusterRoleBindingName = "argocd-agent-argocd-agent-agent-agent" + ) + + Context("1-125_validate_role_ownership_agent_agent", func() { + var ( + k8sClient client.Client + ctx context.Context + argoCD *argov1beta1api.ArgoCD + ns *corev1.Namespace + cleanupFunc func() + serviceNames []string + agentDeployment *appsv1.Deployment + ) + + BeforeEach(func() { + fixture.EnsureSequentialCleanSlate() + fixture.SetEnvInOperatorSubscriptionOrDeployment("ARGOCD_CLUSTER_CONFIG_NAMESPACES", "openshift-gitops, argocd-agent") + + k8sClient, _ = fixtureUtils.GetE2ETestKubeClient() + ctx = context.Background() + ns, cleanupFunc = fixture.CreateNamespaceWithCleanupFunc("argocd-agent") + + // Define ArgoCD CR with agent enabled + argoCD = &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-agent", + Namespace: ns.Name, + }, + Spec: argov1beta1api.ArgoCDSpec{ + Controller: argov1beta1api.ArgoCDApplicationControllerSpec{ + Enabled: ptr.To(false), + }, + Server: argov1beta1api.ArgoCDServerSpec{ + Enabled: ptr.To(false), + }, + ArgoCDAgent: &argov1beta1api.ArgoCDAgentSpec{ + Agent: &argov1beta1api.AgentSpec{ + Enabled: ptr.To(true), + Creds: "mtls:any", + LogLevel: "info", + LogFormat: "text", + Client: &argov1beta1api.AgentClientSpec{ + PrincipalServerAddress: "argocd-agent-principal.example.com", + PrincipalServerPort: "443", + Mode: string(argov1beta1api.AgentModeManaged), + EnableWebSocket: ptr.To(false), + EnableCompression: ptr.To(false), + KeepAliveInterval: "30s", + }, + TLS: &argov1beta1api.AgentTLSSpec{ + SecretName: agentClientTLSSecretName, + RootCASecretName: agentRootCASecretName, + Insecure: ptr.To(false), + }, + Redis: &argov1beta1api.AgentRedisSpec{ + ServerAddress: fmt.Sprintf("%s-%s:%d", "argocd-agent", "redis", common.ArgoCDDefaultRedisPort), + }, + }, + }, + }, + } + + serviceNames = []string{ + "argocd-agent-agent-agent-metrics", + "argocd-agent-agent-agent-healthz", + "argocd-agent-redis", + } + + agentDeployment = &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-agent-agent-agent", + Namespace: ns.Name, + }, + } + + }) + + AfterEach(func() { + By("Cleanup namespace") + if cleanupFunc != nil { + cleanupFunc() + } + }) + + verifyExpectedResourcesExist := func(ns *corev1.Namespace) { + + By("verifying expected resources exist") + for _, serviceName := range serviceNames { + + By("verifying Service '" + serviceName + "' exists and is a LoadBalancer or ClusterIP depending on which service") + + service := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: serviceName, + Namespace: ns.Name, + }, + } + Eventually(service).Should(k8sFixture.ExistByName()) + Expect(string(service.Spec.Type)).To(Equal("ClusterIP")) + + } + By("verifying primary agent Deployment has expected values") + + Eventually(agentDeployment).Should(k8sFixture.ExistByName()) + Eventually(agentDeployment).Should(k8sFixture.HaveLabelWithValue("app.kubernetes.io/component", string(argov1beta1api.AgentComponentTypeAgent))) + Eventually(agentDeployment).Should(k8sFixture.HaveLabelWithValue("app.kubernetes.io/managed-by", "argocd-agent")) + Eventually(agentDeployment).Should(k8sFixture.HaveLabelWithValue("app.kubernetes.io/name", "argocd-agent-agent-agent")) + Eventually(agentDeployment).Should(k8sFixture.HaveLabelWithValue("app.kubernetes.io/part-of", "argocd-agent")) + + } + + It("check agent clusterRole and clusterRoleBinding", func() { + + By("creating ArgoCD instance with agent enabled") + Expect(k8sClient.Create(ctx, argoCD)).To(Succeed()) + + By("waiting for ArgoCD CR to be reconciled and the instance to be ready") + Eventually(argoCD, "5m", "5s").Should(argocdFixture.BeAvailable()) + + By("verifying expected resources are created with correct values") + verifyExpectedResourcesExist(ns) + + By("verifying ClusterRole and ClusterRoleBinding for agent exist with correct names") + + clusterRole := &rbacv1.ClusterRole{} + Eventually(func() error { + return k8sClient.Get(ctx, client.ObjectKey{Name: clusterRoleName}, clusterRole) + }).Should(Succeed()) + initialClusterRoleUid := clusterRole.GetUID() + + clusterRoleBinding := &rbacv1.ClusterRoleBinding{} + Eventually(func() error { + return k8sClient.Get(ctx, client.ObjectKey{Name: clusterRoleBindingName}, clusterRoleBinding) + }).Should(Succeed()) + initialClusterRoleBindingUid := clusterRoleBinding.GetUID() + + By("Create namespace-scoped ArgoCD instance namespace") + nsScoped, cleanupFuncScoped := fixture.CreateNamespaceWithCleanupFunc("agent") + defer cleanupFuncScoped() + + argoCD1 := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-agent-argocd", + Namespace: nsScoped.Name, + }, + Spec: argov1beta1api.ArgoCDSpec{ + Controller: argov1beta1api.ArgoCDApplicationControllerSpec{ + Enabled: ptr.To(false), + }, + Server: argov1beta1api.ArgoCDServerSpec{ + Enabled: ptr.To(false), + }, + ArgoCDAgent: &argov1beta1api.ArgoCDAgentSpec{ + Agent: &argov1beta1api.AgentSpec{ + Enabled: ptr.To(true), + Creds: "mtls:any", + LogLevel: "info", + LogFormat: "text", + Client: &argov1beta1api.AgentClientSpec{ + PrincipalServerAddress: "argocd-agent-principal.example.com", + PrincipalServerPort: "443", + Mode: string(argov1beta1api.AgentModeManaged), + EnableWebSocket: ptr.To(false), + EnableCompression: ptr.To(false), + KeepAliveInterval: "30s", + }, + TLS: &argov1beta1api.AgentTLSSpec{ + SecretName: agentClientTLSSecretName, + RootCASecretName: agentRootCASecretName, + Insecure: ptr.To(false), + }, + Redis: &argov1beta1api.AgentRedisSpec{ + ServerAddress: fmt.Sprintf("%s-%s:%d", "argocd-agent-argocd", "redis", common.ArgoCDDefaultRedisPort), + }, + }, + }, + }, + } + + By("Create namespace-scoped ArgoCD instance with agent") + + Expect(k8sClient.Create(ctx, argoCD1)).To(Succeed()) + Eventually(argoCD1, "5m", "5s").Should(argocdFixture.BeAvailable()) + + By("Verify UID of ClusterRole and ClusterRoleBinding") + newClusterRole := &rbacv1.ClusterRole{} + Eventually(func() error { + return k8sClient.Get(ctx, client.ObjectKey{Name: clusterRoleName}, newClusterRole) + }).Should(Succeed(), "ClusterRole should exist and be fetchable") + newClusterRoleUid := newClusterRole.GetUID() + + newClusterRoleBinding := &rbacv1.ClusterRoleBinding{} + Eventually(func() error { + return k8sClient.Get(ctx, client.ObjectKey{Name: clusterRoleBindingName}, newClusterRoleBinding) + }).Should(Succeed(), "ClusterRoleBinding should exist and be fetchable") + newClusterRoleBindingUid := newClusterRoleBinding.GetUID() + + Expect(newClusterRoleUid).To(Equal(initialClusterRoleUid), "ClusterRole UID should remain the same after creating namespace-scoped ArgoCD instance") + Expect(newClusterRoleBindingUid).To(Equal(initialClusterRoleBindingUid), "ClusterRoleBinding UID should remain the same after creating namespace-scoped ArgoCD instance") + + }) + + }) + +}) diff --git a/test/openshift/e2e/ginkgo/sequential/1-125_validate_role_ownership_agent_principal_test.go b/test/openshift/e2e/ginkgo/sequential/1-125_validate_role_ownership_agent_principal_test.go new file mode 100644 index 00000000000..a4967af3245 --- /dev/null +++ b/test/openshift/e2e/ginkgo/sequential/1-125_validate_role_ownership_agent_principal_test.go @@ -0,0 +1,344 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package sequential + +import ( + "context" + "fmt" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + routev1 "github.com/openshift/api/route/v1" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + "sigs.k8s.io/controller-runtime/pkg/client" + + argov1beta1api "github.com/argoproj-labs/argocd-operator/api/v1beta1" + "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture" + agentFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/agent" + argocdFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/argocd" + deploymentFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/deployment" + fixtureUtils "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/utils" +) + +var _ = Describe("GitOps Operator Sequential E2E Tests", func() { + + Context("1-125_validate_role_ownership_agent_principal", func() { + + var ( + k8sClient client.Client + ctx context.Context + argoCD *argov1beta1api.ArgoCD + ns *corev1.Namespace + cleanupFunc func() + serviceAccount *corev1.ServiceAccount + role *rbacv1.Role + roleBinding *rbacv1.RoleBinding + clusterRole *rbacv1.ClusterRole + clusterRoleBinding *rbacv1.ClusterRoleBinding + serviceNames []string + deploymentNames []string + principalDeployment *appsv1.Deployment + secretNames agentFixture.AgentSecretNames + principalNetworkPolicy *networkingv1.NetworkPolicy + principalRoute *routev1.Route + resourceProxyServiceName string + ) + const ( + argoCDName = "argocd-principal" + argoCDAgentPrincipalName = "argocd-principal-agent-principal" // argoCDName + "-agent-principal" + principalMetricsServiceFmt = "%s-agent-principal-metrics" + principalRedisProxyServiceFmt = "%s-agent-principal-redisproxy" + principalHealthzServiceFmt = "%s-agent-principal-healthz" + clusterRoleName = "argocd-principal-argocd-principal-agent-principal" + clusterRoleBindingName = "argocd-principal-argocd-principal-agent-principal" + nsScopedArgoCDName = "argocd-principal-argocd" + ) + + BeforeEach(func() { + fixture.EnsureSequentialCleanSlate() + fixture.SetEnvInOperatorSubscriptionOrDeployment("ARGOCD_CLUSTER_CONFIG_NAMESPACES", "openshift-gitops, argocd-principal") + + k8sClient, _ = fixtureUtils.GetE2ETestKubeClient() + ctx = context.Background() + ns, cleanupFunc = fixture.CreateNamespaceWithCleanupFunc("argocd-principal") + + // Define ArgoCD CR with principal enabled + argoCD = &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{ + Name: argoCDName, + Namespace: ns.Name, + }, + Spec: argov1beta1api.ArgoCDSpec{ + Controller: argov1beta1api.ArgoCDApplicationControllerSpec{ + Enabled: ptr.To(false), + }, + ArgoCDAgent: &argov1beta1api.ArgoCDAgentSpec{ + Principal: &argov1beta1api.PrincipalSpec{ + Enabled: ptr.To(true), + Auth: "mtls:CN=([^,]+)", + LogLevel: "info", + Namespace: &argov1beta1api.PrincipalNamespaceSpec{ + AllowedNamespaces: []string{ + "*", + }, + }, + TLS: &argov1beta1api.PrincipalTLSSpec{ + InsecureGenerate: ptr.To(true), + }, + JWT: &argov1beta1api.PrincipalJWTSpec{ + InsecureGenerate: ptr.To(true), + }, + Server: &argov1beta1api.PrincipalServerSpec{ + KeepAliveMinInterval: "30s", + }, + }, + }, + SourceNamespaces: []string{ + "agent-managed", + "agent-autonomous", + }, + }, + } + + // Define required resources for principal pod + serviceAccount = &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: argoCDAgentPrincipalName, + Namespace: ns.Name, + }, + } + + role = &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: argoCDAgentPrincipalName, + Namespace: ns.Name, + }, + } + + roleBinding = &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: argoCDAgentPrincipalName, + Namespace: ns.Name, + }, + } + + clusterRole = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterRoleName, + }, + } + + clusterRoleBinding = &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterRoleBindingName, + }, + } + + secretNames = agentFixture.AgentSecretNames{ + JWTSecretName: agentJWTSecretName, + PrincipalTLSSecretName: agentPrincipalTLSSecretName, + RootCASecretName: agentRootCASecretName, + ResourceProxyTLSSecretName: agentResourceProxyTLSSecretName, + RedisInitialPasswordSecretName: "argocd-principal-redis-initial-password", + } + + resourceProxyServiceName = fmt.Sprintf("%s-agent-principal-resource-proxy", argoCDName) + serviceNames = []string{ + argoCDAgentPrincipalName, + fmt.Sprintf(principalMetricsServiceFmt, argoCDName), + fmt.Sprintf("%s-redis", argoCDName), + fmt.Sprintf("%s-repo-server", argoCDName), + fmt.Sprintf("%s-server", argoCDName), + fmt.Sprintf(principalRedisProxyServiceFmt, argoCDName), + resourceProxyServiceName, + fmt.Sprintf(principalHealthzServiceFmt, argoCDName), + } + deploymentNames = []string{fmt.Sprintf("%s-redis", argoCDName), fmt.Sprintf("%s-repo-server", argoCDName), fmt.Sprintf("%s-server", argoCDName)} + + principalDeployment = &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: argoCDAgentPrincipalName, + Namespace: ns.Name, + }, + } + + principalRoute = &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-agent-principal", argoCDName), + Namespace: ns.Name, + }, + } + principalNetworkPolicy = &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-agent-principal-network-policy", argoCDName), + Namespace: ns.Name, + }, + } + + principalNetworkPolicy = &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-agent-principal-network-policy", argoCDName), + Namespace: ns.Name, + }, + } + + }) + + AfterEach(func() { + By("Cleanup cluster-scoped resources") + if clusterRole != nil { + _ = k8sClient.Delete(ctx, clusterRole) + } + if clusterRoleBinding != nil { + _ = k8sClient.Delete(ctx, clusterRoleBinding) + } + + By("Cleanup namespace") + if cleanupFunc != nil { + cleanupFunc() + } + }) + + createRequiredSecrets := func(namespace *corev1.Namespace, additionalPrincipalSANs ...string) { + agentFixture.CreateRequiredSecrets(agentFixture.PrincipalSecretsConfig{ + PrincipalNamespaceName: namespace.Name, + PrincipalServiceName: argoCDAgentPrincipalName, + ResourceProxyServiceName: resourceProxyServiceName, + JWTSecretName: secretNames.JWTSecretName, + PrincipalTLSSecretName: secretNames.PrincipalTLSSecretName, + RootCASecretName: secretNames.RootCASecretName, + ResourceProxyTLSSecretName: secretNames.ResourceProxyTLSSecretName, + AdditionalPrincipalSANs: additionalPrincipalSANs, + }) + } + + verifyExpectedResourcesExist := func(namespace *corev1.Namespace, expectRoute ...bool) { + var expectRoutePtr *bool + if len(expectRoute) > 0 { + expectRoutePtr = ptr.To(expectRoute[0]) + } + + agentFixture.VerifyExpectedResourcesExist(agentFixture.VerifyExpectedResourcesExistParams{ + Namespace: namespace, + ArgoCDAgentPrincipalName: argoCDAgentPrincipalName, + ArgoCDName: argoCDName, + ServiceAccount: serviceAccount, + Role: role, + RoleBinding: roleBinding, + ClusterRole: clusterRole, + ClusterRoleBinding: clusterRoleBinding, + PrincipalDeployment: principalDeployment, + PrincipalRoute: principalRoute, + PrincipalNetworkPolicy: principalNetworkPolicy, + SecretNames: secretNames, + ServiceNames: serviceNames, + DeploymentNames: deploymentNames, + ExpectRoute: expectRoutePtr, + }) + } + + It("validates that the role bug is fixed in agent", func() { + By("Create ArgoCD instance") + + Expect(k8sClient.Create(ctx, argoCD)).To(Succeed()) + + By("Verify expected resources are created for principal pod") + + verifyExpectedResourcesExist(ns) + + By("Create required secrets and certificates for principal pod to start properly") + + createRequiredSecrets(ns) + + By("Verify principal pod starts successfully by checking logs") + + agentFixture.VerifyLogs(argoCDAgentPrincipalName, ns.Name, []string{ + "Starting metrics server", + "Redis proxy started", + "Application informer synced and ready", + "AppProject informer synced and ready", + "Resource proxy started", + "Namespace informer synced and ready", + "Starting healthz server", + }) + + By("verify that deployment is in Ready state") + + Eventually(principalDeployment, "120s", "5s").Should(deploymentFixture.HaveReadyReplicas(1), "Principal deployment should become ready") + + By("Fetch Uid of clusterrole and clusterrolebinding") + clusterRole = &rbacv1.ClusterRole{} + Eventually(func() error { + return k8sClient.Get(ctx, client.ObjectKey{Name: clusterRoleName}, clusterRole) + }).Should(Succeed(), "ClusterRole should exist") + initialClusterRoleUid := clusterRole.GetUID() + + clusterRoleBinding = &rbacv1.ClusterRoleBinding{} + Eventually(func() error { + return k8sClient.Get(ctx, client.ObjectKey{Name: clusterRoleBindingName}, clusterRoleBinding) + }).Should(Succeed(), "ClusterRoleBinding should exist") + initialClusterRoleBindingUid := clusterRoleBinding.GetUID() + + By("Create namespace-scoped ArgoCD instance namespace") + + // Create namespace for hosting namespace-scoped ArgoCD instance with principal + nsScoped, cleanupFuncScoped := fixture.CreateNamespaceWithCleanupFunc("principal") + defer cleanupFuncScoped() + + // Update namespace in ArgoCD CR + argoCD.ResourceVersion = "" + argoCD.UID = "" + argoCD.Name = "argocd-principal-argocd" + argoCD.Namespace = nsScoped.Name + + // Update namespace in resource references + serviceAccount.Namespace = nsScoped.Name + role.Namespace = nsScoped.Name + roleBinding.Namespace = nsScoped.Name + principalDeployment.Namespace = nsScoped.Name + principalRoute.Namespace = nsScoped.Name + principalNetworkPolicy.Namespace = nsScoped.Name + + By("Create namespace-scoped ArgoCD instance with principal") + + Expect(k8sClient.Create(ctx, argoCD)).To(Succeed()) + Eventually(argoCD, "5m", "5s").Should(argocdFixture.BeAvailable()) + + By("Verify UID of ClusterRole and ClusterRoleBinding") + newClusterRole := &rbacv1.ClusterRole{} + Eventually(func() error { + return k8sClient.Get(ctx, client.ObjectKey{Name: clusterRoleName}, newClusterRole) + }).Should(Succeed(), "ClusterRole should exist and be fetchable") + newClusterRoleUid := newClusterRole.GetUID() + + newClusterRoleBinding := &rbacv1.ClusterRoleBinding{} + Eventually(func() error { + return k8sClient.Get(ctx, client.ObjectKey{Name: clusterRoleBindingName}, newClusterRoleBinding) + }).Should(Succeed(), "ClusterRoleBinding should exist and be fetchable") + newClusterRoleBindingUid := newClusterRoleBinding.GetUID() + + Expect(newClusterRoleUid).To(Equal(initialClusterRoleUid), "ClusterRole UID should remain the same after creating namespace-scoped ArgoCD instance") + Expect(newClusterRoleBindingUid).To(Equal(initialClusterRoleBindingUid), "ClusterRoleBinding UID should remain the same after creating namespace-scoped ArgoCD instance") + + }) + + }) +}) diff --git a/test/openshift/e2e/ginkgo/sequential/1-125_validate_role_ownership_image_updater.go b/test/openshift/e2e/ginkgo/sequential/1-125_validate_role_ownership_image_updater.go new file mode 100644 index 00000000000..525189ed6e7 --- /dev/null +++ b/test/openshift/e2e/ginkgo/sequential/1-125_validate_role_ownership_image_updater.go @@ -0,0 +1,189 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package sequential + +import ( + "context" + "strings" + + argov1beta1api "github.com/argoproj-labs/argocd-operator/api/v1beta1" + argocdFixture "github.com/argoproj-labs/argocd-operator/tests/ginkgo/fixture/argocd" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture" + deplFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/deployment" + k8sFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/k8s" + osFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/os" + fixtureUtils "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/utils" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var _ = Describe("GitOps Operator Parallel E2E Tests", func() { + + Context("1-125_validate_role_ownership_image_updater", func() { + + var ( + ctx context.Context + k8sClient client.Client + ns *corev1.Namespace + cleanupFunc func() + ) + const ( + imageUpdaterControllerClusterRoleName = "image-updater-image-updater-argocd-image-updater-controller" + imageUpdaterControllerClusterRoleBindingName = "image-updater-image-updater-argocd-image-updater-controller" + ) + BeforeEach(func() { + fixture.EnsureParallelCleanSlate() + k8sClient, _ = fixtureUtils.GetE2ETestKubeClient() + ctx = context.Background() + }) + + AfterEach(func() { + if cleanupFunc != nil { + cleanupFunc() + } + + fixture.OutputDebugOnFail(ns) + + }) + + It("validates role bug fixes for image updater", func() { + By("create a simple namespace scoped ArgoCD instance with image updater enabled and watch namespace set to '*'") + ns, cleanupFunc = fixture.CreateNamespaceWithCleanupFunc("image-updater") + + By("ensuring default service account has anyuid SCC permission") + serviceAccountUser := "system:serviceaccount:" + ns.Name + ":default" + output, err := osFixture.ExecCommand("oc", "auth", "can-i", "use", "scc/anyuid", "--as", serviceAccountUser) + hasPermission := false + if err == nil && len(output) > 0 { + // Check if the service account user is already in the users list + // Remove quotes and whitespace for comparison + output = strings.TrimSpace(strings.Trim(output, "'\"")) + if strings.Contains(output, serviceAccountUser) { + hasPermission = true + } + } + if !hasPermission { + _, err := osFixture.ExecCommand("oc", "adm", "policy", "add-scc-to-user", "anyuid", "-z", "default", "-n", ns.Name) + Expect(err).NotTo(HaveOccurred(), "Failed to add anyuid SCC to default service account") + } + fixture.SetEnvInOperatorSubscriptionOrDeployment("ARGOCD_CLUSTER_CONFIG_NAMESPACES", "openshift-gitops,image-updater") + + argoCD := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{Name: "image-updater", Namespace: ns.Name}, + Spec: argov1beta1api.ArgoCDSpec{ + ImageUpdater: argov1beta1api.ArgoCDImageUpdaterSpec{ + Env: []corev1.EnvVar{ + { + Name: "IMAGE_UPDATER_WATCH_NAMESPACES", + Value: "*", + }, + }, + Enabled: true}, + }, + } + + Expect(k8sClient.Create(ctx, argoCD)).To(Succeed()) + + By("waiting for ArgoCD CR to be reconciled and the instance to be ready") + Eventually(argoCD, "5m", "5s").Should(argocdFixture.BeAvailable()) + + By("verifying image updater workload has started argocd-image-updater-controller") + depl := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "image-updater-argocd-image-updater-controller", Namespace: "image-updater"}} + Eventually(depl, "2m", "5s").Should(k8sFixture.ExistByName(), "Deployment image-updater-argocd-image-updater-controller did not exist within timeout") + Eventually(depl, "2m", "5s").Should(deplFixture.HaveReplicas(1), "Deployment image-updater-argocd-image-updater-controller did not have correct replicas within timeout") + Eventually(depl, "3m", "5s").Should(deplFixture.HaveReadyReplicas(1), "Deployment image-updater-argocd-image-updater-controller was not ready within timeout") + + By("Verify ClusterRole and ClusterRoleBinding for ArgoCD Image Updater Controller") + clusterRole := &rbacv1.ClusterRole{} + Eventually(func() error { + return k8sClient.Get(ctx, client.ObjectKey{Name: imageUpdaterControllerClusterRoleName}, clusterRole) + }).Should(Succeed(), "ClusterRole should exist and be fetchable") + + clusterRoleBinding := &rbacv1.ClusterRoleBinding{} + Eventually(func() error { + return k8sClient.Get(ctx, client.ObjectKey{Name: imageUpdaterControllerClusterRoleBindingName}, clusterRoleBinding) + }).Should(Succeed(), "ClusterRoleBinding should exist and be fetchable") + initialClusterRoleUid := clusterRole.GetUID() + initialClusterRoleBindingUid := clusterRoleBinding.GetUID() + + By("Create ArgoCD instance in a new namespace") + + ns1, nsCleanup := fixture.CreateNamespaceWithCleanupFunc("updater") + defer nsCleanup() + + By("ensuring default service account has anyuid SCC permission") + serviceAccountUser = "system:serviceaccount:" + ns1.Name + ":default" + output, err = osFixture.ExecCommand("oc", "auth", "can-i", "use", "scc/anyuid", "--as", serviceAccountUser) + hasPermission = false + if err == nil && len(output) > 0 { + // Check if the service account user is already in the users list + // Remove quotes and whitespace for comparison + output = strings.TrimSpace(strings.Trim(output, "'\"")) + if strings.Contains(output, serviceAccountUser) { + hasPermission = true + } + } + if !hasPermission { + _, err := osFixture.ExecCommand("oc", "adm", "policy", "add-scc-to-user", "anyuid", "-z", "default", "-n", ns1.Name) + Expect(err).NotTo(HaveOccurred(), "Failed to add anyuid SCC to default service account") + } + + argoCD1 := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{ + Name: "image-updater-image", + Namespace: ns1.Name, + }, + Spec: argov1beta1api.ArgoCDSpec{ + ImageUpdater: argov1beta1api.ArgoCDImageUpdaterSpec{ + Env: []corev1.EnvVar{ + { + Name: "IMAGE_UPDATER_LOGLEVEL", + Value: "trace", + }, + }, + Enabled: true, + }, + }, + } + Expect(k8sClient.Create(ctx, argoCD1)).To(Succeed()) + + Eventually(argoCD1, "5m", "5s").Should(argocdFixture.BeAvailable()) + + By("Verify UID of ClusterRole and ClusterRoleBinding remain the same after creating namespace-scoped ArgoCD instance") + newClusterRole := &rbacv1.ClusterRole{} + Eventually(func() error { + return k8sClient.Get(ctx, client.ObjectKey{Name: imageUpdaterControllerClusterRoleName}, newClusterRole) + }).Should(Succeed(), "ClusterRole should exist and be fetchable") + newClusterRoleUid := newClusterRole.GetUID() + + newClusterRoleBinding := &rbacv1.ClusterRoleBinding{} + Eventually(func() error { + return k8sClient.Get(ctx, client.ObjectKey{Name: imageUpdaterControllerClusterRoleBindingName}, newClusterRoleBinding) + }).Should(Succeed(), "ClusterRoleBinding should exist and be fetchable") + + newClusterRoleBindingUid := newClusterRoleBinding.GetUID() + + Expect(newClusterRoleUid).To(Equal(initialClusterRoleUid), "ClusterRole UID should remain the same after creating namespace-scoped ArgoCD instance") + Expect(newClusterRoleBindingUid).To(Equal(initialClusterRoleBindingUid), "ClusterRoleBinding UID should remain the same after creating namespace-scoped ArgoCD instance") + }) + }) +})