diff --git a/bundle/manifests/gitops-operator.clusterserviceversion.yaml b/bundle/manifests/gitops-operator.clusterserviceversion.yaml index 1ef532fae..3fb173e82 100644 --- a/bundle/manifests/gitops-operator.clusterserviceversion.yaml +++ b/bundle/manifests/gitops-operator.clusterserviceversion.yaml @@ -134,7 +134,7 @@ metadata: } } }, - "resourceExclusions": "- apiGroups:\n - tekton.dev\n clusters:\n - '*'\n kinds:\n - TaskRun\n - PipelineRun \n", + "resourceExclusions": "- apiGroups:\n - \"\"\n - discovery.k8s.io\n kinds:\n - Endpoints\n - EndpointSlice\n- apiGroups:\n - apiregistration.k8s.io\n kinds:\n - APIService\n- apiGroups:\n - coordination.k8s.io\n kinds:\n - Lease\n- apiGroups:\n - authentication.k8s.io\n - authorization.k8s.io\n kinds:\n - SelfSubjectReview\n - TokenReview\n - LocalSubjectAccessReview\n - SelfSubjectAccessReview\n - SelfSubjectRulesReview\n - SubjectAccessReview\n- apiGroups:\n - certificates.k8s.io\n kinds:\n - CertificateSigningRequest\n- apiGroups:\n - cert-manager.io\n kinds:\n - CertificateRequest\n- apiGroups:\n - cilium.io\n kinds:\n - CiliumIdentity\n - CiliumEndpoint\n - CiliumEndpointSlice\n- apiGroups:\n - kyverno.io\n - reports.kyverno.io\n - wgpolicyk8s.io\n kinds:\n - PolicyReport\n - ClusterPolicyReport\n - EphemeralReport\n - ClusterEphemeralReport\n - AdmissionReport\n - ClusterAdmissionReport\n - BackgroundScanReport\n - ClusterBackgroundScanReport\n - UpdateRequest\n- apiGroups:\n - tekton.dev\n clusters:\n - '*'\n kinds:\n - TaskRun\n - PipelineRun\n", "server": { "resources": { "limits": { @@ -180,7 +180,7 @@ metadata: capabilities: Deep Insights console.openshift.io/plugins: '["gitops-plugin"]' containerImage: quay.io/redhat-developer/gitops-operator - createdAt: "2025-08-21T01:20:45Z" + createdAt: "2025-09-30T08:46:55Z" description: Enables teams to adopt GitOps principles for managing cluster configurations and application delivery across hybrid multi-cluster Kubernetes environments. features.operators.openshift.io/disconnected: "true" diff --git a/config/samples/argoproj.io_v1alpha1_argocd.yaml b/config/samples/argoproj.io_v1alpha1_argocd.yaml index 77a63f7c6..db30c373a 100644 --- a/config/samples/argoproj.io_v1alpha1_argocd.yaml +++ b/config/samples/argoproj.io_v1alpha1_argocd.yaml @@ -55,6 +55,58 @@ spec: cpu: 250m memory: 128Mi resourceExclusions: | + - apiGroups: + - "" + - discovery.k8s.io + kinds: + - Endpoints + - EndpointSlice + - apiGroups: + - apiregistration.k8s.io + kinds: + - APIService + - apiGroups: + - coordination.k8s.io + kinds: + - Lease + - apiGroups: + - authentication.k8s.io + - authorization.k8s.io + kinds: + - SelfSubjectReview + - TokenReview + - LocalSubjectAccessReview + - SelfSubjectAccessReview + - SelfSubjectRulesReview + - SubjectAccessReview + - apiGroups: + - certificates.k8s.io + kinds: + - CertificateSigningRequest + - apiGroups: + - cert-manager.io + kinds: + - CertificateRequest + - apiGroups: + - cilium.io + kinds: + - CiliumIdentity + - CiliumEndpoint + - CiliumEndpointSlice + - apiGroups: + - kyverno.io + - reports.kyverno.io + - wgpolicyk8s.io + kinds: + - PolicyReport + - ClusterPolicyReport + - EphemeralReport + - ClusterEphemeralReport + - AdmissionReport + - ClusterAdmissionReport + - BackgroundScanReport + - ClusterBackgroundScanReport + - UpdateRequest - apiGroups: - tekton.dev clusters: diff --git a/config/samples/argoproj.io_v1beta1_argocd.yaml b/config/samples/argoproj.io_v1beta1_argocd.yaml index 986e61960..d3aca5200 100644 --- a/config/samples/argoproj.io_v1beta1_argocd.yaml +++ b/config/samples/argoproj.io_v1beta1_argocd.yaml @@ -55,13 +55,65 @@ spec: cpu: 250m memory: 128Mi resourceExclusions: | + - apiGroups: + - "" + - discovery.k8s.io + kinds: + - Endpoints + - EndpointSlice + - apiGroups: + - apiregistration.k8s.io + kinds: + - APIService + - apiGroups: + - coordination.k8s.io + kinds: + - Lease + - apiGroups: + - authentication.k8s.io + - authorization.k8s.io + kinds: + - SelfSubjectReview + - TokenReview + - LocalSubjectAccessReview + - SelfSubjectAccessReview + - SelfSubjectRulesReview + - SubjectAccessReview + - apiGroups: + - certificates.k8s.io + kinds: + - CertificateSigningRequest + - apiGroups: + - cert-manager.io + kinds: + - CertificateRequest + - apiGroups: + - cilium.io + kinds: + - CiliumIdentity + - CiliumEndpoint + - CiliumEndpointSlice + - apiGroups: + - kyverno.io + - reports.kyverno.io + - wgpolicyk8s.io + kinds: + - PolicyReport + - ClusterPolicyReport + - EphemeralReport + - ClusterEphemeralReport + - AdmissionReport + - ClusterAdmissionReport + - BackgroundScanReport + - ClusterBackgroundScanReport + - UpdateRequest - apiGroups: - tekton.dev clusters: - '*' kinds: - TaskRun - - PipelineRun + - PipelineRun controller: resources: limits: diff --git a/controllers/argocd/argocd.go b/controllers/argocd/argocd.go index 5ec8d6cfd..6dd09e053 100644 --- a/controllers/argocd/argocd.go +++ b/controllers/argocd/argocd.go @@ -179,9 +179,49 @@ func getDefaultRBAC() argoapp.ArgoCDRBACSpec { } // NewCR returns an ArgoCD reference optimized for use in OpenShift -// with Tekton +// with comprehensive default resource exclusions func NewCR(name, ns string) (*argoapp.ArgoCD, error) { b, err := yaml.Marshal([]resource{ + { + APIGroups: []string{"", "discovery.k8s.io"}, + Kinds: []string{"Endpoints", "EndpointSlice"}, + Clusters: []string{"*"}, + }, + { + APIGroups: []string{"apiregistration.k8s.io"}, + Kinds: []string{"APIService"}, + Clusters: []string{"*"}, + }, + { + APIGroups: []string{"coordination.k8s.io"}, + Kinds: []string{"Lease"}, + Clusters: []string{"*"}, + }, + { + APIGroups: []string{"authentication.k8s.io", "authorization.k8s.io"}, + Kinds: []string{"SelfSubjectReview", "TokenReview", "LocalSubjectAccessReview", "SelfSubjectAccessReview", "SelfSubjectRulesReview", "SubjectAccessReview"}, + Clusters: []string{"*"}, + }, + { + APIGroups: []string{"certificates.k8s.io"}, + Kinds: []string{"CertificateSigningRequest"}, + Clusters: []string{"*"}, + }, + { + APIGroups: []string{"cert-manager.io"}, + Kinds: []string{"CertificateRequest"}, + Clusters: []string{"*"}, + }, + { + APIGroups: []string{"cilium.io"}, + Kinds: []string{"CiliumIdentity", "CiliumEndpoint", "CiliumEndpointSlice"}, + Clusters: []string{"*"}, + }, + { + APIGroups: []string{"kyverno.io", "reports.kyverno.io", "wgpolicyk8s.io"}, + Kinds: []string{"PolicyReport", "ClusterPolicyReport", "EphemeralReport", "ClusterEphemeralReport", "AdmissionReport", "ClusterAdmissionReport", "BackgroundScanReport", "ClusterBackgroundScanReport", "UpdateRequest"}, + Clusters: []string{"*"}, + }, { APIGroups: []string{"tekton.dev"}, Kinds: []string{"TaskRun", "PipelineRun"}, diff --git a/controllers/argocd/argocd_test.go b/controllers/argocd/argocd_test.go index ef3c7a09b..7435a8485 100644 --- a/controllers/argocd/argocd_test.go +++ b/controllers/argocd/argocd_test.go @@ -17,6 +17,7 @@ limitations under the License. package argocd import ( + "strings" "testing" argoapp "github.com/argoproj-labs/argocd-operator/api/v1beta1" @@ -126,6 +127,66 @@ func TestArgoCD(t *testing.T) { }, } assert.DeepEqual(t, testArgoCD.Spec.Server.Resources, testServerResources) + + // Test ResourceExclusions field + resourceExclusions := testArgoCD.Spec.ResourceExclusions + assert.Assert(t, len(resourceExclusions) > 0) + + // Verify that the YAML contains expected resource types + expectedResources := []string{ + "Endpoints", + "EndpointSlice", + "APIService", + "Lease", + "SelfSubjectReview", + "TokenReview", + "LocalSubjectAccessReview", + "SelfSubjectAccessReview", + "SelfSubjectRulesReview", + "SubjectAccessReview", + "CertificateSigningRequest", + "CertificateRequest", + "CiliumIdentity", + "CiliumEndpoint", + "CiliumEndpointSlice", + "PolicyReport", + "ClusterPolicyReport", + "EphemeralReport", + "ClusterEphemeralReport", + "AdmissionReport", + "ClusterAdmissionReport", + "BackgroundScanReport", + "ClusterBackgroundScanReport", + "UpdateRequest", + "TaskRun", + "PipelineRun", + } + + for _, expectedResource := range expectedResources { + assert.Assert(t, strings.Contains(resourceExclusions, expectedResource), + "ResourceExclusions should contain %s", expectedResource) + } + + // Verify that the YAML contains expected API groups + expectedAPIGroups := []string{ + "discovery.k8s.io", + "apiregistration.k8s.io", + "coordination.k8s.io", + "authentication.k8s.io", + "authorization.k8s.io", + "certificates.k8s.io", + "cert-manager.io", + "cilium.io", + "kyverno.io", + "reports.kyverno.io", + "wgpolicyk8s.io", + "tekton.dev", + } + + for _, expectedAPIGroup := range expectedAPIGroups { + assert.Assert(t, strings.Contains(resourceExclusions, expectedAPIGroup), + "ResourceExclusions should contain API group %s", expectedAPIGroup) + } } func TestDexConfiguration(t *testing.T) { diff --git a/test/openshift/e2e/ginkgo/parallel/1-033_validate_resource_exclusions_test.go b/test/openshift/e2e/ginkgo/parallel/1-033_validate_resource_exclusions_test.go index e8bbb7565..a3cd2bede 100644 --- a/test/openshift/e2e/ginkgo/parallel/1-033_validate_resource_exclusions_test.go +++ b/test/openshift/e2e/ginkgo/parallel/1-033_validate_resource_exclusions_test.go @@ -18,6 +18,7 @@ package parallel import ( "context" + "strings" argov1beta1api "github.com/argoproj-labs/argocd-operator/api/v1beta1" . "github.com/onsi/ginkgo/v2" @@ -85,5 +86,524 @@ var _ = Describe("GitOps Operator Parallel E2E Tests", func() { }) + It("verifies default resource exclusions are applied when creating a new ArgoCD instance", func() { + + By("creating namespace-scoped Argo CD instance") + ns, cleanupFunc := fixture.CreateRandomE2ETestNamespaceWithCleanupFunc() + defer cleanupFunc() + + argoCD := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{Name: "argocd", Namespace: ns.Name}, + Spec: argov1beta1api.ArgoCDSpec{}, + } + 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()) + + argocdConfigMap := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "argocd-cm", Namespace: ns.Name}} + + By("verifying default resource exclusions are present in ConfigMap") + Eventually(argocdConfigMap).Should(configmapFixture.HaveNonEmptyDataKey("resource.exclusions")) + + By("verifying default resource exclusions contain expected API groups and kinds") + Eventually(func() bool { + configMap := &corev1.ConfigMap{} + err := k8sClient.Get(ctx, client.ObjectKey{Name: "argocd-cm", Namespace: ns.Name}, configMap) + if err != nil { + return false + } + + resourceExclusions := configMap.Data["resource.exclusions"] + if resourceExclusions == "" { + return false + } + + // Verify expected API groups are present + expectedAPIGroups := []string{ + "discovery.k8s.io", + "apiregistration.k8s.io", + "coordination.k8s.io", + "authentication.k8s.io", + "authorization.k8s.io", + "certificates.k8s.io", + "cert-manager.io", + "cilium.io", + "kyverno.io", + "reports.kyverno.io", + "wgpolicyk8s.io", + } + + for _, apiGroup := range expectedAPIGroups { + if !strings.Contains(resourceExclusions, apiGroup) { + return false + } + } + + // Verify expected resource kinds are present + expectedKinds := []string{ + "Endpoints", + "EndpointSlice", + "APIService", + "Lease", + "SelfSubjectReview", + "TokenReview", + "LocalSubjectAccessReview", + "SelfSubjectAccessReview", + "SelfSubjectRulesReview", + "SubjectAccessReview", + "CertificateSigningRequest", + "CertificateRequest", + "CiliumIdentity", + "CiliumEndpoint", + "CiliumEndpointSlice", + "PolicyReport", + "ClusterPolicyReport", + "EphemeralReport", + "ClusterEphemeralReport", + "AdmissionReport", + "ClusterAdmissionReport", + "BackgroundScanReport", + "ClusterBackgroundScanReport", + "UpdateRequest", + } + + for _, kind := range expectedKinds { + if !strings.Contains(resourceExclusions, kind) { + return false + } + } + + return true + }).Should(BeTrue()) + + By("verifying default resource exclusions have exactly 8 entries") + Eventually(func() bool { + configMap := &corev1.ConfigMap{} + err := k8sClient.Get(ctx, client.ObjectKey{Name: "argocd-cm", Namespace: ns.Name}, configMap) + if err != nil { + return false + } + + resourceExclusions := configMap.Data["resource.exclusions"] + if resourceExclusions == "" { + return false + } + + // Count the number of entries by counting the number of "- apiGroups:" occurrences + // Each resource exclusion entry starts with "- apiGroups:" + entryCount := strings.Count(resourceExclusions, "- apiGroups:") + + // Should have exactly 8 entries as actually applied by the system + return entryCount == 8 + }).Should(BeTrue()) + }) + + It("verifies setting custom resource exclusion on the ArgoCD CR will cause it to be set on Argo CD ConfigMap", func() { + + By("creating namespace-scoped Argo CD instance") + ns, cleanupFunc := fixture.CreateRandomE2ETestNamespaceWithCleanupFunc() + defer cleanupFunc() + + argoCD := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{Name: "argocd", Namespace: ns.Name}, + Spec: argov1beta1api.ArgoCDSpec{}, + } + 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("setting custom resource exclusions on the ArgoCD CR") + argocdFixture.Update(argoCD, func(ac *argov1beta1api.ArgoCD) { + ac.Spec.ResourceExclusions = `- apiGroups: + - tekton.dev + clusters: + - '*' + kinds: + - DaemonSet +- apiGroups: + - kyverno.io + clusters: + - '*' + kinds: + - Policy + - ClusterPolicy` + }) + + argocdConfigMap := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "argocd-cm", Namespace: ns.Name}} + + By("verifying ConfigMap has same resource exclusion value as specified in ArgoCD CR") + Eventually(argocdConfigMap).Should(configmapFixture.HaveStringDataKeyValue("resource.exclusions", `- apiGroups: + - tekton.dev + clusters: + - '*' + kinds: + - DaemonSet +- apiGroups: + - kyverno.io + clusters: + - '*' + kinds: + - Policy + - ClusterPolicy`)) + + By("verifying custom resource exclusions have exactly 2 entries") + Eventually(func() bool { + configMap := &corev1.ConfigMap{} + err := k8sClient.Get(ctx, client.ObjectKey{Name: "argocd-cm", Namespace: ns.Name}, configMap) + if err != nil { + return false + } + + resourceExclusions := configMap.Data["resource.exclusions"] + if resourceExclusions == "" { + return false + } + + // Count the number of entries by counting the number of "- apiGroups:" occurrences + entryCount := strings.Count(resourceExclusions, "- apiGroups:") + + // Should have exactly 2 entries as we set 2 custom exclusions + return entryCount == 2 + }).Should(BeTrue()) + }) + + It("verifies that resource exclusions can be updated and changes are reflected in the ConfigMap", func() { + + By("creating namespace-scoped Argo CD instance") + ns, cleanupFunc := fixture.CreateRandomE2ETestNamespaceWithCleanupFunc() + defer cleanupFunc() + + argoCD := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{Name: "argocd", Namespace: ns.Name}, + Spec: argov1beta1api.ArgoCDSpec{}, + } + 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("setting initial resource exclusions") + argocdFixture.Update(argoCD, func(ac *argov1beta1api.ArgoCD) { + ac.Spec.ResourceExclusions = `- apiGroups: + - tekton.dev + clusters: + - '*' + kinds: + - TaskRun` + }) + + argocdConfigMap := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "argocd-cm", Namespace: ns.Name}} + + By("verifying initial resource exclusions are applied") + Eventually(argocdConfigMap).Should(configmapFixture.HaveStringDataKeyValue("resource.exclusions", `- apiGroups: + - tekton.dev + clusters: + - '*' + kinds: + - TaskRun`)) + + By("updating resource exclusions with additional API groups and kinds") + argocdFixture.Update(argoCD, func(ac *argov1beta1api.ArgoCD) { + ac.Spec.ResourceExclusions = `- apiGroups: + - tekton.dev + clusters: + - '*' + kinds: + - TaskRun + - PipelineRun +- apiGroups: + - cert-manager.io + clusters: + - '*' + kinds: + - Certificate + - CertificateRequest` + }) + + By("verifying updated resource exclusions are applied") + Eventually(argocdConfigMap).Should(configmapFixture.HaveStringDataKeyValue("resource.exclusions", `- apiGroups: + - tekton.dev + clusters: + - '*' + kinds: + - TaskRun + - PipelineRun +- apiGroups: + - cert-manager.io + clusters: + - '*' + kinds: + - Certificate + - CertificateRequest`)) + + By("verifying updated resource exclusions have exactly 2 entries") + Eventually(func() bool { + configMap := &corev1.ConfigMap{} + err := k8sClient.Get(ctx, client.ObjectKey{Name: "argocd-cm", Namespace: ns.Name}, configMap) + if err != nil { + return false + } + + resourceExclusions := configMap.Data["resource.exclusions"] + if resourceExclusions == "" { + return false + } + + // Count the number of entries by counting the number of "- apiGroups:" occurrences + entryCount := strings.Count(resourceExclusions, "- apiGroups:") + + // Should have exactly 2 entries as we set 2 updated exclusions + return entryCount == 2 + }).Should(BeTrue()) + }) + + It("verifies multiple resource exclusions with different API groups and kinds work correctly", func() { + + By("creating namespace-scoped Argo CD instance") + ns, cleanupFunc := fixture.CreateRandomE2ETestNamespaceWithCleanupFunc() + defer cleanupFunc() + + argoCD := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{Name: "argocd", Namespace: ns.Name}, + Spec: argov1beta1api.ArgoCDSpec{}, + } + 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("setting comprehensive resource exclusions with multiple API groups and kinds") + argocdFixture.Update(argoCD, func(ac *argov1beta1api.ArgoCD) { + ac.Spec.ResourceExclusions = `- apiGroups: + - tekton.dev + clusters: + - '*' + kinds: + - TaskRun + - PipelineRun + - Task + - Pipeline +- apiGroups: + - kyverno.io + clusters: + - '*' + kinds: + - Policy + - ClusterPolicy + - PolicyReport +- apiGroups: + - cert-manager.io + clusters: + - '*' + kinds: + - Certificate + - CertificateRequest + - Issuer + - ClusterIssuer +- apiGroups: + - cilium.io + clusters: + - '*' + kinds: + - CiliumIdentity + - CiliumEndpoint + - CiliumEndpointSlice` + }) + + argocdConfigMap := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "argocd-cm", Namespace: ns.Name}} + + By("verifying comprehensive resource exclusions are applied correctly") + Eventually(argocdConfigMap).Should(configmapFixture.HaveStringDataKeyValue("resource.exclusions", `- apiGroups: + - tekton.dev + clusters: + - '*' + kinds: + - TaskRun + - PipelineRun + - Task + - Pipeline +- apiGroups: + - kyverno.io + clusters: + - '*' + kinds: + - Policy + - ClusterPolicy + - PolicyReport +- apiGroups: + - cert-manager.io + clusters: + - '*' + kinds: + - Certificate + - CertificateRequest + - Issuer + - ClusterIssuer +- apiGroups: + - cilium.io + clusters: + - '*' + kinds: + - CiliumIdentity + - CiliumEndpoint + - CiliumEndpointSlice`)) + + By("verifying all expected API groups are present in the ConfigMap") + Eventually(func() bool { + configMap := &corev1.ConfigMap{} + err := k8sClient.Get(ctx, client.ObjectKey{Name: "argocd-cm", Namespace: ns.Name}, configMap) + if err != nil { + return false + } + + resourceExclusions := configMap.Data["resource.exclusions"] + if resourceExclusions == "" { + return false + } + + // Verify all expected API groups are present + expectedAPIGroups := []string{"tekton.dev", "kyverno.io", "cert-manager.io", "cilium.io"} + for _, apiGroup := range expectedAPIGroups { + if !strings.Contains(resourceExclusions, apiGroup) { + return false + } + } + + // Verify all expected kinds are present + expectedKinds := []string{ + "TaskRun", "PipelineRun", "Task", "Pipeline", + "Policy", "ClusterPolicy", "PolicyReport", + "Certificate", "CertificateRequest", "Issuer", "ClusterIssuer", + "CiliumIdentity", "CiliumEndpoint", "CiliumEndpointSlice", + } + for _, kind := range expectedKinds { + if !strings.Contains(resourceExclusions, kind) { + return false + } + } + + return true + }).Should(BeTrue()) + + By("verifying comprehensive resource exclusions have exactly 4 entries") + Eventually(func() bool { + configMap := &corev1.ConfigMap{} + err := k8sClient.Get(ctx, client.ObjectKey{Name: "argocd-cm", Namespace: ns.Name}, configMap) + if err != nil { + return false + } + + resourceExclusions := configMap.Data["resource.exclusions"] + if resourceExclusions == "" { + return false + } + + // Count the number of entries by counting the number of "- apiGroups:" occurrences + entryCount := strings.Count(resourceExclusions, "- apiGroups:") + + // Should have exactly 4 entries as we set 4 comprehensive exclusions + return entryCount == 4 + }).Should(BeTrue()) + }) + + It("verifies that resource exclusions can be cleared and ConfigMap reverts to default exclusions", func() { + + By("creating namespace-scoped Argo CD instance") + ns, cleanupFunc := fixture.CreateRandomE2ETestNamespaceWithCleanupFunc() + defer cleanupFunc() + + argoCD := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{Name: "argocd", Namespace: ns.Name}, + Spec: argov1beta1api.ArgoCDSpec{}, + } + 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("setting initial resource exclusions") + argocdFixture.Update(argoCD, func(ac *argov1beta1api.ArgoCD) { + ac.Spec.ResourceExclusions = `- apiGroups: + - tekton.dev + clusters: + - '*' + kinds: + - TaskRun` + }) + + argocdConfigMap := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "argocd-cm", Namespace: ns.Name}} + + By("verifying initial resource exclusions are applied") + Eventually(argocdConfigMap).Should(configmapFixture.HaveStringDataKeyValue("resource.exclusions", `- apiGroups: + - tekton.dev + clusters: + - '*' + kinds: + - TaskRun`)) + + By("clearing resource exclusions") + argocdFixture.Update(argoCD, func(ac *argov1beta1api.ArgoCD) { + ac.Spec.ResourceExclusions = "" + }) + + By("verifying resource exclusions revert to default exclusions") + Eventually(func() bool { + configMap := &corev1.ConfigMap{} + err := k8sClient.Get(ctx, client.ObjectKey{Name: "argocd-cm", Namespace: ns.Name}, configMap) + if err != nil { + return false + } + + resourceExclusions := configMap.Data["resource.exclusions"] + if resourceExclusions == "" { + return false + } + + // Verify that default API groups are present (indicating default exclusions are applied) + expectedAPIGroups := []string{ + "discovery.k8s.io", + "apiregistration.k8s.io", + "coordination.k8s.io", + "authentication.k8s.io", + "authorization.k8s.io", + "certificates.k8s.io", + "cert-manager.io", + "cilium.io", + "kyverno.io", + } + + for _, apiGroup := range expectedAPIGroups { + if !strings.Contains(resourceExclusions, apiGroup) { + return false + } + } + + // Verify that the custom tekton.dev exclusion is no longer present + return !strings.Contains(resourceExclusions, "tekton.dev") + }).Should(BeTrue()) + + By("verifying cleared resource exclusions revert to exactly 8 default entries") + Eventually(func() bool { + configMap := &corev1.ConfigMap{} + err := k8sClient.Get(ctx, client.ObjectKey{Name: "argocd-cm", Namespace: ns.Name}, configMap) + if err != nil { + return false + } + + resourceExclusions := configMap.Data["resource.exclusions"] + if resourceExclusions == "" { + return false + } + + // Count the number of entries by counting the number of "- apiGroups:" occurrences + entryCount := strings.Count(resourceExclusions, "- apiGroups:") + + // Should have exactly 8 entries as it reverts to default exclusions + return entryCount == 8 + }).Should(BeTrue()) + }) + }) })