From 1db98490f14041857006ae9cf3adc85e91d31bf7 Mon Sep 17 00:00:00 2001 From: Jonathan West Date: Thu, 4 Dec 2025 02:19:57 -0500 Subject: [PATCH] chore: update to argocd-operator '9249b2daeebaaf35723f9712fe9a47c65ca122b6' Signed-off-by: Jonathan West --- Makefile | 2 +- .../argocd/openshift/openshift_test.go | 4 +- go.mod | 2 +- go.sum | 4 +- test/openshift/e2e/ginkgo/fixture/fixture.go | 25 ++ .../e2e/ginkgo/fixture/k8s/fixture.go | 2 + .../e2e/ginkgo/fixture/secret/fixture.go | 10 + ...e_rolebinding_for_source_namespace_test.go | 73 +++-- ...te_applicationset_in_any_namespace_test.go | 213 +++++++++++--- ...1-113_validate_namespacemanagement_test.go | 2 +- .../1-122_validate_namespace_test.go | 268 ++++++++++++++++++ 11 files changed, 531 insertions(+), 74 deletions(-) create mode 100644 test/openshift/e2e/ginkgo/sequential/1-122_validate_namespace_test.go diff --git a/Makefile b/Makefile index 70fd28f68..4718f0529 100644 --- a/Makefile +++ b/Makefile @@ -203,7 +203,7 @@ build: generate fmt vet ## Build manager binary. .PHONY: run run: manifests generate fmt vet ## Run a controller from your host. - CLUSTER_SCOPED_ARGO_ROLLOUTS_NAMESPACES=argo-rollouts,test-rom-ns-1,rom-ns-1,openshift-gitops ARGOCD_CLUSTER_CONFIG_NAMESPACES=openshift-gitops REDIS_CONFIG_PATH="build/redis" go run ./cmd/main.go + CLUSTER_SCOPED_ARGO_ROLLOUTS_NAMESPACES=argo-rollouts,test-rom-ns-1,rom-ns-1,openshift-gitops ARGOCD_CLUSTER_CONFIG_NAMESPACES="openshift-gitops, argocd-e2e-cluster-config, argocd-test-impersonation-1-046, argocd-agent-principal-1-051, argocd-agent-agent-1-052, appset-argocd, appset-old-ns, appset-new-ns" REDIS_CONFIG_PATH="build/redis" go run ./cmd/main.go .PHONY: docker-build docker-build: test ## Build docker image with the manager. diff --git a/controllers/argocd/openshift/openshift_test.go b/controllers/argocd/openshift/openshift_test.go index b8ce1dff2..353ad036d 100644 --- a/controllers/argocd/openshift/openshift_test.go +++ b/controllers/argocd/openshift/openshift_test.go @@ -306,10 +306,10 @@ func TestAdminClusterRoleMapper(t *testing.T) { // Sort both slices to ensure consistent comparison sort.Slice(result, func(i, j int) bool { - return result[i].NamespacedName.Name < result[j].NamespacedName.Name + return result[i].Name < result[j].Name }) sort.Slice(expectedRequests, func(i, j int) bool { - return expectedRequests[i].NamespacedName.Name < expectedRequests[j].NamespacedName.Name + return expectedRequests[i].Name < expectedRequests[j].Name }) assert.Equal(t, expectedRequests, result) diff --git a/go.mod b/go.mod index f9e09ab4a..b9aa51473 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.24.6 require ( github.com/argoproj-labs/argo-rollouts-manager v0.0.6-0.20250731075119-a100fc1d88b8 - github.com/argoproj-labs/argocd-operator v0.0.0-20251125105011-0c039cea85fd + github.com/argoproj-labs/argocd-operator v0.16.0-rc1.0.20251204063443-9249b2daeeba github.com/argoproj/argo-cd/v3 v3.1.5 github.com/argoproj/gitops-engine v0.7.1-0.20250905160054-e48120133eec github.com/go-logr/logr v1.4.3 diff --git a/go.sum b/go.sum index 942a42b65..a9d527b7b 100644 --- a/go.sum +++ b/go.sum @@ -31,8 +31,8 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/argoproj-labs/argo-rollouts-manager v0.0.6-0.20250731075119-a100fc1d88b8 h1:6+eo7BKrNkSIhQ1nnyCUloSNrGzghlb8r8e7GokoeBo= github.com/argoproj-labs/argo-rollouts-manager v0.0.6-0.20250731075119-a100fc1d88b8/go.mod h1:yTwzKUV79YyI764hkXdVojGYBA9yKJk3qXx5mRuQ2Xc= -github.com/argoproj-labs/argocd-operator v0.0.0-20251125105011-0c039cea85fd h1:67RjGDPc5LI2aWGSnnOtAi9LoMSnuBP0oFm5LLoyQlk= -github.com/argoproj-labs/argocd-operator v0.0.0-20251125105011-0c039cea85fd/go.mod h1:NCFt9E3K/eXfjfuXGQXLe+zKQCeRCaZv7ZsbbXFPpOw= +github.com/argoproj-labs/argocd-operator v0.16.0-rc1.0.20251204063443-9249b2daeeba h1:78B//Rfc/acaf/4JDGDHwPtOuNHRujC+j9UiMBDyO8A= +github.com/argoproj-labs/argocd-operator v0.16.0-rc1.0.20251204063443-9249b2daeeba/go.mod h1:NCFt9E3K/eXfjfuXGQXLe+zKQCeRCaZv7ZsbbXFPpOw= github.com/argoproj/argo-cd/v3 v3.1.5 h1:dm1SY5CaILDIQIQINA4H6uJrXpExyif2Yz5915g91kQ= github.com/argoproj/argo-cd/v3 v3.1.5/go.mod h1:ZHb/LOz/hr88VWMJiVTd8DGYL7MheHCAT8S6DgYOBFo= github.com/argoproj/gitops-engine v0.7.1-0.20250905160054-e48120133eec h1:rNAwbRQFvRIuW/e2bU+B10mlzghYXsnwZedYeA7Drz4= diff --git a/test/openshift/e2e/ginkgo/fixture/fixture.go b/test/openshift/e2e/ginkgo/fixture/fixture.go index c9822d78c..cd21c15e7 100644 --- a/test/openshift/e2e/ginkgo/fixture/fixture.go +++ b/test/openshift/e2e/ginkgo/fixture/fixture.go @@ -862,6 +862,29 @@ func OutputDebugOnFail(namespaceParams ...any) { GinkgoWriter.Println(kubectlOutput) GinkgoWriter.Println("----------------------------------------------------------------") + kubectlOutput, err = osFixture.ExecCommandWithOutputParam(false, "kubectl", "get", "deployments", "-n", namespace, "-o", "yaml") + if err != nil { + GinkgoWriter.Println("unable to list", namespace, err, kubectlOutput) + continue + } + + GinkgoWriter.Println("") + GinkgoWriter.Println("----------------------------------------------------------------") + GinkgoWriter.Println("'kubectl get deployments -n " + namespace + " -o yaml") + GinkgoWriter.Println(kubectlOutput) + GinkgoWriter.Println("----------------------------------------------------------------") + + kubectlOutput, err = osFixture.ExecCommandWithOutputParam(false, "kubectl", "get", "events", "-n", namespace) + if err != nil { + GinkgoWriter.Println("unable to get events for namespace", err, kubectlOutput) + } else { + GinkgoWriter.Println("") + GinkgoWriter.Println("----------------------------------------------------------------") + GinkgoWriter.Println("'kubectl get events -n " + namespace + ":") + GinkgoWriter.Println(kubectlOutput) + GinkgoWriter.Println("----------------------------------------------------------------") + } + } kubectlOutput, err := osFixture.ExecCommandWithOutputParam(false, "kubectl", "get", "argocds", "-A", "-o", "yaml") @@ -875,6 +898,8 @@ func OutputDebugOnFail(namespaceParams ...any) { GinkgoWriter.Println("----------------------------------------------------------------") } + GinkgoWriter.Println("You can skip this debug output by setting 'SKIP_DEBUG_OUTPUT=true'") + } func outputPodLog(podSubstring string) { diff --git a/test/openshift/e2e/ginkgo/fixture/k8s/fixture.go b/test/openshift/e2e/ginkgo/fixture/k8s/fixture.go index 70d5f4b84..c520c2f25 100644 --- a/test/openshift/e2e/ginkgo/fixture/k8s/fixture.go +++ b/test/openshift/e2e/ginkgo/fixture/k8s/fixture.go @@ -90,6 +90,8 @@ func NotHaveLabelWithValue(key string, value string) matcher.GomegaMatcher { return true } + GinkgoWriter.Println("NotHaveLabelWithValue: not expected: ", key, "/", value, ". actual:", labels[key]) + return labels[key] != value }, BeTrue()) diff --git a/test/openshift/e2e/ginkgo/fixture/secret/fixture.go b/test/openshift/e2e/ginkgo/fixture/secret/fixture.go index abb9e6266..42e531637 100644 --- a/test/openshift/e2e/ginkgo/fixture/secret/fixture.go +++ b/test/openshift/e2e/ginkgo/fixture/secret/fixture.go @@ -82,6 +82,16 @@ func HaveDataKeyValue(key string, value []byte) matcher.GomegaMatcher { } +// NotHaveDataKey returns true if Secret's .data 'key' does not exist, false otherwise +func NotHaveDataKey(key string) matcher.GomegaMatcher { + return fetchSecret(func(secret *corev1.Secret) bool { + _, exists := secret.Data[key] + GinkgoWriter.Println("NotHaveDataKey - key:", key, "Exists:", exists) + return !exists + }) + +} + // This is intentionally NOT exported, for now. Create another function in this file/package that calls this function, and export that. func fetchSecret(f func(*corev1.Secret) bool) matcher.GomegaMatcher { diff --git a/test/openshift/e2e/ginkgo/sequential/1-036_validate_role_rolebinding_for_source_namespace_test.go b/test/openshift/e2e/ginkgo/sequential/1-036_validate_role_rolebinding_for_source_namespace_test.go index a064c5df2..b0e385c24 100644 --- a/test/openshift/e2e/ginkgo/sequential/1-036_validate_role_rolebinding_for_source_namespace_test.go +++ b/test/openshift/e2e/ginkgo/sequential/1-036_validate_role_rolebinding_for_source_namespace_test.go @@ -12,7 +12,9 @@ import ( namespaceFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/namespace" "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/utils" + corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -22,8 +24,10 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Context("1-036_validate_role_rolebinding_for_source_namespace", func() { var ( - ctx context.Context - k8sClient client.Client + ctx context.Context + k8sClient client.Client + argoNamespace *corev1.Namespace + cleanupArgoNSFunc func() defaultNSArgoCD *v1beta1.ArgoCD @@ -39,30 +43,39 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { AfterEach(func() { - fixture.OutputDebugOnFail("default") + fixture.OutputDebugOnFail(argoNamespace) + + // Clean up argo cd instance created in test namespace first (before deleting the namespace itself) + Expect(defaultNSArgoCD).ToNot(BeNil()) + err := k8sClient.Delete(ctx, defaultNSArgoCD) + if err != nil && !apierrors.IsNotFound(err) { + Expect(err).ToNot(HaveOccurred()) + } // Clean up namespaces created for _, namespaceCleanupFunction := range cleanupFunctions { namespaceCleanupFunction() } - // Clean up argo cd instance created in 'default' NS - Expect(defaultNSArgoCD).ToNot(BeNil()) - Expect(k8sClient.Delete(ctx, defaultNSArgoCD)).To(Succeed()) - }) It("verifies that ArgoCD CR '.spec.sourceNamespaces' field wildcard-matching matches and manages only namespaces which match the wildcard", func() { + fixture.SetEnvInOperatorSubscriptionOrDeployment("ARGOCD_CLUSTER_CONFIG_NAMESPACES", "openshift-gitops, argocd-e2e-cluster-config") + + By("creating cluster-scoped namespace for Argo CD instance") + argoNamespace, cleanupArgoNSFunc = fixture.CreateNamespaceWithCleanupFunc("argocd-e2e-cluster-config") + cleanupFunctions = append(cleanupFunctions, cleanupArgoNSFunc) + By("creating test NS") testNS, cleanupFunc := fixture.CreateNamespaceWithCleanupFunc("test") cleanupFunctions = append(cleanupFunctions, cleanupFunc) - By("creating Argo CD instance in default NS, with 'test' sourceNamespace only") + By("creating Argo CD instance in argocd-e2e-cluster-config NS, with 'test' sourceNamespace only") defaultNSArgoCD = &v1beta1.ArgoCD{ ObjectMeta: metav1.ObjectMeta{ Name: "example-argocd", - Namespace: "default", + Namespace: argoNamespace.Name, }, Spec: v1beta1.ArgoCDSpec{ SourceNamespaces: []string{ @@ -73,7 +86,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Expect(k8sClient.Create(ctx, defaultNSArgoCD)).To(Succeed()) By("verifying Argo CD instance starts managing the namespace via managed-by-cluster-argocd label") - Eventually(testNS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "default")) + Eventually(testNS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", argoNamespace.Name)) expectRoleAndRoleBindingValues := func(name string, ns string) { @@ -107,12 +120,12 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { { Kind: "ServiceAccount", Name: "example-argocd-argocd-server", - Namespace: "default", + Namespace: argoNamespace.Name, }, { Kind: "ServiceAccount", Name: "example-argocd-argocd-application-controller", - Namespace: "default", + Namespace: argoNamespace.Name, }, })) @@ -133,7 +146,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { }) By("verifying test-1 NS becomes managed, and expected role/rolebindings exist in test* namespaces but not dev") - Eventually(test1NS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "default")) + Eventually(test1NS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", argoNamespace.Name)) expectRoleAndRoleBindingValues("example-argocd_test", "test") @@ -161,7 +174,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { cleanupFunctions = append(cleanupFunctions, cleanupFunc) By("verifying the test-2 namespace becomes managed by the argo cd instance, and has the expected role/rolebinding") - Eventually(test2NS, "2m", "5s").Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "default")) + Eventually(test2NS, "2m", "5s").Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", argoNamespace.Name)) expectRoleAndRoleBindingValues("example-argocd_test-2", "test-2") @@ -173,17 +186,17 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { }) By("verifying test, test-1, test-2, and dev are all managed and have the expected roles") - Eventually(testNS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "default")) - Consistently(testNS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "default")) + Eventually(testNS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", argoNamespace.Name)) + Consistently(testNS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", argoNamespace.Name)) - Eventually(test1NS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "default")) - Consistently(test1NS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "default")) + Eventually(test1NS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", argoNamespace.Name)) + Consistently(test1NS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", argoNamespace.Name)) - Eventually(test2NS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "default")) - Consistently(test2NS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "default")) + Eventually(test2NS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", argoNamespace.Name)) + Consistently(test2NS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", argoNamespace.Name)) - Eventually(devNS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "default")) - Consistently(devNS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "default")) + Eventually(devNS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", argoNamespace.Name)) + Consistently(devNS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", argoNamespace.Name)) expectRoleAndRoleBindingValues("example-argocd_test", "test") expectRoleAndRoleBindingValues("example-argocd_test-1", "test-1") @@ -208,11 +221,11 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { cleanupFunctions = append(cleanupFunctions, cleanupFunc) By("verifying test-ns-1 and dev-ns-1 are managed, but other-ns isn't") - Eventually(test_ns_1NS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "default")) - Consistently(test_ns_1NS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "default")) + Eventually(test_ns_1NS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", argoNamespace.Name)) + Consistently(test_ns_1NS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", argoNamespace.Name)) - Eventually(dev_ns_1NS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "default")) - Consistently(dev_ns_1NS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "default")) + Eventually(dev_ns_1NS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", argoNamespace.Name)) + Consistently(dev_ns_1NS).Should(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", argoNamespace.Name)) expectRoleAndRoleBindingValues("example-argocd_test-ns-1", "test-ns-1") expectRoleAndRoleBindingValues("example-argocd_dev-ns-1", "dev-ns-1") @@ -239,18 +252,18 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { }) By("verifying dev-ns-1 eventually becomes unmanaged") - Eventually(dev_ns_1NS).ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "default")) - Consistently(dev_ns_1NS).ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", "default")) + Eventually(dev_ns_1NS).ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", argoNamespace.Name)) + Consistently(dev_ns_1NS).ShouldNot(namespaceFixture.HaveLabel("argocd.argoproj.io/managed-by-cluster-argocd", argoNamespace.Name)) devns1Role := &rbacv1.Role{ ObjectMeta: metav1.ObjectMeta{ - Name: " example-argocd_dev-ns-1", + Name: "example-argocd_dev-ns-1", Namespace: dev_ns_1NS.Name, }, } devns1RoleBinding := &rbacv1.RoleBinding{ ObjectMeta: metav1.ObjectMeta{ - Name: " example-argocd_dev-ns-1", + Name: "example-argocd_dev-ns-1", Namespace: dev_ns_1NS.Name, }, } diff --git a/test/openshift/e2e/ginkgo/sequential/1-037_validate_applicationset_in_any_namespace_test.go b/test/openshift/e2e/ginkgo/sequential/1-037_validate_applicationset_in_any_namespace_test.go index 6d85c99ae..3e9eed7e4 100644 --- a/test/openshift/e2e/ginkgo/sequential/1-037_validate_applicationset_in_any_namespace_test.go +++ b/test/openshift/e2e/ginkgo/sequential/1-037_validate_applicationset_in_any_namespace_test.go @@ -5,15 +5,18 @@ import ( "fmt" "github.com/argoproj-labs/argocd-operator/api/v1beta1" + "github.com/argoproj-labs/argocd-operator/common" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture" 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" k8sFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/k8s" namespaceFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/namespace" roleFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/role" "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" @@ -40,7 +43,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { AfterEach(func() { - fixture.OutputDebugOnFail("appset-argocd", "appset-old-ns", "appset-new-ns") + fixture.OutputDebugOnFail("appset-argocd", "appset-old-ns", "appset-new-ns", "appset-namespace-scoped", "target-ns-1-037") // Clean up namespaces created for _, namespaceCleanupFunction := range cleanupFunctions { @@ -51,6 +54,8 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { It("verifying that ArgoCD CR '.spec.applicationset.sourcenamespaces' and '.spec.sourcenamespaces' correctly control role/rolebindings within the managed namespaces", func() { + fixture.SetEnvInOperatorSubscriptionOrDeployment("ARGOCD_CLUSTER_CONFIG_NAMESPACES", "openshift-gitops, argocd-e2e-cluster-config, appset-argocd, appset-old-ns, appset-new-ns") + By("0) create namespaces: appset-argocd, appset-old-ns, appset-new-ns") appset_argocdNS, cleanupFunc := fixture.CreateNamespaceWithCleanupFunc("appset-argocd") @@ -81,6 +86,16 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { } Expect(k8sClient.Create(ctx, argoCD)).To(Succeed()) + By("verifying that the appset deployment does not contains 'applications in any namespace' parameter, since no source namespaces are specified") + appsetDeployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-applicationset-controller", + Namespace: argoCD.Namespace, + }, + } + Eventually(appsetDeployment).Should(k8sFixture.ExistByName()) + Expect(appsetDeployment).ShouldNot(deploymentFixture.HaveContainerCommandSubstring("--applicationset-namespaces", 0)) + Eventually(argoCD, "5m", "5s").Should(argocdFixture.BeAvailable()) Eventually(argoCD).Should(argocdFixture.HaveApplicationSetControllerStatus("Running")) @@ -142,6 +157,16 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Eventually(argoCD, "5m", "5s").Should(argocdFixture.BeAvailable()) + By("verifying that the appset deployment does not contain 'applications in any namespace' parameter, because .spec.sourceNamespaces is not specified") + appsetDeployment = &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-applicationset-controller", + Namespace: argoCD.Namespace, + }, + } + Eventually(appsetDeployment).Should(k8sFixture.ExistByName()) + Eventually(appsetDeployment).ShouldNot(deploymentFixture.HaveContainerCommandSubstring("--applicationset-namespaces", 0)) + expectRoleAndRoleBindingAndNamespaceToNotBeManaged([]string{"example_appset-old-ns", "example-appset-argocd-applicationset"}, appset_old_nsNS.Name) // ---- @@ -186,6 +211,10 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { } }) + By("verifying appset namespaces parameter exists, and it points to only the namespace specified in .spec.sourceNamespaces") + Eventually(appsetDeployment).Should(k8sFixture.ExistByName()) + Eventually(appsetDeployment).Should(deploymentFixture.HaveContainerCommandSubstring("--applicationset-namespaces appset-new-ns", 0)) + By("verifying that Role in appset-new-ns has expected RBAC permissions: ability to modify applications, batch, and applicationsets") example_appset_new_nsRole := &rbacv1.Role{ ObjectMeta: metav1.ObjectMeta{Name: "example_appset-new-ns", Namespace: appset_new_nsNS.Name}, @@ -206,15 +235,6 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { "delete", }, }, - { - APIGroups: []string{"batch"}, - Resources: []string{ - "jobs", - "cronjobs", - "cronjobs/finalizers", - }, - Verbs: []string{"create", "update"}, - }, { APIGroups: []string{"argoproj.io"}, Resources: []string{"applicationsets"}, @@ -303,6 +323,9 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { } }) + Eventually(appsetDeployment).Should(k8sFixture.ExistByName()) + Eventually(appsetDeployment).Should(deploymentFixture.HaveContainerCommandSubstring("--applicationset-namespaces appset-old-ns,appset-new-ns", 0)) + By("verifying that appset-old-ns gains Role/RoleBindings similar to appset-new-ns") example_appset_old_nsRole := &rbacv1.Role{ ObjectMeta: metav1.ObjectMeta{ @@ -325,15 +348,6 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { "delete", }, }, - { - APIGroups: []string{"batch"}, - Resources: []string{ - "jobs", - "cronjobs", - "cronjobs/finalizers", - }, - Verbs: []string{"create", "update"}, - }, { APIGroups: []string{"argoproj.io"}, Resources: []string{ @@ -413,15 +427,6 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { "delete", }, }, - { - APIGroups: []string{"batch"}, - Resources: []string{ - "jobs", - "cronjobs", - "cronjobs/finalizers", - }, - Verbs: []string{"create", "update"}, - }, { APIGroups: []string{"argoproj.io"}, Resources: []string{ @@ -503,15 +508,6 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { "delete", }, }, - { - APIGroups: []string{"batch"}, - Resources: []string{ - "jobs", - "cronjobs", - "cronjobs/finalizers", - }, - Verbs: []string{"create", "update"}, - }, })) By("verifying RoleBinding still has expected role and subjects") @@ -593,5 +589,148 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { }) + It("verifies that ArgoCD sourcenamespaces resources are cleaned up automatically", func() { + + By("creating Argo CD namespace and appset source namespace") + targetNS, cleanupFunc := fixture.CreateNamespaceWithCleanupFunc("target-ns-1-037") + cleanupFunctions = append(cleanupFunctions, cleanupFunc) + + appset_argocdNS, cleanupFunc := fixture.CreateNamespaceWithCleanupFunc("appset-namespace-scoped") + cleanupFunctions = append(cleanupFunctions, cleanupFunc) + + By("creating ArgoCD instance with target ns as a source NS, BUT note the ArgoCD instance is namespace-scoped") + argoCD := &v1beta1.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example", + Namespace: appset_argocdNS.Name, + }, + Spec: v1beta1.ArgoCDSpec{ + ApplicationSet: &v1beta1.ArgoCDApplicationSet{ + SourceNamespaces: []string{targetNS.Name}, + }, + SourceNamespaces: []string{targetNS.Name}, + }, + } + Expect(k8sClient.Create(ctx, argoCD)).To(Succeed()) + Eventually(argoCD).Should(argocdFixture.HaveApplicationSetControllerStatus("Running")) + + By("verifying that the appset deplomyent does not contain 'applications in any namespace' parameter") + appsetDeployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-applicationset-controller", + Namespace: argoCD.Namespace, + }, + } + Eventually(appsetDeployment).Should(k8sFixture.ExistByName()) + Expect(appsetDeployment).ShouldNot(deploymentFixture.HaveContainerCommandSubstring("--applicationset-namespaces", 0)) + + By("first verify that the ClusterRole was not automatically created for the Argo CD instance") + clusterRole := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: argoCD.Name + "-" + argoCD.Namespace + "-" + common.ArgoCDApplicationSetControllerComponent, + Annotations: common.DefaultAnnotations(argoCD.Name, argoCD.Namespace), + }, + } + Consistently(clusterRole).Should(k8sFixture.NotExistByName()) + + By("creating ClusterRole and then ensuring it is automatically cleaned up") + Expect(k8sClient.Create(ctx, clusterRole)).To(Succeed()) + Eventually(clusterRole).ShouldNot(k8sFixture.ExistByName()) + + By("first verify that ClusterRoleBinding was not automatically created for the Argo CD instance") + clusterRoleBinding := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: argoCD.Name + "-" + argoCD.Namespace + "-" + common.ArgoCDApplicationSetControllerComponent, + Annotations: common.DefaultAnnotations(argoCD.Name, argoCD.Namespace), + }, + Subjects: []rbacv1.Subject{ + { + Kind: "ServiceAccount", + Name: "sa", + Namespace: argoCD.Namespace, + }, + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "ClusterRole", + Name: clusterRole.Name, + }, + } + Consistently(clusterRoleBinding).Should(k8sFixture.NotExistByName()) + By("creating ClusterRoleBinding and then ensuring it is automatically cleaned up") + Expect(k8sClient.Create(ctx, clusterRoleBinding)).To(Succeed()) + Eventually(clusterRoleBinding).ShouldNot(k8sFixture.ExistByName()) + + By("first verifying that Role does not exist in namespace specified in appset sourceNamespaces field") + roleInTargetNS := &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-%s-applicationset", argoCD.Name, argoCD.Namespace), + Namespace: targetNS.Name, + }, + } + Consistently(roleInTargetNS).Should(k8sFixture.NotExistByName()) + By("creating Role in source NS and verifying it is not cleaned up (yet)") + Expect(k8sClient.Create(ctx, roleInTargetNS)).To(Succeed()) + Consistently(roleInTargetNS).Should(k8sFixture.ExistByName()) + + By("verifying that there exist no rolebindings that point to the namespace-scoped argocd instance namespace") + Consistently(func() bool { + var roleBindings rbacv1.RoleBindingList + if err := k8sClient.List(ctx, &roleBindings, client.InNamespace(targetNS.Name)); err != nil { + GinkgoWriter.Println(err) + return false + } + for _, crb := range roleBindings.Items { + for _, subject := range crb.Subjects { + if subject.Namespace == argoCD.Namespace { + GinkgoWriter.Println("detected an RB that pointed to namespace scoped ArgoCD instance. This shouldn't happen:", crb.Name) + return false + } + } + } + return true + + }).Should(BeTrue()) + + By("first verifying that RoleBinding does not exist in source namespace") + roleBindingInTargetNS := &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-%s-applicationset", argoCD.Name, argoCD.Namespace), + Namespace: targetNS.Name, + }, + Subjects: []rbacv1.Subject{ + { + Kind: "ServiceAccount", + Name: "sa", + Namespace: argoCD.Namespace, + }, + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "Role", + Name: roleInTargetNS.Name, + }, + } + Consistently(roleBindingInTargetNS).Should(k8sFixture.NotExistByName()) + By("creating RoleBinding in source NS and verifying it is not cleaned up (yet)") + Expect(k8sClient.Create(ctx, roleBindingInTargetNS)).To(Succeed()) + Consistently(roleBindingInTargetNS).Should(k8sFixture.ExistByName()) + + By("adding ArgoCDApplicationSetManagedByClusterArgoCDLabel label to target NS") + namespaceFixture.Update(targetNS, func(n *corev1.Namespace) { + if n.Labels == nil { + n.Labels = map[string]string{} + } + n.Labels[common.ArgoCDApplicationSetManagedByClusterArgoCDLabel] = argoCD.Namespace + }) + + By("verifying the label is automatically removed") + Eventually(targetNS).Should(k8sFixture.NotHaveLabelWithValue(common.ArgoCDApplicationSetManagedByClusterArgoCDLabel, argoCD.Namespace)) + + By("verifying that the roles/rolebindings we created in the previous steps are now automatically cleaned up, because the namespace had the ArgoCDApplicationSetManagedByClusterArgoCDLabel") + Eventually(roleBindingInTargetNS).Should(k8sFixture.NotExistByName()) + Eventually(roleInTargetNS).Should(k8sFixture.NotExistByName()) + }) + }) }) diff --git a/test/openshift/e2e/ginkgo/sequential/1-113_validate_namespacemanagement_test.go b/test/openshift/e2e/ginkgo/sequential/1-113_validate_namespacemanagement_test.go index 62b093576..b7c3f2d44 100644 --- a/test/openshift/e2e/ginkgo/sequential/1-113_validate_namespacemanagement_test.go +++ b/test/openshift/e2e/ginkgo/sequential/1-113_validate_namespacemanagement_test.go @@ -36,7 +36,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { cleanupFunc1 func() cleanupFunc2 func() cleanupFunc3 func() - nmName string = "nm-test" + nmName = "nm-test" ) BeforeEach(func() { diff --git a/test/openshift/e2e/ginkgo/sequential/1-122_validate_namespace_test.go b/test/openshift/e2e/ginkgo/sequential/1-122_validate_namespace_test.go new file mode 100644 index 000000000..d3dc992a7 --- /dev/null +++ b/test/openshift/e2e/ginkgo/sequential/1-122_validate_namespace_test.go @@ -0,0 +1,268 @@ +/* +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" + + argov1beta1api "github.com/argoproj-labs/argocd-operator/api/v1beta1" + "github.com/argoproj-labs/argocd-operator/common" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture" + 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" + k8sFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/k8s" + namespaceFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/namespace" + statefulsetFixture "github.com/redhat-developer/gitops-operator/test/openshift/e2e/ginkgo/fixture/statefulset" + 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 Sequential E2E Tests", func() { + + Context("1-122_validate_namespace", func() { + + var ( + k8sClient client.Client + ctx context.Context + + cleanupArgoNamespace func() + cleanupSourceNamespace func() + argoNamespace *corev1.Namespace + sourceNamespace *corev1.Namespace + ) + + BeforeEach(func() { + fixture.EnsureSequentialCleanSlate() + + k8sClient, _ = fixtureUtils.GetE2ETestKubeClient() + ctx = context.Background() + }) + + AfterEach(func() { + fixture.OutputDebugOnFail(argoNamespace, sourceNamespace) + + if cleanupArgoNamespace != nil { + cleanupArgoNamespace() + } + if cleanupSourceNamespace != nil { + cleanupSourceNamespace() + } + }) + + It("Should validate namespace for new resources", func() { + + argoNamespace, cleanupArgoNamespace = fixture.CreateRandomE2ETestNamespaceWithCleanupFunc() + sourceNamespace, cleanupSourceNamespace = fixture.CreateRandomE2ETestNamespaceWithCleanupFunc() + + By("creating ArgoCD CR with a single source namespace specified") + argoCD := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd", + Namespace: argoNamespace.Name, + }, + Spec: argov1beta1api.ArgoCDSpec{ + SourceNamespaces: []string{sourceNamespace.Name}, + }, + } + Expect(k8sClient.Create(ctx, argoCD)).To(Succeed()) + Eventually(argoCD, "5m", "5s").Should(argocdFixture.BeAvailable()) + + By("verifying that there exist no clusterrolebindings that point to this namespace-scoped namespace") + Consistently(func() bool { + var clusterRoleBindings rbacv1.ClusterRoleBindingList + if err := k8sClient.List(ctx, &clusterRoleBindings); err != nil { + GinkgoWriter.Println(err) + return false + } + for _, crb := range clusterRoleBindings.Items { + for _, subject := range crb.Subjects { + if subject.Namespace == argoCD.Namespace { + GinkgoWriter.Println("detected a CRB that pointed to namespace scoped ArgoCD instance. This shouldn't happen:", crb.Name) + return false + } + } + } + return true + + }).Should(BeTrue()) + + By("verifying Role is not created in sourceNamespace, since the namespace is not cluster-scoped") + Consistently(&rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s_%s", "argocd", sourceNamespace.Name), + Namespace: sourceNamespace.Name, + }, + }, "30s", "5s").Should(k8sFixture.NotExistByName()) + + By("verifying RoleBinding is not created") + Consistently(&rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s_%s", "argocd", sourceNamespace.Name), + Namespace: sourceNamespace.Name, + }, + }, "30s", "5s").Should(k8sFixture.NotExistByName()) + + By("verifying there exist no rolebindings in source namespace that point to argocd namespace") + var roleBindingList rbacv1.RoleBindingList + Expect(k8sClient.List(ctx, &roleBindingList, client.InNamespace(sourceNamespace.Name))).To(Succeed()) + for _, rb := range roleBindingList.Items { + for _, subject := range rb.Subjects { + if subject.Namespace == argoCD.Namespace { + Fail("There should exist no rolebindings that point to our argocd namespace: " + rb.Name) + } + } + } + + By("verifying namespace does not have label") + Consistently(&corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: sourceNamespace.Name, + }, + }, "30s", "3s").Should(k8sFixture.NotHaveLabelWithValue(common.ArgoCDManagedByClusterArgoCDLabel, argoNamespace.Name)) + + By("verifying application namespaces is not set on server") + serverDeployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-server", + Namespace: argoNamespace.Name, + }, + } + Eventually(serverDeployment, "30s", "3s").Should(k8sFixture.ExistByName()) + Consistently(serverDeployment, "30s", "3s").ShouldNot(deploymentFixture.HaveContainerCommandSubstring("--application-namespaces", 0)) + + appControllerStatefulSet := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-application-controller", + Namespace: argoNamespace.Name, + }, + } + Eventually(appControllerStatefulSet).Should(k8sFixture.ExistByName()) + Consistently(appControllerStatefulSet, "30s", "3s").ShouldNot(statefulsetFixture.HaveContainerCommandSubstring("--application-namespaces", 0)) + + }) + + It("Should validate namespace for existing resources", func() { + + argoNamespace, cleanupArgoNamespace = fixture.CreateRandomE2ETestNamespaceWithCleanupFunc() + sourceNamespace, cleanupSourceNamespace = fixture.CreateRandomE2ETestNamespaceWithCleanupFunc() + + By("creating role/rolebinding in source namespace") + roleName := fmt.Sprintf("%s_%s", "argocd", sourceNamespace.Name) + Expect(k8sClient.Create(ctx, &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: roleName, + Namespace: sourceNamespace.Name, + }, + Rules: []rbacv1.PolicyRule{ + { + Verbs: []string{"get", "list"}, + APIGroups: []string{""}, + Resources: []string{"configmaps"}, + }, + }, + })).To(Succeed()) + + roleBindingName := fmt.Sprintf("%s_%s", "argocd", sourceNamespace.Name) + + Expect(k8sClient.Create(ctx, &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: roleBindingName, + Namespace: sourceNamespace.Name, + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: rbacv1.GroupName, + Kind: "Role", + Name: roleName, + }, + Subjects: []rbacv1.Subject{ + { + Kind: rbacv1.ServiceAccountKind, + Name: "argocd-application-controller", + Namespace: argoNamespace.Name, + }, + }, + })).To(Succeed()) + + argoCD := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd", + Namespace: argoNamespace.Name, + }, + Spec: argov1beta1api.ArgoCDSpec{ + SourceNamespaces: []string{sourceNamespace.Name}, + }, + } + + Expect(k8sClient.Create(ctx, argoCD)).To(Succeed()) + Eventually(argoCD, "5m", "5s").Should(argocdFixture.BeAvailable()) + + By("adding label to source namespace") + namespaceFixture.Update(sourceNamespace, func(ns *corev1.Namespace) { + if ns.Labels == nil { + ns.Labels = map[string]string{} + } + ns.Labels[common.ArgoCDManagedByClusterArgoCDLabel] = argoNamespace.Name + }) + + Eventually(&rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: roleName, + Namespace: sourceNamespace.Name, + }, + }, "30s", "3s").Should(k8sFixture.NotExistByName()) + + Eventually(&rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: roleBindingName, + Namespace: sourceNamespace.Name, + }, + }, "30s", "3s").Should(k8sFixture.NotExistByName()) + + Eventually(&corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: sourceNamespace.Name, + }, + }, "30s", "3s").Should(k8sFixture.NotHaveLabelWithValue(common.ArgoCDManagedByClusterArgoCDLabel, argoNamespace.Name)) + + serverDeployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-server", + Namespace: argoNamespace.Name, + }, + } + Eventually(serverDeployment, "30s", "3s").Should(k8sFixture.ExistByName()) + Consistently(serverDeployment, "30s", "3s").ShouldNot(deploymentFixture.HaveContainerCommandSubstring("--application-namespaces", 0)) + + appControllerStatefulSet := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-application-controller", + Namespace: argoNamespace.Name, + }, + } + Eventually(appControllerStatefulSet).Should(k8sFixture.ExistByName()) + Consistently(appControllerStatefulSet, "30s", "3s").ShouldNot(statefulsetFixture.HaveContainerCommandSubstring("--application-namespaces", 0)) + + }) + }) +})