diff --git a/argocd-operator/api/v1alpha1/argocd_conversion.go b/argocd-operator/api/v1alpha1/argocd_conversion.go index aab5a1031b6..cb4e1066d25 100644 --- a/argocd-operator/api/v1alpha1/argocd_conversion.go +++ b/argocd-operator/api/v1alpha1/argocd_conversion.go @@ -817,20 +817,21 @@ func ConvertAlphaToBetaPrincipal(src *PrincipalSpec) *v1beta1.PrincipalSpec { var dst *v1beta1.PrincipalSpec if src != nil { dst = &v1beta1.PrincipalSpec{ - Enabled: src.Enabled, - Auth: src.Auth, - LogLevel: src.LogLevel, - LogFormat: src.LogFormat, - Image: src.Image, - LabelSelector: src.LabelSelector, - Env: src.Env, - Server: ConvertAlphaToBetaPrincipalServer(src.Server), - Redis: ConvertAlphaToBetaPrincipalRedis(src.Redis), - Namespace: ConvertAlphaToBetaPrincipalNamespace(src.Namespace), - TLS: ConvertAlphaToBetaPrincipalTLS(src.TLS), - ResourceProxy: ConvertAlphaToBetaPrincipalResourceProxy(src.ResourceProxy), - JWT: ConvertAlphaToBetaPrincipalJWT(src.JWT), - Metrics: ConvertAlphaToBetaMetrics(src.Metrics), + Enabled: src.Enabled, + Auth: src.Auth, + LogLevel: src.LogLevel, + LogFormat: src.LogFormat, + Image: src.Image, + LabelSelector: src.LabelSelector, + Env: src.Env, + Server: ConvertAlphaToBetaPrincipalServer(src.Server), + Redis: ConvertAlphaToBetaPrincipalRedis(src.Redis), + Namespace: ConvertAlphaToBetaPrincipalNamespace(src.Namespace), + TLS: ConvertAlphaToBetaPrincipalTLS(src.TLS), + ResourceProxy: ConvertAlphaToBetaPrincipalResourceProxy(src.ResourceProxy), + JWT: ConvertAlphaToBetaPrincipalJWT(src.JWT), + Metrics: ConvertAlphaToBetaMetrics(src.Metrics), + SelfRegistration: ConvertAlphaToBetaPrincipalSelfRegistration(src.SelfRegistration), } } return dst @@ -851,20 +852,21 @@ func ConvertBetaToAlphaPrincipal(src *v1beta1.PrincipalSpec) *PrincipalSpec { var dst *PrincipalSpec if src != nil { dst = &PrincipalSpec{ - Enabled: src.Enabled, - Auth: src.Auth, - LogLevel: src.LogLevel, - LogFormat: src.LogFormat, - Image: src.Image, - LabelSelector: src.LabelSelector, - Env: src.Env, - Server: ConvertBetaToAlphaPrincipalServer(src.Server), - Redis: ConvertBetaToAlphaPrincipalRedis(src.Redis), - Namespace: ConvertBetaToAlphaPrincipalNamespace(src.Namespace), - TLS: ConvertBetaToAlphaPrincipalTLS(src.TLS), - ResourceProxy: ConvertBetaToAlphaPrincipalResourceProxy(src.ResourceProxy), - JWT: ConvertBetaToAlphaPrincipalJWT(src.JWT), - Metrics: ConvertBetaToAlphaMetrics(src.Metrics), + Enabled: src.Enabled, + Auth: src.Auth, + LogLevel: src.LogLevel, + LogFormat: src.LogFormat, + Image: src.Image, + LabelSelector: src.LabelSelector, + Env: src.Env, + Server: ConvertBetaToAlphaPrincipalServer(src.Server), + Redis: ConvertBetaToAlphaPrincipalRedis(src.Redis), + Namespace: ConvertBetaToAlphaPrincipalNamespace(src.Namespace), + TLS: ConvertBetaToAlphaPrincipalTLS(src.TLS), + ResourceProxy: ConvertBetaToAlphaPrincipalResourceProxy(src.ResourceProxy), + JWT: ConvertBetaToAlphaPrincipalJWT(src.JWT), + Metrics: ConvertBetaToAlphaMetrics(src.Metrics), + SelfRegistration: ConvertBetaToAlphaPrincipalSelfRegistration(src.SelfRegistration), } } return dst @@ -1211,3 +1213,25 @@ func ConvertAlphaToBetaNamespaceManagement(src []ManagedNamespaces) []v1beta1.Ma } return dst } + +func ConvertAlphaToBetaPrincipalSelfRegistration(src *PrincipalSelfRegistrationSpec) *v1beta1.PrincipalSelfRegistrationSpec { + var dst *v1beta1.PrincipalSelfRegistrationSpec + if src != nil { + dst = &v1beta1.PrincipalSelfRegistrationSpec{ + Enabled: src.Enabled, + ClientCertSecretName: src.ClientCertSecretName, + } + } + return dst +} + +func ConvertBetaToAlphaPrincipalSelfRegistration(src *v1beta1.PrincipalSelfRegistrationSpec) *PrincipalSelfRegistrationSpec { + var dst *PrincipalSelfRegistrationSpec + if src != nil { + dst = &PrincipalSelfRegistrationSpec{ + Enabled: src.Enabled, + ClientCertSecretName: src.ClientCertSecretName, + } + } + return dst +} diff --git a/argocd-operator/api/v1alpha1/argocd_conversion_test.go b/argocd-operator/api/v1alpha1/argocd_conversion_test.go index bdf5ce082d7..90892572747 100644 --- a/argocd-operator/api/v1alpha1/argocd_conversion_test.go +++ b/argocd-operator/api/v1alpha1/argocd_conversion_test.go @@ -831,6 +831,35 @@ func TestAlphaToBetaConversion(t *testing.T) { } }), }, + { + name: "ArgoCD Example - Agent Principal with SelfRegistration", + input: makeTestArgoCDAlpha(func(cr *ArgoCD) { + enabled := true + selfRegEnabled := true + cr.Spec.ArgoCDAgent = &ArgoCDAgentSpec{ + Principal: &PrincipalSpec{ + Enabled: &enabled, + SelfRegistration: &PrincipalSelfRegistrationSpec{ + Enabled: &selfRegEnabled, + ClientCertSecretName: "argocd-agent-shared-client-cert", + }, + }, + } + }), + expectedOutput: makeTestArgoCDBeta(func(cr *v1beta1.ArgoCD) { + enabled := true + selfRegEnabled := true + cr.Spec.ArgoCDAgent = &v1beta1.ArgoCDAgentSpec{ + Principal: &v1beta1.PrincipalSpec{ + Enabled: &enabled, + SelfRegistration: &v1beta1.PrincipalSelfRegistrationSpec{ + Enabled: &selfRegEnabled, + ClientCertSecretName: "argocd-agent-shared-client-cert", + }, + }, + } + }), + }, } for _, test := range tests { @@ -1252,6 +1281,35 @@ func TestBetaToAlphaConversion(t *testing.T) { } }), }, + { + name: "ArgoCD Example - Agent Principal with SelfRegistration", + input: makeTestArgoCDBeta(func(cr *v1beta1.ArgoCD) { + enabled := true + selfRegEnabled := true + cr.Spec.ArgoCDAgent = &v1beta1.ArgoCDAgentSpec{ + Principal: &v1beta1.PrincipalSpec{ + Enabled: &enabled, + SelfRegistration: &v1beta1.PrincipalSelfRegistrationSpec{ + Enabled: &selfRegEnabled, + ClientCertSecretName: "argocd-agent-shared-client-cert", + }, + }, + } + }), + expectedOutput: makeTestArgoCDAlpha(func(cr *ArgoCD) { + enabled := true + selfRegEnabled := true + cr.Spec.ArgoCDAgent = &ArgoCDAgentSpec{ + Principal: &PrincipalSpec{ + Enabled: &enabled, + SelfRegistration: &PrincipalSelfRegistrationSpec{ + Enabled: &selfRegEnabled, + ClientCertSecretName: "argocd-agent-shared-client-cert", + }, + }, + } + }), + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { diff --git a/argocd-operator/api/v1alpha1/argocd_types.go b/argocd-operator/api/v1alpha1/argocd_types.go index 782061e16f5..52970c0f19d 100644 --- a/argocd-operator/api/v1alpha1/argocd_types.go +++ b/argocd-operator/api/v1alpha1/argocd_types.go @@ -1288,6 +1288,19 @@ type PrincipalSpec struct { // Metrics defines the metrics configuration for the Principal ServiceMonitor. Metrics *ArgoCDMetricsSpec `json:"metrics,omitempty"` + + // SelfRegistration defines the self-registration options for the Principal component. + SelfRegistration *PrincipalSelfRegistrationSpec `json:"selfRegistration,omitempty"` +} + +type PrincipalSelfRegistrationSpec struct { + // Enabled is the flag to enable self-registration of agents. + // When enabled, agents with valid credentials can automatically register on connection. + Enabled *bool `json:"enabled,omitempty"` + + // ClientCertSecretName is the name of the TLS secret containing shared client cert + // for self-registered cluster secrets (must have tls.crt, tls.key, ca.crt). + ClientCertSecretName string `json:"clientCertSecretName,omitempty"` } type PrincipalServerSpec struct { diff --git a/argocd-operator/api/v1alpha1/zz_generated.deepcopy.go b/argocd-operator/api/v1alpha1/zz_generated.deepcopy.go index 2c2bea14a8f..b68564e1896 100644 --- a/argocd-operator/api/v1alpha1/zz_generated.deepcopy.go +++ b/argocd-operator/api/v1alpha1/zz_generated.deepcopy.go @@ -1793,6 +1793,26 @@ func (in *PrincipalResourceProxySpec) DeepCopy() *PrincipalResourceProxySpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PrincipalSelfRegistrationSpec) DeepCopyInto(out *PrincipalSelfRegistrationSpec) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrincipalSelfRegistrationSpec. +func (in *PrincipalSelfRegistrationSpec) DeepCopy() *PrincipalSelfRegistrationSpec { + if in == nil { + return nil + } + out := new(PrincipalSelfRegistrationSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PrincipalServerSpec) DeepCopyInto(out *PrincipalServerSpec) { *out = *in @@ -1865,6 +1885,11 @@ func (in *PrincipalSpec) DeepCopyInto(out *PrincipalSpec) { *out = new(ArgoCDMetricsSpec) **out = **in } + if in.SelfRegistration != nil { + in, out := &in.SelfRegistration, &out.SelfRegistration + *out = new(PrincipalSelfRegistrationSpec) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrincipalSpec. diff --git a/argocd-operator/api/v1beta1/argocd_types.go b/argocd-operator/api/v1beta1/argocd_types.go index 93863ca768c..e2bd4738e19 100644 --- a/argocd-operator/api/v1beta1/argocd_types.go +++ b/argocd-operator/api/v1beta1/argocd_types.go @@ -1490,6 +1490,19 @@ type PrincipalSpec struct { // Metrics defines the metrics configuration for the Principal ServiceMonitor. Metrics *ArgoCDMetricsSpec `json:"metrics,omitempty"` + + // SelfRegistration defines the self-registration options for the Principal component. + SelfRegistration *PrincipalSelfRegistrationSpec `json:"selfRegistration,omitempty"` +} + +type PrincipalSelfRegistrationSpec struct { + // Enabled is the flag to enable self-registration of agents. + // When enabled, agents with valid credentials can automatically register on connection. + Enabled *bool `json:"enabled,omitempty"` + + // ClientCertSecretName is the name of the TLS secret containing shared client cert + // for self-registered cluster secrets (must have tls.crt, tls.key, ca.crt). + ClientCertSecretName string `json:"clientCertSecretName,omitempty"` } type PrincipalServerSpec struct { diff --git a/argocd-operator/api/v1beta1/zz_generated.deepcopy.go b/argocd-operator/api/v1beta1/zz_generated.deepcopy.go index a8daa076a82..211eb6de4bb 100644 --- a/argocd-operator/api/v1beta1/zz_generated.deepcopy.go +++ b/argocd-operator/api/v1beta1/zz_generated.deepcopy.go @@ -2007,6 +2007,26 @@ func (in *PrincipalResourceProxySpec) DeepCopy() *PrincipalResourceProxySpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PrincipalSelfRegistrationSpec) DeepCopyInto(out *PrincipalSelfRegistrationSpec) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrincipalSelfRegistrationSpec. +func (in *PrincipalSelfRegistrationSpec) DeepCopy() *PrincipalSelfRegistrationSpec { + if in == nil { + return nil + } + out := new(PrincipalSelfRegistrationSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PrincipalServerSpec) DeepCopyInto(out *PrincipalServerSpec) { *out = *in @@ -2089,6 +2109,11 @@ func (in *PrincipalSpec) DeepCopyInto(out *PrincipalSpec) { *out = new(ArgoCDMetricsSpec) **out = **in } + if in.SelfRegistration != nil { + in, out := &in.SelfRegistration, &out.SelfRegistration + *out = new(PrincipalSelfRegistrationSpec) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrincipalSpec. diff --git a/argocd-operator/bundle/manifests/argoproj.io_argocds.yaml b/argocd-operator/bundle/manifests/argoproj.io_argocds.yaml index 139972b1867..5758e9c03c6 100644 --- a/argocd-operator/bundle/manifests/argoproj.io_argocds.yaml +++ b/argocd-operator/bundle/manifests/argoproj.io_argocds.yaml @@ -1022,6 +1022,21 @@ spec: the TLS certificate and key for the resource proxy. type: string type: object + selfRegistration: + description: SelfRegistration defines the self-registration + options for the Principal component. + properties: + clientCertSecretName: + description: |- + ClientCertSecretName is the name of the TLS secret containing shared client cert + for self-registered cluster secrets (must have tls.crt, tls.key, ca.crt). + type: string + enabled: + description: |- + Enabled is the flag to enable self-registration of agents. + When enabled, agents with valid credentials can automatically register on connection. + type: boolean + type: object server: description: Server defines the server options for the Principal component. @@ -14183,6 +14198,21 @@ spec: More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object + selfRegistration: + description: SelfRegistration defines the self-registration + options for the Principal component. + properties: + clientCertSecretName: + description: |- + ClientCertSecretName is the name of the TLS secret containing shared client cert + for self-registered cluster secrets (must have tls.crt, tls.key, ca.crt). + type: string + enabled: + description: |- + Enabled is the flag to enable self-registration of agents. + When enabled, agents with valid credentials can automatically register on connection. + type: boolean + type: object server: description: Server defines the server options for the Principal component. diff --git a/argocd-operator/common/defaults.go b/argocd-operator/common/defaults.go index b81bcab7411..711c7325af9 100644 --- a/argocd-operator/common/defaults.go +++ b/argocd-operator/common/defaults.go @@ -312,10 +312,10 @@ vs-ssh.visualstudio.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOf ArgoCDCmdParamsConfigMapName = "argocd-cmd-params-cm" // ArgoCDAgentPrincipalDefaultImageName is the default image name for the ArgoCD agent's principal component. - ArgoCDAgentPrincipalDefaultImageName = "quay.io/argoprojlabs/argocd-agent:v0.9.0" + ArgoCDAgentPrincipalDefaultImageName = "quay.io/argoprojlabs/argocd-agent:v0.10.0" // ArgoCDAgentAgentDefaultImageName is the default image name for the ArgoCD agent's agent component. - ArgoCDAgentAgentDefaultImageName = "quay.io/argoprojlabs/argocd-agent:v0.9.0" + ArgoCDAgentAgentDefaultImageName = "quay.io/argoprojlabs/argocd-agent:v0.10.0" // ArgoCDImageUpdaterControllerComponent is the name of the Image Updater controller control plane component ArgoCDImageUpdaterControllerComponent = "argocd-image-updater-controller" diff --git a/argocd-operator/config/crd/bases/argoproj.io_argocds.yaml b/argocd-operator/config/crd/bases/argoproj.io_argocds.yaml index 40fe001b11d..ed34dea9a2b 100644 --- a/argocd-operator/config/crd/bases/argoproj.io_argocds.yaml +++ b/argocd-operator/config/crd/bases/argoproj.io_argocds.yaml @@ -1011,6 +1011,21 @@ spec: the TLS certificate and key for the resource proxy. type: string type: object + selfRegistration: + description: SelfRegistration defines the self-registration + options for the Principal component. + properties: + clientCertSecretName: + description: |- + ClientCertSecretName is the name of the TLS secret containing shared client cert + for self-registered cluster secrets (must have tls.crt, tls.key, ca.crt). + type: string + enabled: + description: |- + Enabled is the flag to enable self-registration of agents. + When enabled, agents with valid credentials can automatically register on connection. + type: boolean + type: object server: description: Server defines the server options for the Principal component. @@ -14172,6 +14187,21 @@ spec: More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object + selfRegistration: + description: SelfRegistration defines the self-registration + options for the Principal component. + properties: + clientCertSecretName: + description: |- + ClientCertSecretName is the name of the TLS secret containing shared client cert + for self-registered cluster secrets (must have tls.crt, tls.key, ca.crt). + type: string + enabled: + description: |- + Enabled is the flag to enable self-registration of agents. + When enabled, agents with valid credentials can automatically register on connection. + type: boolean + type: object server: description: Server defines the server options for the Principal component. diff --git a/argocd-operator/controllers/argocdagent/deployment.go b/argocd-operator/controllers/argocdagent/deployment.go index f07d1dcc4f3..8fad4d04cb6 100644 --- a/argocd-operator/controllers/argocdagent/deployment.go +++ b/argocd-operator/controllers/argocdagent/deployment.go @@ -416,6 +416,15 @@ func buildPrincipalContainerEnv(cr *argoproj.ArgoCD, centralTLSProfile tlsProfil }, { Name: EnvArgoCDPrincipalLabelSelector, Value: getPrincipalLabelSelector(cr), + }, { + Name: EnvArgoCDPrincipalEnableSelfClusterRegistration, + Value: getPrincipalEnableSelfClusterRegistration(cr), + }, { + Name: EnvArgoCDPrincipalSelfRegistrationClientCertSecret, + Value: getPrincipalSelfRegistrationClientCertSecret(cr), + }, { + Name: EnvArgoCDPrincipalResourceProxyAddress, + Value: getPrincipalResourceProxyAddress(cr), }, } @@ -432,31 +441,34 @@ func buildPrincipalContainerEnv(cr *argoproj.ArgoCD, centralTLSProfile tlsProfil // These constants are environment variables that correspond to the environment variables // used to configure Argo CD agent, and should match the names exactly from the agent const ( - EnvArgoCDPrincipalLogLevel = "ARGOCD_PRINCIPAL_LOG_LEVEL" - EnvArgoCDPrincipalLogFormat = "ARGOCD_PRINCIPAL_LOG_FORMAT" - EnvArgoCDPrincipalNamespace = "ARGOCD_PRINCIPAL_NAMESPACE" - EnvArgoCDPrincipalAllowedNamespaces = "ARGOCD_PRINCIPAL_ALLOWED_NAMESPACES" - EnvArgoCDPrincipalNamespaceCreateEnable = "ARGOCD_PRINCIPAL_NAMESPACE_CREATE_ENABLE" - EnvArgoCDPrincipalNamespaceCreatePattern = "ARGOCD_PRINCIPAL_NAMESPACE_CREATE_PATTERN" - EnvArgoCDPrincipalNamespaceCreateLabels = "ARGOCD_PRINCIPAL_NAMESPACE_CREATE_LABELS" - EnvArgoCDPrincipalTLSServerAllowGenerate = "ARGOCD_PRINCIPAL_TLS_SERVER_ALLOW_GENERATE" - EnvArgoCDPrincipalJWTAllowGenerate = "ARGOCD_PRINCIPAL_JWT_ALLOW_GENERATE" - EnvArgoCDPrincipalAuth = "ARGOCD_PRINCIPAL_AUTH" - EnvArgoCDPrincipalEnableWebSocket = "ARGOCD_PRINCIPAL_ENABLE_WEBSOCKET" - EnvArgoCDPrincipalEnableResourceProxy = "ARGOCD_PRINCIPAL_ENABLE_RESOURCE_PROXY" - EnvArgoCDPrincipalKeepAliveMinInterval = "ARGOCD_PRINCIPAL_KEEP_ALIVE_MIN_INTERVAL" - EnvArgoCDPrincipalRedisServerAddress = "ARGOCD_PRINCIPAL_REDIS_SERVER_ADDRESS" - EnvArgoCDPrincipalRedisCompressionType = "ARGOCD_PRINCIPAL_REDIS_COMPRESSION_TYPE" - EnvArgoCDPrincipalTLSSecretName = "ARGOCD_PRINCIPAL_TLS_SECRET_NAME" - EnvArgoCDPrincipalTLSServerRootCASecretName = "ARGOCD_PRINCIPAL_TLS_SERVER_ROOT_CA_SECRET_NAME" - EnvArgoCDPrincipalResourceProxySecretName = "ARGOCD_PRINCIPAL_RESOURCE_PROXY_SECRET_NAME" - EnvArgoCDPrincipalResourceProxyCaSecretName = "ARGOCD_PRINCIPAL_RESOURCE_PROXY_CA_SECRET_NAME" - EnvArgoCDPrincipalJwtSecretName = "ARGOCD_PRINCIPAL_JWT_SECRET_NAME" - EnvArgoCDPrincipalImage = "ARGOCD_PRINCIPAL_IMAGE" - EnvArgoCDPrincipalDestinationBasedMapping = "ARGOCD_PRINCIPAL_DESTINATION_BASED_MAPPING" - EnvArgoCDPrincipalLabelSelector = "ARGOCD_PRINCIPAL_LABEL_SELECTOR" - EnvArgoCDPrincipalTlsMinVersion = "ARGOCD_PRINCIPAL_TLS_MIN_VERSION" - EnvArgoCDPrincipalCipherSuites = "ARGOCD_PRINCIPAL_TLS_CIPHERSUITES" + EnvArgoCDPrincipalLogLevel = "ARGOCD_PRINCIPAL_LOG_LEVEL" + EnvArgoCDPrincipalLogFormat = "ARGOCD_PRINCIPAL_LOG_FORMAT" + EnvArgoCDPrincipalNamespace = "ARGOCD_PRINCIPAL_NAMESPACE" + EnvArgoCDPrincipalAllowedNamespaces = "ARGOCD_PRINCIPAL_ALLOWED_NAMESPACES" + EnvArgoCDPrincipalNamespaceCreateEnable = "ARGOCD_PRINCIPAL_NAMESPACE_CREATE_ENABLE" + EnvArgoCDPrincipalNamespaceCreatePattern = "ARGOCD_PRINCIPAL_NAMESPACE_CREATE_PATTERN" + EnvArgoCDPrincipalNamespaceCreateLabels = "ARGOCD_PRINCIPAL_NAMESPACE_CREATE_LABELS" + EnvArgoCDPrincipalTLSServerAllowGenerate = "ARGOCD_PRINCIPAL_TLS_SERVER_ALLOW_GENERATE" + EnvArgoCDPrincipalJWTAllowGenerate = "ARGOCD_PRINCIPAL_JWT_ALLOW_GENERATE" + EnvArgoCDPrincipalAuth = "ARGOCD_PRINCIPAL_AUTH" + EnvArgoCDPrincipalEnableWebSocket = "ARGOCD_PRINCIPAL_ENABLE_WEBSOCKET" + EnvArgoCDPrincipalEnableResourceProxy = "ARGOCD_PRINCIPAL_ENABLE_RESOURCE_PROXY" + EnvArgoCDPrincipalKeepAliveMinInterval = "ARGOCD_PRINCIPAL_KEEP_ALIVE_MIN_INTERVAL" + EnvArgoCDPrincipalRedisServerAddress = "ARGOCD_PRINCIPAL_REDIS_SERVER_ADDRESS" + EnvArgoCDPrincipalRedisCompressionType = "ARGOCD_PRINCIPAL_REDIS_COMPRESSION_TYPE" + EnvArgoCDPrincipalTLSSecretName = "ARGOCD_PRINCIPAL_TLS_SECRET_NAME" + EnvArgoCDPrincipalTLSServerRootCASecretName = "ARGOCD_PRINCIPAL_TLS_SERVER_ROOT_CA_SECRET_NAME" + EnvArgoCDPrincipalResourceProxySecretName = "ARGOCD_PRINCIPAL_RESOURCE_PROXY_SECRET_NAME" + EnvArgoCDPrincipalResourceProxyCaSecretName = "ARGOCD_PRINCIPAL_RESOURCE_PROXY_CA_SECRET_NAME" + EnvArgoCDPrincipalJwtSecretName = "ARGOCD_PRINCIPAL_JWT_SECRET_NAME" + EnvArgoCDPrincipalImage = "ARGOCD_PRINCIPAL_IMAGE" + EnvArgoCDPrincipalDestinationBasedMapping = "ARGOCD_PRINCIPAL_DESTINATION_BASED_MAPPING" + EnvArgoCDPrincipalLabelSelector = "ARGOCD_PRINCIPAL_LABEL_SELECTOR" + EnvArgoCDPrincipalTlsMinVersion = "ARGOCD_PRINCIPAL_TLS_MIN_VERSION" + EnvArgoCDPrincipalCipherSuites = "ARGOCD_PRINCIPAL_TLS_CIPHERSUITES" + EnvArgoCDPrincipalEnableSelfClusterRegistration = "ARGOCD_PRINCIPAL_ENABLE_SELF_CLUSTER_REGISTRATION" + EnvArgoCDPrincipalSelfRegistrationClientCertSecret = "ARGOCD_PRINCIPAL_SELF_REGISTRATION_CLIENT_CERT_SECRET" + EnvArgoCDPrincipalResourceProxyAddress = "ARGOCD_PRINCIPAL_RESOURCE_PROXY_ADDRESS" ) func getPrincipalTlsConfig(centralTLSProfile tlsProfile.TLSConfigProfile) map[string]string { @@ -686,3 +698,29 @@ func hasRedis(cr *argoproj.ArgoCD) bool { cr.Spec.ArgoCDAgent.Principal != nil && cr.Spec.ArgoCDAgent.Principal.Redis != nil } + +func hasSelfRegistration(cr *argoproj.ArgoCD) bool { + return cr.Spec.ArgoCDAgent != nil && + cr.Spec.ArgoCDAgent.Principal != nil && + cr.Spec.ArgoCDAgent.Principal.SelfRegistration != nil +} + +func getPrincipalEnableSelfClusterRegistration(cr *argoproj.ArgoCD) string { + if hasSelfRegistration(cr) && cr.Spec.ArgoCDAgent.Principal.SelfRegistration.Enabled != nil { + return strconv.FormatBool(*cr.Spec.ArgoCDAgent.Principal.SelfRegistration.Enabled) + } + return "false" +} + +func getPrincipalSelfRegistrationClientCertSecret(cr *argoproj.ArgoCD) string { + if hasSelfRegistration(cr) && cr.Spec.ArgoCDAgent.Principal.SelfRegistration.ClientCertSecretName != "" { + return cr.Spec.ArgoCDAgent.Principal.SelfRegistration.ClientCertSecretName + } + return "" +} + +func getPrincipalResourceProxyAddress(cr *argoproj.ArgoCD) string { + return fmt.Sprintf("%s:%d", + generateAgentResourceName(cr.Name, string(argoproj.AgentComponentTypePrincipal)+"-resource-proxy"), + PrincipalResourceProxyServicePort) +} diff --git a/argocd-operator/controllers/argocdagent/deployment_test.go b/argocd-operator/controllers/argocdagent/deployment_test.go index 1a42718e263..7c3b56746cc 100644 --- a/argocd-operator/controllers/argocdagent/deployment_test.go +++ b/argocd-operator/controllers/argocdagent/deployment_test.go @@ -862,3 +862,129 @@ func TestBuildPrincipalContainerEnv_LabelSelector(t *testing.T) { }) } } + +func withSelfRegistration(enabled bool, clientCertSecretName string) argoCDOpt { + return func(a *argoproj.ArgoCD) { + if a.Spec.ArgoCDAgent == nil { + a.Spec.ArgoCDAgent = &argoproj.ArgoCDAgentSpec{} + } + if a.Spec.ArgoCDAgent.Principal == nil { + a.Spec.ArgoCDAgent.Principal = &argoproj.PrincipalSpec{} + } + a.Spec.ArgoCDAgent.Principal.SelfRegistration = &argoproj.PrincipalSelfRegistrationSpec{ + Enabled: &enabled, + ClientCertSecretName: clientCertSecretName, + } + } +} + +func TestGetPrincipalEnableSelfClusterRegistration(t *testing.T) { + tests := []struct { + name string + cr *argoproj.ArgoCD + expected string + }{ + { + name: "principal not configured", + cr: makeTestArgoCD(), + expected: "false", + }, + { + name: "self-registration not configured", + cr: makeTestArgoCD(withPrincipalEnabled(true)), + expected: "false", + }, + { + name: "self-registration enabled", + cr: makeTestArgoCD(withPrincipalEnabled(true), withSelfRegistration(true, "")), + expected: "true", + }, + { + name: "self-registration disabled", + cr: makeTestArgoCD(withPrincipalEnabled(true), withSelfRegistration(false, "")), + expected: "false", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, getPrincipalEnableSelfClusterRegistration(tt.cr)) + }) + } +} + +func TestGetPrincipalSelfRegistrationClientCertSecret(t *testing.T) { + tests := []struct { + name string + cr *argoproj.ArgoCD + expected string + }{ + { + name: "principal not configured", + cr: makeTestArgoCD(), + expected: "", + }, + { + name: "self-registration not configured", + cr: makeTestArgoCD(withPrincipalEnabled(true)), + expected: "", + }, + { + name: "client cert secret set", + cr: makeTestArgoCD(withPrincipalEnabled(true), withSelfRegistration(true, "argocd-agent-shared-client-cert")), + expected: "argocd-agent-shared-client-cert", + }, + { + name: "client cert secret empty", + cr: makeTestArgoCD(withPrincipalEnabled(true), withSelfRegistration(true, "")), + expected: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, getPrincipalSelfRegistrationClientCertSecret(tt.cr)) + }) + } +} + +func TestBuildPrincipalContainerEnv_SelfRegistration(t *testing.T) { + tests := []struct { + name string + cr *argoproj.ArgoCD + expectedEnabled string + expectedClientCertSecret string + expectedResourceProxyAddr string + }{ + { + name: "defaults when self-registration not configured", + cr: makeTestArgoCD(withPrincipalEnabled(true)), + expectedEnabled: "false", + expectedClientCertSecret: "", + expectedResourceProxyAddr: generateAgentResourceName(testArgoCDName, testCompName+"-resource-proxy") + ":9090", + }, + { + name: "self-registration enabled with client cert", + cr: makeTestArgoCD(withPrincipalEnabled(true), withSelfRegistration(true, "argocd-agent-shared-client-cert")), + expectedEnabled: "true", + expectedClientCertSecret: "argocd-agent-shared-client-cert", + expectedResourceProxyAddr: generateAgentResourceName(testArgoCDName, testCompName+"-resource-proxy") + ":9090", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + envVars := buildPrincipalContainerEnv(tt.cr, tlsprofile.TLSConfigProfile{}) + envMap := make(map[string]string) + for _, e := range envVars { + envMap[e.Name] = e.Value + } + assert.Equal(t, tt.expectedEnabled, envMap[EnvArgoCDPrincipalEnableSelfClusterRegistration], + "ARGOCD_PRINCIPAL_ENABLE_SELF_CLUSTER_REGISTRATION mismatch") + assert.Equal(t, tt.expectedClientCertSecret, envMap[EnvArgoCDPrincipalSelfRegistrationClientCertSecret], + "ARGOCD_PRINCIPAL_SELF_REGISTRATION_CLIENT_CERT_SECRET mismatch") + assert.Equal(t, tt.expectedResourceProxyAddr, envMap[EnvArgoCDPrincipalResourceProxyAddress], + "ARGOCD_PRINCIPAL_RESOURCE_PROXY_ADDRESS mismatch") + }) + } +} diff --git a/argocd-operator/deploy/olm-catalog/argocd-operator/0.20.0/argoproj.io_argocds.yaml b/argocd-operator/deploy/olm-catalog/argocd-operator/0.20.0/argoproj.io_argocds.yaml index 139972b1867..5758e9c03c6 100644 --- a/argocd-operator/deploy/olm-catalog/argocd-operator/0.20.0/argoproj.io_argocds.yaml +++ b/argocd-operator/deploy/olm-catalog/argocd-operator/0.20.0/argoproj.io_argocds.yaml @@ -1022,6 +1022,21 @@ spec: the TLS certificate and key for the resource proxy. type: string type: object + selfRegistration: + description: SelfRegistration defines the self-registration + options for the Principal component. + properties: + clientCertSecretName: + description: |- + ClientCertSecretName is the name of the TLS secret containing shared client cert + for self-registered cluster secrets (must have tls.crt, tls.key, ca.crt). + type: string + enabled: + description: |- + Enabled is the flag to enable self-registration of agents. + When enabled, agents with valid credentials can automatically register on connection. + type: boolean + type: object server: description: Server defines the server options for the Principal component. @@ -14183,6 +14198,21 @@ spec: More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object + selfRegistration: + description: SelfRegistration defines the self-registration + options for the Principal component. + properties: + clientCertSecretName: + description: |- + ClientCertSecretName is the name of the TLS secret containing shared client cert + for self-registered cluster secrets (must have tls.crt, tls.key, ca.crt). + type: string + enabled: + description: |- + Enabled is the flag to enable self-registration of agents. + When enabled, agents with valid credentials can automatically register on connection. + type: boolean + type: object server: description: Server defines the server options for the Principal component. diff --git a/argocd-operator/tests/ginkgo/sequential/1-051_validate_argocd_agent_principal_test.go b/argocd-operator/tests/ginkgo/sequential/1-051_validate_argocd_agent_principal_test.go index e1efcb3dc80..8221252e96c 100644 --- a/argocd-operator/tests/ginkgo/sequential/1-051_validate_argocd_agent_principal_test.go +++ b/argocd-operator/tests/ginkgo/sequential/1-051_validate_argocd_agent_principal_test.go @@ -200,27 +200,30 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { // List environment variables with expected values for the principal deployment expectedEnvVariables = map[string]string{ - argocdagent.EnvArgoCDPrincipalLogLevel: "info", - argocdagent.EnvArgoCDPrincipalNamespace: ns.Name, - argocdagent.EnvArgoCDPrincipalAllowedNamespaces: "*", - argocdagent.EnvArgoCDPrincipalNamespaceCreateEnable: "false", - argocdagent.EnvArgoCDPrincipalNamespaceCreatePattern: "", - argocdagent.EnvArgoCDPrincipalNamespaceCreateLabels: "", - argocdagent.EnvArgoCDPrincipalTLSServerAllowGenerate: "true", - argocdagent.EnvArgoCDPrincipalJWTAllowGenerate: "true", - argocdagent.EnvArgoCDPrincipalAuth: "mtls:CN=([^,]+)", - argocdagent.EnvArgoCDPrincipalEnableResourceProxy: "true", - argocdagent.EnvArgoCDPrincipalKeepAliveMinInterval: "30s", - argocdagent.EnvArgoCDPrincipalRedisServerAddress: fmt.Sprintf("%s-%s:%d", argoCDName, "redis", common.ArgoCDDefaultRedisPort), - argocdagent.EnvArgoCDPrincipalRedisCompressionType: "gzip", - argocdagent.EnvArgoCDPrincipalLogFormat: "text", - argocdagent.EnvArgoCDPrincipalEnableWebSocket: "false", - argocdagent.EnvArgoCDPrincipalTLSSecretName: agentPrincipalTLSSecretName, - argocdagent.EnvArgoCDPrincipalTLSServerRootCASecretName: agentRootCASecretName, - argocdagent.EnvArgoCDPrincipalResourceProxySecretName: agentResourceProxyTLSSecretName, - argocdagent.EnvArgoCDPrincipalResourceProxyCaSecretName: agentRootCASecretName, - argocdagent.EnvArgoCDPrincipalJwtSecretName: agentJWTSecretName, - argocdagent.EnvArgoCDPrincipalLabelSelector: "argocd-agent=true", + argocdagent.EnvArgoCDPrincipalLogLevel: "info", + argocdagent.EnvArgoCDPrincipalNamespace: ns.Name, + argocdagent.EnvArgoCDPrincipalAllowedNamespaces: "*", + argocdagent.EnvArgoCDPrincipalNamespaceCreateEnable: "false", + argocdagent.EnvArgoCDPrincipalNamespaceCreatePattern: "", + argocdagent.EnvArgoCDPrincipalNamespaceCreateLabels: "", + argocdagent.EnvArgoCDPrincipalTLSServerAllowGenerate: "true", + argocdagent.EnvArgoCDPrincipalJWTAllowGenerate: "true", + argocdagent.EnvArgoCDPrincipalAuth: "mtls:CN=([^,]+)", + argocdagent.EnvArgoCDPrincipalEnableResourceProxy: "true", + argocdagent.EnvArgoCDPrincipalKeepAliveMinInterval: "30s", + argocdagent.EnvArgoCDPrincipalRedisServerAddress: fmt.Sprintf("%s-%s:%d", argoCDName, "redis", common.ArgoCDDefaultRedisPort), + argocdagent.EnvArgoCDPrincipalRedisCompressionType: "gzip", + argocdagent.EnvArgoCDPrincipalLogFormat: "text", + argocdagent.EnvArgoCDPrincipalEnableWebSocket: "false", + argocdagent.EnvArgoCDPrincipalTLSSecretName: agentPrincipalTLSSecretName, + argocdagent.EnvArgoCDPrincipalTLSServerRootCASecretName: agentRootCASecretName, + argocdagent.EnvArgoCDPrincipalResourceProxySecretName: agentResourceProxyTLSSecretName, + argocdagent.EnvArgoCDPrincipalResourceProxyCaSecretName: agentRootCASecretName, + argocdagent.EnvArgoCDPrincipalJwtSecretName: agentJWTSecretName, + argocdagent.EnvArgoCDPrincipalLabelSelector: "argocd-agent=true", + argocdagent.EnvArgoCDPrincipalEnableSelfClusterRegistration: "false", + argocdagent.EnvArgoCDPrincipalSelfRegistrationClientCertSecret: "", + argocdagent.EnvArgoCDPrincipalResourceProxyAddress: fmt.Sprintf("%s-agent-principal-resource-proxy:%d", argoCDName, argocdagent.PrincipalResourceProxyServicePort), } principalResources = agentFixture.PrincipalResources{ @@ -1021,5 +1024,65 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { Consistently(clusterRole, "10s", "2s").Should(k8sFixture.NotExistByName()) Consistently(clusterRoleBinding, "10s", "2s").Should(k8sFixture.NotExistByName()) }) + + It("should propagate self-registration configuration to principal deployment env vars", func() { + By("Create ArgoCD instance with self-registration enabled") + + argoCD.Spec.ArgoCDAgent.Principal.SelfRegistration = &argov1beta1api.PrincipalSelfRegistrationSpec{ + Enabled: new(true), + ClientCertSecretName: "argocd-agent-shared-client-cert", + } + Expect(k8sClient.Create(ctx, argoCD)).To(Succeed()) + + By("Verify expected resources are created for principal pod") + + verifyExpectedResourcesExist(ns) + + By("Verify self-registration environment variables are set correctly") + + container := deploymentFixture.GetTemplateSpecContainerByName(argoCDAgentPrincipalName, *principalDeployment) + Expect(container).ToNot(BeNil()) + + expectedEnvVariables[argocdagent.EnvArgoCDPrincipalEnableSelfClusterRegistration] = "true" + expectedEnvVariables[argocdagent.EnvArgoCDPrincipalSelfRegistrationClientCertSecret] = "argocd-agent-shared-client-cert" + + for key, value := range expectedEnvVariables { + Expect(container.Env).To(ContainElement(corev1.EnvVar{Name: key, Value: value}), "Environment variable %s should be set to %s", key, value) + } + + By("Update self-registration configuration: disable and change client cert secret") + + Expect(k8sClient.Get(ctx, client.ObjectKey{Name: argoCDName, Namespace: ns.Name}, argoCD)).To(Succeed()) + argocdFixture.Update(argoCD, func(ac *argov1beta1api.ArgoCD) { + ac.Spec.ArgoCDAgent.Principal.SelfRegistration.Enabled = new(false) + ac.Spec.ArgoCDAgent.Principal.SelfRegistration.ClientCertSecretName = "updated-client-cert" + }) + + By("Wait for the client cert secret update to propagate, then verify all env vars") + + Eventually(func() bool { + err := k8sClient.Get(ctx, client.ObjectKey{Name: argoCDAgentPrincipalName, Namespace: ns.Name}, principalDeployment) + if err != nil { + return false + } + container = deploymentFixture.GetTemplateSpecContainerByName(argoCDAgentPrincipalName, *principalDeployment) + if container == nil { + return false + } + for _, env := range container.Env { + if env.Name == argocdagent.EnvArgoCDPrincipalSelfRegistrationClientCertSecret { + return env.Value == "updated-client-cert" + } + } + return false + }, "120s", "5s").Should(BeTrue(), "Self-registration client cert secret should be updated") + + expectedEnvVariables[argocdagent.EnvArgoCDPrincipalEnableSelfClusterRegistration] = "false" + expectedEnvVariables[argocdagent.EnvArgoCDPrincipalSelfRegistrationClientCertSecret] = "updated-client-cert" + + for key, value := range expectedEnvVariables { + Expect(container.Env).To(ContainElement(corev1.EnvVar{Name: key, Value: value}), "Environment variable %s should be set to %s", key, value) + } + }) }) }) diff --git a/bundle/manifests/argoproj.io_argocds.yaml b/bundle/manifests/argoproj.io_argocds.yaml index dc6d750f00e..69eed58a035 100644 --- a/bundle/manifests/argoproj.io_argocds.yaml +++ b/bundle/manifests/argoproj.io_argocds.yaml @@ -1022,6 +1022,21 @@ spec: the TLS certificate and key for the resource proxy. type: string type: object + selfRegistration: + description: SelfRegistration defines the self-registration + options for the Principal component. + properties: + clientCertSecretName: + description: |- + ClientCertSecretName is the name of the TLS secret containing shared client cert + for self-registered cluster secrets (must have tls.crt, tls.key, ca.crt). + type: string + enabled: + description: |- + Enabled is the flag to enable self-registration of agents. + When enabled, agents with valid credentials can automatically register on connection. + type: boolean + type: object server: description: Server defines the server options for the Principal component. @@ -14183,6 +14198,21 @@ spec: More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object + selfRegistration: + description: SelfRegistration defines the self-registration + options for the Principal component. + properties: + clientCertSecretName: + description: |- + ClientCertSecretName is the name of the TLS secret containing shared client cert + for self-registered cluster secrets (must have tls.crt, tls.key, ca.crt). + type: string + enabled: + description: |- + Enabled is the flag to enable self-registration of agents. + When enabled, agents with valid credentials can automatically register on connection. + type: boolean + type: object server: description: Server defines the server options for the Principal component. diff --git a/config/crd/bases/argoproj.io_argocds.yaml b/config/crd/bases/argoproj.io_argocds.yaml index 40fe001b11d..ed34dea9a2b 100644 --- a/config/crd/bases/argoproj.io_argocds.yaml +++ b/config/crd/bases/argoproj.io_argocds.yaml @@ -1011,6 +1011,21 @@ spec: the TLS certificate and key for the resource proxy. type: string type: object + selfRegistration: + description: SelfRegistration defines the self-registration + options for the Principal component. + properties: + clientCertSecretName: + description: |- + ClientCertSecretName is the name of the TLS secret containing shared client cert + for self-registered cluster secrets (must have tls.crt, tls.key, ca.crt). + type: string + enabled: + description: |- + Enabled is the flag to enable self-registration of agents. + When enabled, agents with valid credentials can automatically register on connection. + type: boolean + type: object server: description: Server defines the server options for the Principal component. @@ -14172,6 +14187,21 @@ spec: More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object + selfRegistration: + description: SelfRegistration defines the self-registration + options for the Principal component. + properties: + clientCertSecretName: + description: |- + ClientCertSecretName is the name of the TLS secret containing shared client cert + for self-registered cluster secrets (must have tls.crt, tls.key, ca.crt). + type: string + enabled: + description: |- + Enabled is the flag to enable self-registration of agents. + When enabled, agents with valid credentials can automatically register on connection. + type: boolean + type: object server: description: Server defines the server options for the Principal component.