From 768902b3147b9f6e320bd4eaf70fd4fb4f67b0c6 Mon Sep 17 00:00:00 2001 From: Anand Kumar Singh Date: Wed, 25 Feb 2026 12:49:52 +0530 Subject: [PATCH 1/2] downstream networkpolicy Signed-off-by: Anand Kumar Singh --- bundle/manifests/argoproj.io_argocds.yaml | 72 +++- ...gitops-operator.clusterserviceversion.yaml | 2 +- config/crd/bases/argoproj.io_argocds.yaml | 72 +++- .../1-124_validate_networkpolicies_test.go | 377 ++++++++++++++++++ 4 files changed, 490 insertions(+), 33 deletions(-) create mode 100644 test/openshift/e2e/ginkgo/parallel/1-124_validate_networkpolicies_test.go diff --git a/bundle/manifests/argoproj.io_argocds.yaml b/bundle/manifests/argoproj.io_argocds.yaml index cc781e852..a2fb8f2d6 100644 --- a/bundle/manifests/argoproj.io_argocds.yaml +++ b/bundle/manifests/argoproj.io_argocds.yaml @@ -1900,6 +1900,18 @@ spec: - name type: object type: array + networkPolicy: + description: NetworkPolicy controls whether the operator should create + NetworkPolicy resources for this Argo CD instance. + properties: + enabled: + default: true + description: |- + Enabled defines whether NetworkPolicy resources should be created for this Argo CD instance. + When enabled, the operator will reconcile NetworkPolicies for Argo CD components. + When disabled, the operator will remove any previously-created NetworkPolicies. + type: boolean + type: object nodePlacement: description: NodePlacement defines NodeSelectors and Taints for Argo CD workloads @@ -2221,15 +2233,20 @@ spec: ArgoCD. properties: enabled: - description: Enabled will toggle Prometheus support globally for - ArgoCD. + description: |- + Enabled will toggle Prometheus support globally for ArgoCD. + When set to true, ServiceMonitors and PrometheusRules will be created for Argo CD metrics. + The Prometheus CR, Route, and Ingress are deprecated and will no longer be created. type: boolean host: - description: Host is the hostname to use for Ingress/Route resources. + description: |- + Host is the hostname to use for Ingress/Route resources. + Deprecated: This field is no longer used and will be ignored. type: string ingress: - description: Ingress defines the desired state for an Ingress - for the Prometheus component. + description: |- + Ingress defines the desired state for an Ingress for the Prometheus component. + Deprecated: This field is no longer used and will be ignored. properties: annotations: additionalProperties: @@ -2281,8 +2298,9 @@ spec: - enabled type: object route: - description: Route defines the desired state for an OpenShift - Route for the Prometheus component. + description: |- + Route defines the desired state for an OpenShift Route for the Prometheus component. + Deprecated: This field is no longer used and will be ignored. properties: annotations: additionalProperties: @@ -2388,7 +2406,9 @@ spec: - enabled type: object size: - description: Size is the replica count for the Prometheus StatefulSet. + description: |- + Size is the replica count for the Prometheus StatefulSet. + Deprecated: This field is no longer used and will be ignored. format: int32 type: integer required: @@ -17981,6 +18001,18 @@ spec: - name type: object type: array + networkPolicy: + description: NetworkPolicy controls whether the operator should create + NetworkPolicy resources for this Argo CD instance. + properties: + enabled: + default: true + description: |- + Enabled defines whether NetworkPolicy resources are created for this Argo CD instance. + When enabled, the operator will reconcile NetworkPolicies for Argo CD components. + When disabled, the operator will remove any previously-created NetworkPolicies. + type: boolean + type: object nodePlacement: description: NodePlacement defines NodeSelectors and Taints for Argo CD workloads @@ -18302,15 +18334,20 @@ spec: ArgoCD. properties: enabled: - description: Enabled will toggle Prometheus support globally for - ArgoCD. + description: |- + Enabled will toggle Prometheus support globally for ArgoCD. + When set to true, ServiceMonitors and PrometheusRules will be created for Argo CD metrics. + The Prometheus CR, Route, and Ingress are deprecated and will no longer be created. type: boolean host: - description: Host is the hostname to use for Ingress/Route resources. + description: |- + Host is the hostname to use for Ingress/Route resources. + Deprecated: This field is no longer used and will be ignored. type: string ingress: - description: Ingress defines the desired state for an Ingress - for the Prometheus component. + description: |- + Ingress defines the desired state for an Ingress for the Prometheus component. + Deprecated: This field is no longer used and will be ignored. properties: annotations: additionalProperties: @@ -18362,8 +18399,9 @@ spec: - enabled type: object route: - description: Route defines the desired state for an OpenShift - Route for the Prometheus component. + description: |- + Route defines the desired state for an OpenShift Route for the Prometheus component. + Deprecated: This field is no longer used and will be ignored. properties: annotations: additionalProperties: @@ -18469,7 +18507,9 @@ spec: - enabled type: object size: - description: Size is the replica count for the Prometheus StatefulSet. + description: |- + Size is the replica count for the Prometheus StatefulSet. + Deprecated: This field is no longer used and will be ignored. format: int32 type: integer required: diff --git a/bundle/manifests/gitops-operator.clusterserviceversion.yaml b/bundle/manifests/gitops-operator.clusterserviceversion.yaml index 2163f06df..175efd0a6 100644 --- a/bundle/manifests/gitops-operator.clusterserviceversion.yaml +++ b/bundle/manifests/gitops-operator.clusterserviceversion.yaml @@ -180,7 +180,7 @@ metadata: capabilities: Deep Insights console.openshift.io/plugins: '["gitops-plugin"]' containerImage: quay.io/redhat-developer/gitops-operator - createdAt: "2026-02-18T10:47:28Z" + createdAt: "2026-02-25T05:56:01Z" 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/crd/bases/argoproj.io_argocds.yaml b/config/crd/bases/argoproj.io_argocds.yaml index 46c79dc39..7e451ac1b 100644 --- a/config/crd/bases/argoproj.io_argocds.yaml +++ b/config/crd/bases/argoproj.io_argocds.yaml @@ -1889,6 +1889,18 @@ spec: - name type: object type: array + networkPolicy: + description: NetworkPolicy controls whether the operator should create + NetworkPolicy resources for this Argo CD instance. + properties: + enabled: + default: true + description: |- + Enabled defines whether NetworkPolicy resources should be created for this Argo CD instance. + When enabled, the operator will reconcile NetworkPolicies for Argo CD components. + When disabled, the operator will remove any previously-created NetworkPolicies. + type: boolean + type: object nodePlacement: description: NodePlacement defines NodeSelectors and Taints for Argo CD workloads @@ -2210,15 +2222,20 @@ spec: ArgoCD. properties: enabled: - description: Enabled will toggle Prometheus support globally for - ArgoCD. + description: |- + Enabled will toggle Prometheus support globally for ArgoCD. + When set to true, ServiceMonitors and PrometheusRules will be created for Argo CD metrics. + The Prometheus CR, Route, and Ingress are deprecated and will no longer be created. type: boolean host: - description: Host is the hostname to use for Ingress/Route resources. + description: |- + Host is the hostname to use for Ingress/Route resources. + Deprecated: This field is no longer used and will be ignored. type: string ingress: - description: Ingress defines the desired state for an Ingress - for the Prometheus component. + description: |- + Ingress defines the desired state for an Ingress for the Prometheus component. + Deprecated: This field is no longer used and will be ignored. properties: annotations: additionalProperties: @@ -2270,8 +2287,9 @@ spec: - enabled type: object route: - description: Route defines the desired state for an OpenShift - Route for the Prometheus component. + description: |- + Route defines the desired state for an OpenShift Route for the Prometheus component. + Deprecated: This field is no longer used and will be ignored. properties: annotations: additionalProperties: @@ -2377,7 +2395,9 @@ spec: - enabled type: object size: - description: Size is the replica count for the Prometheus StatefulSet. + description: |- + Size is the replica count for the Prometheus StatefulSet. + Deprecated: This field is no longer used and will be ignored. format: int32 type: integer required: @@ -17970,6 +17990,18 @@ spec: - name type: object type: array + networkPolicy: + description: NetworkPolicy controls whether the operator should create + NetworkPolicy resources for this Argo CD instance. + properties: + enabled: + default: true + description: |- + Enabled defines whether NetworkPolicy resources are created for this Argo CD instance. + When enabled, the operator will reconcile NetworkPolicies for Argo CD components. + When disabled, the operator will remove any previously-created NetworkPolicies. + type: boolean + type: object nodePlacement: description: NodePlacement defines NodeSelectors and Taints for Argo CD workloads @@ -18291,15 +18323,20 @@ spec: ArgoCD. properties: enabled: - description: Enabled will toggle Prometheus support globally for - ArgoCD. + description: |- + Enabled will toggle Prometheus support globally for ArgoCD. + When set to true, ServiceMonitors and PrometheusRules will be created for Argo CD metrics. + The Prometheus CR, Route, and Ingress are deprecated and will no longer be created. type: boolean host: - description: Host is the hostname to use for Ingress/Route resources. + description: |- + Host is the hostname to use for Ingress/Route resources. + Deprecated: This field is no longer used and will be ignored. type: string ingress: - description: Ingress defines the desired state for an Ingress - for the Prometheus component. + description: |- + Ingress defines the desired state for an Ingress for the Prometheus component. + Deprecated: This field is no longer used and will be ignored. properties: annotations: additionalProperties: @@ -18351,8 +18388,9 @@ spec: - enabled type: object route: - description: Route defines the desired state for an OpenShift - Route for the Prometheus component. + description: |- + Route defines the desired state for an OpenShift Route for the Prometheus component. + Deprecated: This field is no longer used and will be ignored. properties: annotations: additionalProperties: @@ -18458,7 +18496,9 @@ spec: - enabled type: object size: - description: Size is the replica count for the Prometheus StatefulSet. + description: |- + Size is the replica count for the Prometheus StatefulSet. + Deprecated: This field is no longer used and will be ignored. format: int32 type: integer required: diff --git a/test/openshift/e2e/ginkgo/parallel/1-124_validate_networkpolicies_test.go b/test/openshift/e2e/ginkgo/parallel/1-124_validate_networkpolicies_test.go new file mode 100644 index 000000000..f24b9652f --- /dev/null +++ b/test/openshift/e2e/ginkgo/parallel/1-124_validate_networkpolicies_test.go @@ -0,0 +1,377 @@ +/* +Copyright 2026. + +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 parallel + +import ( + "context" + + 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" + 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" + + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var _ = Describe("GitOps Operator Parallel E2E Tests", func() { + + Context("1-124_validate_networkpolicies", func() { + + var ( + ctx context.Context + k8sClient client.Client + nsCleanup func() + ns *metav1.PartialObjectMetadata + ) + + BeforeEach(func() { + fixture.EnsureParallelCleanSlate() + k8sClient, _ = fixtureUtils.GetE2ETestKubeClient() + ctx = context.Background() + }) + + AfterEach(func() { + if nsCleanup != nil { + nsCleanup() + } + }) + + It("creates expected NetworkPolicies with correct pod selectors and ingress rules", func() { + + By("creating namespace-scoped Argo CD with notifications, dex, and applicationset enabled") + nsObj, cleanup := fixture.CreateRandomE2ETestNamespaceWithCleanupFunc() + nsCleanup = cleanup + + // Track namespace for debug (same pattern as other tests) + ns = &metav1.PartialObjectMetadata{} + ns.SetName(nsObj.Name) + + argocd := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-argocd", + Namespace: nsObj.Name, + }, + Spec: argov1beta1api.ArgoCDSpec{ + Server: argov1beta1api.ArgoCDServerSpec{ + Route: argov1beta1api.ArgoCDRouteSpec{ + Enabled: true, + }, + }, + Notifications: argov1beta1api.ArgoCDNotifications{ + Enabled: true, + }, + ApplicationSet: &argov1beta1api.ArgoCDApplicationSet{ + Enabled: ptr.To(true), + }, + SSO: &argov1beta1api.ArgoCDSSOSpec{ + Provider: argov1beta1api.SSOProviderTypeDex, + Dex: &argov1beta1api.ArgoCDDexSpec{ + Config: "test-config", + Volumes: []corev1.Volume{ + {Name: "empty-dir-volume", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}}, + }, + VolumeMounts: []corev1.VolumeMount{ + {Name: "empty-dir-volume", MountPath: "/etc/test"}, + }, + }, + }, + }, + } + Expect(k8sClient.Create(ctx, argocd)).To(Succeed()) + + By("waiting for Argo CD to become available") + Eventually(argocd, "6m", "5s").Should(argocdFixture.BeAvailable()) + + By("verifying NetworkPolicies exist") + expectedNPs := []string{ + "example-argocd-redis-network-policy", + "example-argocd-redis-ha-network-policy", + "example-argocd-notifications-controller-network-policy", + "example-argocd-dex-server-network-policy", + "example-argocd-applicationset-controller-network-policy", + "example-argocd-server-network-policy", + "example-argocd-application-controller-network-policy", + "example-argocd-repo-server-network-policy", + } + for _, npName := range expectedNPs { + Eventually(&networkingv1.NetworkPolicy{ObjectMeta: metav1.ObjectMeta{Name: npName, Namespace: nsObj.Name}}, "3m", "5s").Should(k8sFixture.ExistByName()) + } + + By("verifying repo-server NetworkPolicy ingress peers/ports") + repoNP := &networkingv1.NetworkPolicy{ObjectMeta: metav1.ObjectMeta{Name: "example-argocd-repo-server-network-policy", Namespace: nsObj.Name}} + Eventually(func() bool { + if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(repoNP), repoNP); err != nil { + return false + } + if repoNP.Spec.PodSelector.MatchLabels[common.ArgoCDKeyName] != "example-argocd-repo-server" { + return false + } + if len(repoNP.Spec.Ingress) != 2 { + return false + } + // Rule 1: internal components on 8081 + if len(repoNP.Spec.Ingress[0].Ports) != 1 || repoNP.Spec.Ingress[0].Ports[0].Port == nil || repoNP.Spec.Ingress[0].Ports[0].Port.IntVal != 8081 { + return false + } + // Expect applicationset fixed label to be present + foundAppSet := false + for _, peer := range repoNP.Spec.Ingress[0].From { + if peer.PodSelector != nil && peer.PodSelector.MatchLabels != nil && peer.PodSelector.MatchLabels[common.ArgoCDKeyName] == "argocd-applicationset-controller" { + foundAppSet = true + } + } + if !foundAppSet { + return false + } + // Rule 2: any namespace on 8084 (metrics) + if repoNP.Spec.Ingress[1].From == nil || len(repoNP.Spec.Ingress[1].From) != 1 || repoNP.Spec.Ingress[1].From[0].NamespaceSelector == nil { + return false + } + if len(repoNP.Spec.Ingress[1].Ports) != 1 || repoNP.Spec.Ingress[1].Ports[0].Port == nil || repoNP.Spec.Ingress[1].Ports[0].Port.IntVal != 8084 { + return false + } + return true + }, "3m", "5s").Should(BeTrue()) + + By("verifying dex-server NetworkPolicy ports") + dexNP := &networkingv1.NetworkPolicy{ObjectMeta: metav1.ObjectMeta{Name: "example-argocd-dex-server-network-policy", Namespace: nsObj.Name}} + Eventually(func() bool { + if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(dexNP), dexNP); err != nil { + return false + } + if dexNP.Spec.PodSelector.MatchLabels[common.ArgoCDKeyName] != "example-argocd-dex-server" { + return false + } + if len(dexNP.Spec.Ingress) != 2 { + return false + } + if len(dexNP.Spec.Ingress[0].Ports) != 2 { + return false + } + if dexNP.Spec.Ingress[0].Ports[0].Port == nil || dexNP.Spec.Ingress[0].Ports[0].Port.IntVal != common.ArgoCDDefaultDexHTTPPort { + return false + } + if dexNP.Spec.Ingress[0].Ports[1].Port == nil || dexNP.Spec.Ingress[0].Ports[1].Port.IntVal != common.ArgoCDDefaultDexGRPCPort { + return false + } + if len(dexNP.Spec.Ingress[1].Ports) != 1 || dexNP.Spec.Ingress[1].Ports[0].Port == nil || dexNP.Spec.Ingress[1].Ports[0].Port.IntVal != common.ArgoCDDefaultDexMetricsPort { + return false + } + return true + }, "3m", "5s").Should(BeTrue()) + + By("verifying notifications-controller NetworkPolicy ports") + notifNP := &networkingv1.NetworkPolicy{ObjectMeta: metav1.ObjectMeta{Name: "example-argocd-notifications-controller-network-policy", Namespace: nsObj.Name}} + Eventually(func() bool { + if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(notifNP), notifNP); err != nil { + return false + } + if notifNP.Spec.PodSelector.MatchLabels[common.ArgoCDKeyName] != "example-argocd-notifications-controller" { + return false + } + if len(notifNP.Spec.Ingress) != 1 { + return false + } + if notifNP.Spec.Ingress[0].From == nil || len(notifNP.Spec.Ingress[0].From) != 1 || notifNP.Spec.Ingress[0].From[0].NamespaceSelector == nil { + return false + } + if len(notifNP.Spec.Ingress[0].Ports) != 1 || notifNP.Spec.Ingress[0].Ports[0].Port == nil || notifNP.Spec.Ingress[0].Ports[0].Port.IntVal != 9001 { + return false + } + return true + }, "3m", "5s").Should(BeTrue()) + + By("verifying applicationset-controller NetworkPolicy ports") + appSetNP := &networkingv1.NetworkPolicy{ObjectMeta: metav1.ObjectMeta{Name: "example-argocd-applicationset-controller-network-policy", Namespace: nsObj.Name}} + expectedAppSetSelectorLabel := "argocd-applicationset-controller" + Eventually(func() bool { + if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(appSetNP), appSetNP); err != nil { + return false + } + if appSetNP.Spec.PodSelector.MatchLabels[common.ArgoCDKeyName] != expectedAppSetSelectorLabel { + return false + } + if len(appSetNP.Spec.Ingress) != 1 { + return false + } + if appSetNP.Spec.Ingress[0].From == nil || len(appSetNP.Spec.Ingress[0].From) != 1 || appSetNP.Spec.Ingress[0].From[0].NamespaceSelector == nil { + return false + } + if len(appSetNP.Spec.Ingress[0].Ports) != 2 { + return false + } + if appSetNP.Spec.Ingress[0].Ports[0].Port == nil || appSetNP.Spec.Ingress[0].Ports[0].Port.IntVal != 7000 { + return false + } + if appSetNP.Spec.Ingress[0].Ports[1].Port == nil || appSetNP.Spec.Ingress[0].Ports[1].Port.IntVal != 8080 { + return false + } + return true + }, "3m", "5s").Should(BeTrue()) + + By("verifying argocd-server NetworkPolicy shape") + serverNP := &networkingv1.NetworkPolicy{ObjectMeta: metav1.ObjectMeta{Name: "example-argocd-server-network-policy", Namespace: nsObj.Name}} + Eventually(func() bool { + if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(serverNP), serverNP); err != nil { + return false + } + if serverNP.Spec.PodSelector.MatchLabels[common.ArgoCDKeyName] != "example-argocd-server" { + return false + } + if len(serverNP.Spec.Ingress) != 1 { + return false + } + // empty ingress rule (allow all) + if len(serverNP.Spec.Ingress[0].From) != 0 || len(serverNP.Spec.Ingress[0].Ports) != 0 { + return false + } + return true + }, "3m", "5s").Should(BeTrue()) + + By("verifying application-controller NetworkPolicy ports") + appControllerNP := &networkingv1.NetworkPolicy{ObjectMeta: metav1.ObjectMeta{Name: "example-argocd-application-controller-network-policy", Namespace: nsObj.Name}} + Eventually(func() bool { + if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(appControllerNP), appControllerNP); err != nil { + return false + } + if appControllerNP.Spec.PodSelector.MatchLabels[common.ArgoCDKeyName] != "example-argocd-application-controller" { + return false + } + if len(appControllerNP.Spec.Ingress) != 1 { + return false + } + if appControllerNP.Spec.Ingress[0].From == nil || len(appControllerNP.Spec.Ingress[0].From) != 1 || appControllerNP.Spec.Ingress[0].From[0].NamespaceSelector == nil { + return false + } + if len(appControllerNP.Spec.Ingress[0].Ports) != 1 || appControllerNP.Spec.Ingress[0].Ports[0].Port == nil || appControllerNP.Spec.Ingress[0].Ports[0].Port.IntVal != 8082 { + return false + } + return true + }, "3m", "5s").Should(BeTrue()) + + By("verifying redis NetworkPolicy ports") + redisNP := &networkingv1.NetworkPolicy{ObjectMeta: metav1.ObjectMeta{Name: "example-argocd-redis-network-policy", Namespace: nsObj.Name}} + Eventually(func() bool { + if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(redisNP), redisNP); err != nil { + return false + } + if redisNP.Spec.PodSelector.MatchLabels[common.ArgoCDKeyName] != "example-argocd-redis" { + return false + } + if len(redisNP.Spec.Ingress) != 1 { + return false + } + if len(redisNP.Spec.Ingress[0].Ports) != 1 || redisNP.Spec.Ingress[0].Ports[0].Port == nil || redisNP.Spec.Ingress[0].Ports[0].Port.IntVal != 6379 { + return false + } + return true + }, "3m", "5s").Should(BeTrue()) + + By("verifying redis-ha NetworkPolicy ports") + redisHANP := &networkingv1.NetworkPolicy{ObjectMeta: metav1.ObjectMeta{Name: "example-argocd-redis-ha-network-policy", Namespace: nsObj.Name}} + Eventually(func() bool { + if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(redisHANP), redisHANP); err != nil { + return false + } + if redisHANP.Spec.PodSelector.MatchLabels[common.ArgoCDKeyName] != "example-argocd-redis-ha-haproxy" { + return false + } + if len(redisHANP.Spec.Ingress) != 1 { + return false + } + if len(redisHANP.Spec.Ingress[0].Ports) != 2 { + return false + } + if redisHANP.Spec.Ingress[0].Ports[0].Port == nil || redisHANP.Spec.Ingress[0].Ports[0].Port.IntVal != 6379 { + return false + } + if redisHANP.Spec.Ingress[0].Ports[1].Port == nil || redisHANP.Spec.Ingress[0].Ports[1].Port.IntVal != 26379 { + return false + } + return true + }, "3m", "5s").Should(BeTrue()) + }) + + It("reconciles drifted NetworkPolicy and respects disabling networkPolicy.enabled", func() { + By("creating namespace-scoped Argo CD instance") + nsObj, cleanup := fixture.CreateRandomE2ETestNamespaceWithCleanupFunc() + defer cleanup() + + argocd := &argov1beta1api.ArgoCD{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-argocd", + Namespace: nsObj.Name, + }, + Spec: argov1beta1api.ArgoCDSpec{ + Server: argov1beta1api.ArgoCDServerSpec{ + Route: argov1beta1api.ArgoCDRouteSpec{ + Enabled: true, + }, + }, + }, + } + Expect(k8sClient.Create(ctx, argocd)).To(Succeed()) + Eventually(argocd, "6m", "5s").Should(argocdFixture.BeAvailable()) + + By("waiting for repo-server NetworkPolicy to exist") + repoNP := &networkingv1.NetworkPolicy{ObjectMeta: metav1.ObjectMeta{Name: "example-argocd-repo-server-network-policy", Namespace: nsObj.Name}} + Eventually(repoNP, "3m", "5s").Should(k8sFixture.ExistByName()) + + By("introducing drift into the repo-server NetworkPolicy") + k8sFixture.Update(repoNP, func(obj client.Object) { + np := obj.(*networkingv1.NetworkPolicy) + if np.Spec.PodSelector.MatchLabels == nil { + np.Spec.PodSelector.MatchLabels = map[string]string{} + } + np.Spec.PodSelector.MatchLabels[common.ArgoCDKeyName] = "wrong" + }) + + By("verifying the operator reconciles NetworkPolicy drift") + Eventually(func() bool { + if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(repoNP), repoNP); err != nil { + return false + } + return repoNP.Spec.PodSelector.MatchLabels[common.ArgoCDKeyName] == "example-argocd-repo-server" + }, "3m", "5s").Should(BeTrue()) + + By("disabling networkPolicy.enabled") + disabled := false + argocdFixture.Update(argocd, func(ac *argov1beta1api.ArgoCD) { + ac.Spec.NetworkPolicy.Enabled = &disabled + }) + + By("verifying NetworkPolicies are deleted and not recreated while disabled") + coreNPs := []string{ + "example-argocd-repo-server-network-policy", + "example-argocd-server-network-policy", + "example-argocd-application-controller-network-policy", + } + for _, npName := range coreNPs { + Eventually(&networkingv1.NetworkPolicy{ObjectMeta: metav1.ObjectMeta{Name: npName, Namespace: nsObj.Name}}, "3m", "5s").Should(k8sFixture.NotExistByName()) + Consistently(&networkingv1.NetworkPolicy{ObjectMeta: metav1.ObjectMeta{Name: npName, Namespace: nsObj.Name}}, "30s", "5s").Should(k8sFixture.NotExistByName()) + } + }) + }) +}) From 8d8c6442749c6f74e775f4e6d87c736a40992c92 Mon Sep 17 00:00:00 2001 From: Anand Kumar Singh Date: Wed, 25 Feb 2026 21:11:31 +0530 Subject: [PATCH 2/2] add netwrok policy for principal component and add tests Signed-off-by: Anand Kumar Singh --- go.mod | 2 +- go.sum | 4 +- .../e2e/ginkgo/fixture/agent/fixture.go | 5 ++ ...51_validate_argocd_agent_principal_test.go | 73 +++++++++++++++++++ 4 files changed, 81 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index fb03f24ec..f45c16175 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.25.5 require ( github.com/argoproj-labs/argo-rollouts-manager v0.0.8-0.20260218104514-432c01ce417a - github.com/argoproj-labs/argocd-operator v0.17.0-rc1.0.20260225073619-a52ee52d3941 + github.com/argoproj-labs/argocd-operator v0.17.0-rc1.0.20260225142026-dabb25d60d94 github.com/argoproj/argo-cd/v3 v3.3.0 github.com/argoproj/gitops-engine v0.7.1-0.20251217140045-5baed5604d2d github.com/go-logr/logr v1.4.3 diff --git a/go.sum b/go.sum index f19a36392..b7dc4da1b 100644 --- a/go.sum +++ b/go.sum @@ -39,8 +39,8 @@ github.com/argoproj-labs/argo-rollouts-manager v0.0.8-0.20260218104514-432c01ce4 github.com/argoproj-labs/argo-rollouts-manager v0.0.8-0.20260218104514-432c01ce417a/go.mod h1:WPyZkNHZjir/OTt8mrRwcUZKe1euHrHPJsRv1Wp/F/0= github.com/argoproj-labs/argocd-image-updater v1.1.1 h1:7YDaR3WX2NMsDKp0wN7TRaRRHaVHQ94tSybi2P99MGk= github.com/argoproj-labs/argocd-image-updater v1.1.1/go.mod h1:gMHiNrGNwNSt4ljf0ykcnmNvXBk/NJ+Z17AnZVe7V7I= -github.com/argoproj-labs/argocd-operator v0.17.0-rc1.0.20260225073619-a52ee52d3941 h1:wkBZFBhSxIpaOfQOwQT44kgwkI/UC7IxM85GJ8w+nHI= -github.com/argoproj-labs/argocd-operator v0.17.0-rc1.0.20260225073619-a52ee52d3941/go.mod h1:3/Y9YWMU+DHC+onOQVXPAxrNkoBAGZD+UQui9BgJBjY= +github.com/argoproj-labs/argocd-operator v0.17.0-rc1.0.20260225142026-dabb25d60d94 h1:1HO3tG4kb0rJ2XD+pjHlSLbZMufBl/fNqGhkf9vkpho= +github.com/argoproj-labs/argocd-operator v0.17.0-rc1.0.20260225142026-dabb25d60d94/go.mod h1:3/Y9YWMU+DHC+onOQVXPAxrNkoBAGZD+UQui9BgJBjY= github.com/argoproj/argo-cd/v3 v3.3.0 h1:9UlruTd5cC/MyvorTXgAIblfZTy63MF5FYvvoAaUvwU= github.com/argoproj/argo-cd/v3 v3.3.0/go.mod h1:5VAfe0s/a4VY5GmAIFK76FtW6xn7zAcLmaw25bOL/2g= github.com/argoproj/gitops-engine v0.7.1-0.20251217140045-5baed5604d2d h1:iUJYrbSvpV9n8vyl1sBt1GceM60HhHfnHxuzcm5apDg= diff --git a/test/openshift/e2e/ginkgo/fixture/agent/fixture.go b/test/openshift/e2e/ginkgo/fixture/agent/fixture.go index cf7a3b534..60a417231 100644 --- a/test/openshift/e2e/ginkgo/fixture/agent/fixture.go +++ b/test/openshift/e2e/ginkgo/fixture/agent/fixture.go @@ -23,6 +23,7 @@ import ( 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/apimachinery/pkg/types" @@ -47,6 +48,7 @@ type PrincipalResources struct { ClusterRoleBinding *rbacv1.ClusterRoleBinding PrincipalDeployment *appsv1.Deployment PrincipalRoute *routev1.Route + PrincipalNetworkPolicy *networkingv1.NetworkPolicy ServicesToDelete []string } @@ -102,6 +104,7 @@ type VerifyExpectedResourcesExistParams struct { ClusterRoleBinding *rbacv1.ClusterRoleBinding PrincipalDeployment *appsv1.Deployment PrincipalRoute *routev1.Route + PrincipalNetworkPolicy *networkingv1.NetworkPolicy SecretNames AgentSecretNames ServiceNames []string DeploymentNames []string @@ -118,6 +121,7 @@ func VerifyResourcesDeleted(resources PrincipalResources) { Eventually(resources.ClusterRole).Should(k8sFixture.NotExistByName()) Eventually(resources.ClusterRoleBinding).Should(k8sFixture.NotExistByName()) Eventually(resources.PrincipalDeployment).Should(k8sFixture.NotExistByName()) + Eventually(resources.PrincipalNetworkPolicy).Should(k8sFixture.NotExistByName()) for _, serviceName := range resources.ServicesToDelete { if serviceName == "" { @@ -327,6 +331,7 @@ func VerifyExpectedResourcesExist(params VerifyExpectedResourcesExistParams) { Eventually(params.RoleBinding).Should(k8sFixture.ExistByName()) Eventually(params.ClusterRole).Should(k8sFixture.ExistByName()) Eventually(params.ClusterRoleBinding).Should(k8sFixture.ExistByName()) + Eventually(params.PrincipalNetworkPolicy).Should(k8sFixture.ExistByName()) for _, serviceName := range params.ServiceNames { if serviceName == "" { diff --git a/test/openshift/e2e/ginkgo/sequential/1-051_validate_argocd_agent_principal_test.go b/test/openshift/e2e/ginkgo/sequential/1-051_validate_argocd_agent_principal_test.go index a11f2b05b..8477d8cd2 100644 --- a/test/openshift/e2e/ginkgo/sequential/1-051_validate_argocd_agent_principal_test.go +++ b/test/openshift/e2e/ginkgo/sequential/1-051_validate_argocd_agent_principal_test.go @@ -25,6 +25,7 @@ import ( 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" @@ -69,6 +70,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { principalDeployment *appsv1.Deployment expectedEnvVariables map[string]string secretNames agentFixture.AgentSecretNames + principalNetworkPolicy *networkingv1.NetworkPolicy principalRoute *routev1.Route resourceProxyServiceName string principalResources agentFixture.PrincipalResources @@ -189,6 +191,12 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Namespace: ns.Name, }, } + principalNetworkPolicy = &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-agent-principal-network-policy", argoCDName), + Namespace: ns.Name, + }, + } // List environment variables with expected values for the principal deployment expectedEnvVariables = map[string]string{ @@ -225,6 +233,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { ClusterRoleBinding: clusterRoleBinding, PrincipalDeployment: principalDeployment, PrincipalRoute: principalRoute, + PrincipalNetworkPolicy: principalNetworkPolicy, ServicesToDelete: []string{ argoCDAgentPrincipalName, fmt.Sprintf(principalMetricsServiceFmt, argoCDName), @@ -276,6 +285,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { ClusterRoleBinding: clusterRoleBinding, PrincipalDeployment: principalDeployment, PrincipalRoute: principalRoute, + PrincipalNetworkPolicy: principalNetworkPolicy, SecretNames: secretNames, ServiceNames: serviceNames, DeploymentNames: deploymentNames, @@ -699,5 +709,68 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { return principalService.Spec.Type }, "30s", "2s").Should(Equal(corev1.ServiceTypeLoadBalancer)) }) + + It("should create principal NetworkPolicy if principal is enabled", func() { + By("Create ArgoCD instance with principal enabled") + + argoCD.Spec.ArgoCDAgent.Principal.Enabled = ptr.To(true) + Expect(k8sClient.Create(ctx, argoCD)).To(Succeed()) + + verifyExpectedResourcesExist(ns) + By("Verify principal NetworkPolicy exists and has expected policy addresses and ports") + + Expect(principalNetworkPolicy.Spec.PodSelector.MatchLabels[common.ArgoCDKeyName]).To(Equal(argoCDAgentPrincipalName)) + Expect(principalNetworkPolicy.Spec.PolicyTypes).To(ContainElement(networkingv1.PolicyTypeIngress)) + Expect(principalNetworkPolicy.Spec.Ingress).To(HaveLen(2)) + + ing := principalNetworkPolicy.Spec.Ingress[0] + Expect(ing.From).To(HaveLen(1)) + Expect(ing.From[0].NamespaceSelector).ToNot(BeNil()) + Expect(*ing.From[0].NamespaceSelector).To(Equal(metav1.LabelSelector{})) + + // Ports the principal exposes (see argocdagent deployment/service code) + expectedPorts := map[int32]bool{ + 8443: true, // principal HTTPS target port + 8000: true, // metrics + 6379: true, // redis proxy + 9090: true, // resource proxy + 8003: true, // healthz + } + Expect(ing.Ports).To(HaveLen(len(expectedPorts))) + for _, p := range ing.Ports { + Expect(p.Port).ToNot(BeNil()) + Expect(expectedPorts[p.Port.IntVal]).To(BeTrue(), "unexpected ingress port %d", p.Port.IntVal) + } + Expect(principalNetworkPolicy.Spec.Ingress[1].From).To(HaveLen(1)) + + Expect(*principalNetworkPolicy.Spec.Ingress[1].From[0].IPBlock).To(Equal(networkingv1.IPBlock{CIDR: "0.0.0.0/0"})) + Expect(principalNetworkPolicy.Spec.Ingress[1].Ports).To(HaveLen(2)) + Expect(principalNetworkPolicy.Spec.Ingress[1].Ports[0].Port.IntVal).To(Equal(int32(8443))) + Expect(principalNetworkPolicy.Spec.Ingress[1].Ports[1].Port.IntVal).To(Equal(int32(443))) + + By("Verify principal NetworkPolicy is deleted when principal instance is disabled") + + argocdFixture.Update(argoCD, func(ac *argov1beta1api.ArgoCD) { + ac.Spec.ArgoCDAgent.Principal.Enabled = nil + }) + + Eventually(principalNetworkPolicy).Should(k8sFixture.NotExistByName()) + + By("Verify principal NetworkPolicy is created when principal instance is enabled and network policy is enabled") + + argocdFixture.Update(argoCD, func(ac *argov1beta1api.ArgoCD) { + ac.Spec.ArgoCDAgent.Principal.Enabled = ptr.To(true) + ac.Spec.NetworkPolicy.Enabled = ptr.To(true) + }) + Eventually(principalNetworkPolicy).Should(k8sFixture.ExistByName()) + + By("Verify principal NetworkPolicy is not created when network policy is disabled") + + argocdFixture.Update(argoCD, func(ac *argov1beta1api.ArgoCD) { + ac.Spec.NetworkPolicy.Enabled = ptr.To(false) + }) + + Eventually(principalNetworkPolicy).Should(k8sFixture.NotExistByName()) + }) }) })