From 0bb5720929da4b354f3d9f189ee2fb6d9ac24ed6 Mon Sep 17 00:00:00 2001 From: Yonatan Sasson Date: Mon, 15 Sep 2025 06:08:24 -0700 Subject: [PATCH 1/2] feat: add configurable cluster domain support Fixes #1149 Signed-off-by: Yonatan Sasson --- api/v1beta1/argocd_types.go | 6 + common/defaults.go | 3 + config/certmanager/certificate.yaml | 4 +- config/default/kustomization.yaml | 2 + controllers/argocd/secret.go | 2 +- controllers/argocd/util.go | 13 +- controllers/argocd/util_test.go | 192 +++++++++++++++++++++ examples/argocd-custom-cluster-domain.yaml | 7 + 8 files changed, 224 insertions(+), 5 deletions(-) create mode 100644 examples/argocd-custom-cluster-domain.yaml diff --git a/api/v1beta1/argocd_types.go b/api/v1beta1/argocd_types.go index 6703b4ed7..9748331d2 100644 --- a/api/v1beta1/argocd_types.go +++ b/api/v1beta1/argocd_types.go @@ -982,6 +982,12 @@ type ArgoCDSpec struct { //+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Version",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:fieldGroup:ArgoCD","urn:alm:descriptor:com.tectonic.ui:text"} Version string `json:"version,omitempty"` + // ClusterDomain is the cluster domain suffix used for constructing service FQDNs. Defaults to "cluster.local". + // The full FQDN will be: ..svc. + // This is useful for clusters that use a different DNS suffix (e.g., "CLUSTER_ID.cluster.local", "edge.local"). + //+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Cluster Domain'",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:text","urn:alm:descriptor:com.tectonic.ui:advanced"} + ClusterDomain string `json:"clusterDomain,omitempty"` + // Banner defines an additional banner to be displayed in Argo CD UI Banner *Banner `json:"banner,omitempty"` diff --git a/common/defaults.go b/common/defaults.go index d1f14cb52..af0f2589b 100644 --- a/common/defaults.go +++ b/common/defaults.go @@ -289,6 +289,9 @@ vs-ssh.visualstudio.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOf // ArgoCDAgentPrincipalDefaultImageName is the default image name for the ArgoCD agent principal. ArgoCDAgentPrincipalDefaultImageName = "quay.io/argoprojlabs/argocd-agent:v0.3.2" + + // ArgoCDDefaultClusterDomain is the default cluster domain suffix for service FQDNs. + ArgoCDDefaultClusterDomain = "cluster.local" ) // DefaultLabels returns the default set of labels for controllers. diff --git a/config/certmanager/certificate.yaml b/config/certmanager/certificate.yaml index 52d866183..a8a2653f7 100644 --- a/config/certmanager/certificate.yaml +++ b/config/certmanager/certificate.yaml @@ -15,10 +15,10 @@ metadata: name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml namespace: system spec: - # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize + # $(SERVICE_NAME), $(SERVICE_NAMESPACE) and $(CLUSTER_DOMAIN) will be substituted by kustomize dnsNames: - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc - - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local + - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.$(CLUSTER_DOMAIN) issuerRef: kind: Issuer name: selfsigned-issuer diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index 2bd4e09fd..580390acc 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -72,3 +72,5 @@ vars: # kind: Service # version: v1 # name: webhook-service +#- name: CLUSTER_DOMAIN +# value: cluster.local diff --git a/controllers/argocd/secret.go b/controllers/argocd/secret.go index 2a65858b1..2f3a853ac 100644 --- a/controllers/argocd/secret.go +++ b/controllers/argocd/secret.go @@ -123,7 +123,7 @@ func newCertificateSecret(suffix string, caCert *x509.Certificate, caKey *rsa.Pr dnsNames := []string{ cr.Name, nameWithSuffix("grpc", cr), - fmt.Sprintf("%s.%s.svc.cluster.local", cr.Name, cr.Namespace), + fmt.Sprintf("%s.%s.svc.%s", cr.Name, cr.Namespace, getClusterDomain(cr)), } //lint:ignore SA1019 known to be deprecated diff --git a/controllers/argocd/util.go b/controllers/argocd/util.go index 98c8afaf1..653ac65d7 100644 --- a/controllers/argocd/util.go +++ b/controllers/argocd/util.go @@ -558,7 +558,7 @@ func getRedisServerAddress(cr *argoproj.ArgoCD) string { // If principal is enabled, then Argo CD server/repo server should be configured to use redis proxy from principal (argo cd agent) if cr.Spec.ArgoCDAgent != nil && cr.Spec.ArgoCDAgent.Principal != nil && cr.Spec.ArgoCDAgent.Principal.IsEnabled() { - return argoutil.GenerateAgentPrincipalRedisProxyServiceName(cr.Name) + "." + cr.Namespace + ".svc.cluster.local:6379" + return fmt.Sprintf("%s.%s.svc.%s:6379", argoutil.GenerateAgentPrincipalRedisProxyServiceName(cr.Name), cr.Namespace, getClusterDomain(cr)) } if cr.Spec.HA.Enabled { @@ -592,10 +592,19 @@ func nameWithSuffix(suffix string, cr *argoproj.ArgoCD) string { return fmt.Sprintf("%s-%s", cr.Name, suffix) } +// getClusterDomain returns the cluster domain suffix for the given ArgoCD instance. +// If not specified in the CR, defaults to the standard cluster domain. +func getClusterDomain(cr *argoproj.ArgoCD) string { + if cr.Spec.ClusterDomain != "" { + return cr.Spec.ClusterDomain + } + return common.ArgoCDDefaultClusterDomain +} + // fqdnServiceRef will return the FQDN referencing a specific service name, as set up by the operator, with the // given port. func fqdnServiceRef(service string, port int, cr *argoproj.ArgoCD) string { - return fmt.Sprintf("%s.%s.svc.cluster.local:%d", nameWithSuffix(service, cr), cr.Namespace, port) + return fmt.Sprintf("%s.%s.svc.%s:%d", nameWithSuffix(service, cr), cr.Namespace, getClusterDomain(cr), port) } // InspectCluster will verify the availability of extra features available to the cluster, such as Prometheus and diff --git a/controllers/argocd/util_test.go b/controllers/argocd/util_test.go index d97d8e240..a352a89c9 100644 --- a/controllers/argocd/util_test.go +++ b/controllers/argocd/util_test.go @@ -1697,3 +1697,195 @@ func TestGetNamespacesToDelete(t *testing.T) { }) } } + +func TestGetClusterDomain(t *testing.T) { + tests := []struct { + name string + clusterDomain string + expectedDomain string + }{ + { + name: "default cluster domain when not specified", + clusterDomain: "", + expectedDomain: "cluster.local", + }, + { + name: "custom cluster domain", + clusterDomain: "CLUSTER_ID.cluster.local", + expectedDomain: "CLUSTER_ID.cluster.local", + }, + { + name: "custom domain for different cloud provider", + clusterDomain: "eks.amazonaws.com", + expectedDomain: "eks.amazonaws.com", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := makeTestArgoCD() + a.Spec.ClusterDomain = tt.clusterDomain + + result := getClusterDomain(a) + assert.Equal(t, tt.expectedDomain, result) + }) + } +} + +func TestFqdnServiceRefWithCustomDomain(t *testing.T) { + tests := []struct { + name string + service string + port int + clusterDomain string + expectedFQDN string + }{ + { + name: "default cluster domain", + service: "redis", + port: 6379, + clusterDomain: "", + expectedFQDN: "argocd-redis.argocd.svc.cluster.local:6379", + }, + { + name: "custom cluster domain", + service: "redis", + port: 6379, + clusterDomain: "CLUSTER_ID.cluster.local", + expectedFQDN: "argocd-redis.argocd.svc.CLUSTER_ID.cluster.local:6379", + }, + { + name: "repo server with custom domain", + service: "repo-server", + port: 8081, + clusterDomain: "eks.amazonaws.com", + expectedFQDN: "argocd-repo-server.argocd.svc.eks.amazonaws.com:8081", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := makeTestArgoCD() + a.Spec.ClusterDomain = tt.clusterDomain + + result := fqdnServiceRef(tt.service, tt.port, a) + assert.Equal(t, tt.expectedFQDN, result) + }) + } +} + +func TestGetRedisServerAddressWithCustomDomain(t *testing.T) { + tests := []struct { + name string + clusterDomain string + haEnabled bool + remoteRedis *string + expectedAddr string + }{ + { + name: "default cluster domain - standalone redis", + clusterDomain: "", + haEnabled: false, + expectedAddr: "argocd-redis.argocd.svc.cluster.local:6379", + }, + { + name: "custom cluster domain - standalone redis", + clusterDomain: "CLUSTER_ID.cluster.local", + haEnabled: false, + expectedAddr: "argocd-redis.argocd.svc.CLUSTER_ID.cluster.local:6379", + }, + { + name: "custom cluster domain - HA redis", + clusterDomain: "eks.amazonaws.com", + haEnabled: true, + expectedAddr: "argocd-redis-ha-haproxy.argocd.svc.eks.amazonaws.com:6379", + }, + { + name: "remote redis - custom domain ignored", + remoteRedis: stringPtr("remote-redis:6379"), + expectedAddr: "remote-redis:6379", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := makeTestArgoCD() + a.Spec.ClusterDomain = tt.clusterDomain + a.Spec.HA.Enabled = tt.haEnabled + a.Spec.Redis.Remote = tt.remoteRedis + + result := getRedisServerAddress(a) + assert.Equal(t, tt.expectedAddr, result) + }) + } +} + +// stringPtr is a helper function to get a pointer to a string +func stringPtr(s string) *string { + return &s +} + +func TestGetDexServerAddressWithCustomDomain(t *testing.T) { + tests := []struct { + name string + clusterDomain string + expectedAddr string + }{ + { + name: "default cluster domain", + clusterDomain: "", + expectedAddr: "https://argocd-dex-server.argocd.svc.cluster.local:5556", + }, + { + name: "custom cluster domain", + clusterDomain: "CLUSTER_ID.cluster.local", + expectedAddr: "https://argocd-dex-server.argocd.svc.CLUSTER_ID.cluster.local:5556", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := makeTestArgoCD() + a.Spec.ClusterDomain = tt.clusterDomain + + result := getDexServerAddress(a) + assert.Equal(t, tt.expectedAddr, result) + }) + } +} + +func TestGetRepoServerAddressWithCustomDomain(t *testing.T) { + tests := []struct { + name string + clusterDomain string + remoteRepo *string + expectedAddr string + }{ + { + name: "default cluster domain", + clusterDomain: "", + expectedAddr: "argocd-repo-server.argocd.svc.cluster.local:8081", + }, + { + name: "custom cluster domain", + clusterDomain: "CLUSTER_ID.cluster.local", + expectedAddr: "argocd-repo-server.argocd.svc.CLUSTER_ID.cluster.local:8081", + }, + { + name: "remote repo server - custom domain ignored", + remoteRepo: stringPtr("remote-repo:8081"), + expectedAddr: "remote-repo:8081", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := makeTestArgoCD() + a.Spec.ClusterDomain = tt.clusterDomain + a.Spec.Repo.Remote = tt.remoteRepo + + result := getRepoServerAddress(a) + assert.Equal(t, tt.expectedAddr, result) + }) + } +} diff --git a/examples/argocd-custom-cluster-domain.yaml b/examples/argocd-custom-cluster-domain.yaml new file mode 100644 index 000000000..cffe8c3a5 --- /dev/null +++ b/examples/argocd-custom-cluster-domain.yaml @@ -0,0 +1,7 @@ +apiVersion: argoproj.io/v1beta1 +kind: ArgoCD +metadata: + name: example-argocd + namespace: argocd +spec: + clusterDomain: "abc123.cluster.local" \ No newline at end of file From c97df98bbdf9041e8b273bc9d024065261661021 Mon Sep 17 00:00:00 2001 From: Yonatan Sasson Date: Mon, 15 Sep 2025 06:20:34 -0700 Subject: [PATCH 2/2] Add generated manifests Signed-off-by: Yonatan Sasson --- .../argocd-operator.clusterserviceversion.yaml | 11 ++++++++++- bundle/manifests/argoproj.io_argocds.yaml | 6 ++++++ config/crd/bases/argoproj.io_argocds.yaml | 6 ++++++ .../bases/argocd-operator.clusterserviceversion.yaml | 9 +++++++++ ...argocd-operator.v0.17.0.clusterserviceversion.yaml | 11 ++++++++++- .../argocd-operator/0.17.0/argoproj.io_argocds.yaml | 6 ++++++ 6 files changed, 47 insertions(+), 2 deletions(-) diff --git a/bundle/manifests/argocd-operator.clusterserviceversion.yaml b/bundle/manifests/argocd-operator.clusterserviceversion.yaml index da60df7b8..21915bb45 100644 --- a/bundle/manifests/argocd-operator.clusterserviceversion.yaml +++ b/bundle/manifests/argocd-operator.clusterserviceversion.yaml @@ -257,7 +257,7 @@ metadata: capabilities: Deep Insights categories: Integration & Delivery certified: "false" - createdAt: "2025-08-28T14:32:44Z" + createdAt: "2025-09-15T13:17:46Z" description: Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes. operators.operatorframework.io/builder: operator-sdk-v1.35.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 @@ -1070,6 +1070,15 @@ spec: - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Prometheus - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server - urn:alm:descriptor:com.tectonic.ui:booleanSwitch + - description: 'ClusterDomain is the cluster domain suffix used for constructing + service FQDNs. Defaults to "cluster.local". The full FQDN will be: ..svc. + This is useful for clusters that use a different DNS suffix (e.g., "CLUSTER_ID.cluster.local", + "edge.local").' + displayName: Cluster Domain' + path: clusterDomain + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:text + - urn:alm:descriptor:com.tectonic.ui:advanced - description: 'Deprecated: ConfigManagementPlugins field is no longer supported. Argo CD now requires plugins to be defined as sidecar containers of repo server component. See ''.spec.repo.sidecarContainers''. ConfigManagementPlugins diff --git a/bundle/manifests/argoproj.io_argocds.yaml b/bundle/manifests/argoproj.io_argocds.yaml index 8d572b20a..e0dca22d5 100644 --- a/bundle/manifests/argoproj.io_argocds.yaml +++ b/bundle/manifests/argoproj.io_argocds.yaml @@ -10248,6 +10248,12 @@ spec: required: - content type: object + clusterDomain: + description: |- + ClusterDomain is the cluster domain suffix used for constructing service FQDNs. Defaults to "cluster.local". + The full FQDN will be: ..svc. + This is useful for clusters that use a different DNS suffix (e.g., "CLUSTER_ID.cluster.local", "edge.local"). + type: string cmdParams: additionalProperties: type: string diff --git a/config/crd/bases/argoproj.io_argocds.yaml b/config/crd/bases/argoproj.io_argocds.yaml index 52a308f44..3db537f6f 100644 --- a/config/crd/bases/argoproj.io_argocds.yaml +++ b/config/crd/bases/argoproj.io_argocds.yaml @@ -10237,6 +10237,12 @@ spec: required: - content type: object + clusterDomain: + description: |- + ClusterDomain is the cluster domain suffix used for constructing service FQDNs. Defaults to "cluster.local". + The full FQDN will be: ..svc. + This is useful for clusters that use a different DNS suffix (e.g., "CLUSTER_ID.cluster.local", "edge.local"). + type: string cmdParams: additionalProperties: type: string diff --git a/config/manifests/bases/argocd-operator.clusterserviceversion.yaml b/config/manifests/bases/argocd-operator.clusterserviceversion.yaml index 38687107b..b713480c8 100644 --- a/config/manifests/bases/argocd-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/argocd-operator.clusterserviceversion.yaml @@ -289,6 +289,15 @@ spec: - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Prometheus - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server - urn:alm:descriptor:com.tectonic.ui:booleanSwitch + - description: 'ClusterDomain is the cluster domain suffix used for constructing + service FQDNs. Defaults to "cluster.local". The full FQDN will be: ..svc. + This is useful for clusters that use a different DNS suffix (e.g., "CLUSTER_ID.cluster.local", + "edge.local").' + displayName: Cluster Domain' + path: clusterDomain + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:text + - urn:alm:descriptor:com.tectonic.ui:advanced - description: 'Deprecated: ConfigManagementPlugins field is no longer supported. Argo CD now requires plugins to be defined as sidecar containers of repo server component. See ''.spec.repo.sidecarContainers''. ConfigManagementPlugins diff --git a/deploy/olm-catalog/argocd-operator/0.17.0/argocd-operator.v0.17.0.clusterserviceversion.yaml b/deploy/olm-catalog/argocd-operator/0.17.0/argocd-operator.v0.17.0.clusterserviceversion.yaml index da60df7b8..21915bb45 100644 --- a/deploy/olm-catalog/argocd-operator/0.17.0/argocd-operator.v0.17.0.clusterserviceversion.yaml +++ b/deploy/olm-catalog/argocd-operator/0.17.0/argocd-operator.v0.17.0.clusterserviceversion.yaml @@ -257,7 +257,7 @@ metadata: capabilities: Deep Insights categories: Integration & Delivery certified: "false" - createdAt: "2025-08-28T14:32:44Z" + createdAt: "2025-09-15T13:17:46Z" description: Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes. operators.operatorframework.io/builder: operator-sdk-v1.35.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 @@ -1070,6 +1070,15 @@ spec: - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Prometheus - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server - urn:alm:descriptor:com.tectonic.ui:booleanSwitch + - description: 'ClusterDomain is the cluster domain suffix used for constructing + service FQDNs. Defaults to "cluster.local". The full FQDN will be: ..svc. + This is useful for clusters that use a different DNS suffix (e.g., "CLUSTER_ID.cluster.local", + "edge.local").' + displayName: Cluster Domain' + path: clusterDomain + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:text + - urn:alm:descriptor:com.tectonic.ui:advanced - description: 'Deprecated: ConfigManagementPlugins field is no longer supported. Argo CD now requires plugins to be defined as sidecar containers of repo server component. See ''.spec.repo.sidecarContainers''. ConfigManagementPlugins diff --git a/deploy/olm-catalog/argocd-operator/0.17.0/argoproj.io_argocds.yaml b/deploy/olm-catalog/argocd-operator/0.17.0/argoproj.io_argocds.yaml index 8d572b20a..e0dca22d5 100644 --- a/deploy/olm-catalog/argocd-operator/0.17.0/argoproj.io_argocds.yaml +++ b/deploy/olm-catalog/argocd-operator/0.17.0/argoproj.io_argocds.yaml @@ -10248,6 +10248,12 @@ spec: required: - content type: object + clusterDomain: + description: |- + ClusterDomain is the cluster domain suffix used for constructing service FQDNs. Defaults to "cluster.local". + The full FQDN will be: ..svc. + This is useful for clusters that use a different DNS suffix (e.g., "CLUSTER_ID.cluster.local", "edge.local"). + type: string cmdParams: additionalProperties: type: string