Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions argocd-operator/api/v1alpha1/argocd_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -1220,6 +1220,7 @@ func ConvertAlphaToBetaPrincipalSelfRegistration(src *PrincipalSelfRegistrationS
dst = &v1beta1.PrincipalSelfRegistrationSpec{
Enabled: src.Enabled,
ClientCertSecretName: src.ClientCertSecretName,
ClusterSecretLabels: src.ClusterSecretLabels,
}
}
return dst
Expand All @@ -1231,6 +1232,7 @@ func ConvertBetaToAlphaPrincipalSelfRegistration(src *v1beta1.PrincipalSelfRegis
dst = &PrincipalSelfRegistrationSpec{
Enabled: src.Enabled,
ClientCertSecretName: src.ClientCertSecretName,
ClusterSecretLabels: src.ClusterSecretLabels,
}
}
return dst
Expand Down
4 changes: 4 additions & 0 deletions argocd-operator/api/v1alpha1/argocd_conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,7 @@ func TestAlphaToBetaConversion(t *testing.T) {
SelfRegistration: &PrincipalSelfRegistrationSpec{
Enabled: &selfRegEnabled,
ClientCertSecretName: "argocd-agent-shared-client-cert",
ClusterSecretLabels: map[string]string{"foo": "bar"},
},
},
}
Expand All @@ -861,6 +862,7 @@ func TestAlphaToBetaConversion(t *testing.T) {
SelfRegistration: &v1beta1.PrincipalSelfRegistrationSpec{
Enabled: &selfRegEnabled,
ClientCertSecretName: "argocd-agent-shared-client-cert",
ClusterSecretLabels: map[string]string{"foo": "bar"},
},
},
}
Expand Down Expand Up @@ -1310,6 +1312,7 @@ func TestBetaToAlphaConversion(t *testing.T) {
SelfRegistration: &v1beta1.PrincipalSelfRegistrationSpec{
Enabled: &selfRegEnabled,
ClientCertSecretName: "argocd-agent-shared-client-cert",
ClusterSecretLabels: map[string]string{"foo": "bar"},
},
},
}
Expand All @@ -1323,6 +1326,7 @@ func TestBetaToAlphaConversion(t *testing.T) {
SelfRegistration: &PrincipalSelfRegistrationSpec{
Enabled: &selfRegEnabled,
ClientCertSecretName: "argocd-agent-shared-client-cert",
ClusterSecretLabels: map[string]string{"foo": "bar"},
},
},
}
Expand Down
3 changes: 3 additions & 0 deletions argocd-operator/api/v1alpha1/argocd_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1302,6 +1302,9 @@ type PrincipalSelfRegistrationSpec struct {
// 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"`

// ClusterSecretLabels is the set of labels to apply to cluster secrets created for agents. Ex: {"foo": "bar", "bar": "baz"}
ClusterSecretLabels map[string]string `json:"clusterSecretLabels,omitempty"`
}

type PrincipalServerSpec struct {
Expand Down
7 changes: 7 additions & 0 deletions argocd-operator/api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions argocd-operator/api/v1beta1/argocd_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1507,6 +1507,9 @@ type PrincipalSelfRegistrationSpec struct {
// 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"`

// ClusterSecretLabels is the set of labels to apply to cluster secrets created for agents. Ex: {"foo": "bar", "bar": "baz"}
ClusterSecretLabels map[string]string `json:"clusterSecretLabels,omitempty"`
}

type PrincipalServerSpec struct {
Expand Down
7 changes: 7 additions & 0 deletions argocd-operator/api/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions argocd-operator/controllers/argocdagent/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"fmt"
"os"
"reflect"
"sort"
"strconv"
"strings"

Expand Down Expand Up @@ -441,6 +442,9 @@ func buildPrincipalContainerEnv(cr *argoproj.ArgoCD, centralTLSProfile tlsProfil
}, {
Name: EnvArgoCDPrincipalSelfRegistrationClientCertSecret,
Value: getPrincipalSelfRegistrationClientCertSecret(cr),
}, {
Name: EnvArgoCDPrincipalSelfRegistrationSecretLabels,
Value: getPrincipalSelfRegistrationSecretLabels(cr),
}, {
Name: EnvArgoCDPrincipalResourceProxyAddress,
Value: getPrincipalResourceProxyAddress(cr),
Expand Down Expand Up @@ -487,6 +491,7 @@ const (
EnvArgoCDPrincipalCipherSuites = "ARGOCD_PRINCIPAL_TLS_CIPHERSUITES"
EnvArgoCDPrincipalEnableSelfClusterRegistration = "ARGOCD_PRINCIPAL_ENABLE_SELF_CLUSTER_REGISTRATION"
EnvArgoCDPrincipalSelfRegistrationClientCertSecret = "ARGOCD_PRINCIPAL_SELF_REGISTRATION_CLIENT_CERT_SECRET"
EnvArgoCDPrincipalSelfRegistrationSecretLabels = "ARGOCD_PRINCIPAL_SELF_REGISTRATION_SECRET_LABELS"
EnvArgoCDPrincipalResourceProxyAddress = "ARGOCD_PRINCIPAL_RESOURCE_PROXY_ADDRESS"
EnvArgoCDPrincipalTLSCurvePreferences = "ARGOCD_PRINCIPAL_TLS_CURVE_PREFERENCES"
)
Expand Down Expand Up @@ -755,6 +760,23 @@ func getPrincipalSelfRegistrationClientCertSecret(cr *argoproj.ArgoCD) string {
return ""
}

func getPrincipalSelfRegistrationSecretLabels(cr *argoproj.ArgoCD) string {
if !hasSelfRegistration(cr) || len(cr.Spec.ArgoCDAgent.Principal.SelfRegistration.ClusterSecretLabels) == 0 {
return ""
}
labels := cr.Spec.ArgoCDAgent.Principal.SelfRegistration.ClusterSecretLabels
keys := make([]string, 0, len(labels))
for k := range labels {
keys = append(keys, k)
}
sort.Strings(keys)
pairs := make([]string, 0, len(labels))
for _, k := range keys {
pairs = append(pairs, k+"="+labels[k])
}
return strings.Join(pairs, ",")
}

func getPrincipalResourceProxyAddress(cr *argoproj.ArgoCD) string {
return fmt.Sprintf("%s:%d",
generateAgentResourceName(cr.Name, string(argoproj.AgentComponentTypePrincipal)+"-resource-proxy"),
Expand Down
75 changes: 75 additions & 0 deletions argocd-operator/controllers/argocdagent/deployment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,21 @@ func withSelfRegistration(enabled bool, clientCertSecretName string) argoCDOpt {
}
}

func withSelfRegistrationClusterSecretLabels(labels map[string]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{}
}
if a.Spec.ArgoCDAgent.Principal.SelfRegistration == nil {
a.Spec.ArgoCDAgent.Principal.SelfRegistration = &argoproj.PrincipalSelfRegistrationSpec{}
}
a.Spec.ArgoCDAgent.Principal.SelfRegistration.ClusterSecretLabels = labels
}
}

func TestGetPrincipalEnableSelfClusterRegistration(t *testing.T) {
tests := []struct {
name string
Expand Down Expand Up @@ -1042,20 +1057,33 @@ func TestBuildPrincipalContainerEnv_SelfRegistration(t *testing.T) {
cr *argoproj.ArgoCD
expectedEnabled string
expectedClientCertSecret string
expectedSecretLabels string
expectedResourceProxyAddr string
}{
{
name: "defaults when self-registration not configured",
cr: makeTestArgoCD(withPrincipalEnabled(true)),
expectedEnabled: "false",
expectedClientCertSecret: "",
expectedSecretLabels: "",
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",
expectedSecretLabels: "",
expectedResourceProxyAddr: generateAgentResourceName(testArgoCDName, testCompName+"-resource-proxy") + ":9090",
},
{
name: "self-registration enabled with cluster secret labels",
cr: makeTestArgoCD(withPrincipalEnabled(true),
withSelfRegistration(true, "argocd-agent-shared-client-cert"),
withSelfRegistrationClusterSecretLabels(map[string]string{"foo": "bar"})),
expectedEnabled: "true",
expectedClientCertSecret: "argocd-agent-shared-client-cert",
expectedSecretLabels: "foo=bar",
expectedResourceProxyAddr: generateAgentResourceName(testArgoCDName, testCompName+"-resource-proxy") + ":9090",
},
}
Expand All @@ -1072,8 +1100,55 @@ func TestBuildPrincipalContainerEnv_SelfRegistration(t *testing.T) {
"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.expectedSecretLabels, envMap[EnvArgoCDPrincipalSelfRegistrationSecretLabels],
"ARGOCD_PRINCIPAL_SELF_REGISTRATION_SECRET_LABELS mismatch")
assert.Equal(t, tt.expectedResourceProxyAddr, envMap[EnvArgoCDPrincipalResourceProxyAddress],
"ARGOCD_PRINCIPAL_RESOURCE_PROXY_ADDRESS mismatch")
})
}
}

func TestGetPrincipalSelfRegistrationSecretLabels(t *testing.T) {
tests := []struct {
name string
cr *argoproj.ArgoCD
expected string
}{
{
name: "no self-registration configured",
cr: makeTestArgoCD(withPrincipalEnabled(true)),
expected: "",
},
{
name: "self-registration set but no cluster secret labels",
cr: makeTestArgoCD(withPrincipalEnabled(true), withSelfRegistration(true, "")),
expected: "",
},
{
name: "single label",
cr: makeTestArgoCD(withPrincipalEnabled(true),
withSelfRegistrationClusterSecretLabels(map[string]string{"foo": "bar"})),
expected: "foo=bar",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.expected, getPrincipalSelfRegistrationSecretLabels(tt.cr))
})
}
}

func TestGetPrincipalSelfRegistrationSecretLabels_MultipleLabels(t *testing.T) {
cr := makeTestArgoCD(withPrincipalEnabled(true),
withSelfRegistrationClusterSecretLabels(map[string]string{
"env": "production",
"app": "argocd",
"team": "platform",
}))
result := getPrincipalSelfRegistrationSecretLabels(cr)
// Map iteration order is non-deterministic; verify each pair is present individually.
assert.Contains(t, result, "env=production")
assert.Contains(t, result, "app=argocd")
assert.Contains(t, result, "team=platform")
}
14 changes: 14 additions & 0 deletions bundle/manifests/argoproj.io_argocds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1031,6 +1031,13 @@ spec:
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
clusterSecretLabels:
additionalProperties:
type: string
description: 'ClusterSecretLabels is the set of labels
to apply to cluster secrets created for agents. Ex:
{"foo": "bar", "bar": "baz"}'
type: object
enabled:
description: |-
Enabled is the flag to enable self-registration of agents.
Expand Down Expand Up @@ -14218,6 +14225,13 @@ spec:
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
clusterSecretLabels:
additionalProperties:
type: string
description: 'ClusterSecretLabels is the set of labels
to apply to cluster secrets created for agents. Ex:
{"foo": "bar", "bar": "baz"}'
type: object
enabled:
description: |-
Enabled is the flag to enable self-registration of agents.
Expand Down
14 changes: 14 additions & 0 deletions config/crd/bases/argoproj.io_argocds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,13 @@ spec:
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
clusterSecretLabels:
additionalProperties:
type: string
description: 'ClusterSecretLabels is the set of labels
to apply to cluster secrets created for agents. Ex:
{"foo": "bar", "bar": "baz"}'
type: object
enabled:
description: |-
Enabled is the flag to enable self-registration of agents.
Expand Down Expand Up @@ -14207,6 +14214,13 @@ spec:
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
clusterSecretLabels:
additionalProperties:
type: string
description: 'ClusterSecretLabels is the set of labels
to apply to cluster secrets created for agents. Ex:
{"foo": "bar", "bar": "baz"}'
type: object
enabled:
description: |-
Enabled is the flag to enable self-registration of agents.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1051,11 +1051,12 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() {
})

It("should propagate self-registration configuration to principal deployment env vars", func() {
By("Create ArgoCD instance with self-registration enabled")
By("Create ArgoCD instance with self-registration enabled and cluster secret labels")

argoCD.Spec.ArgoCDAgent.Principal.SelfRegistration = &argov1beta1api.PrincipalSelfRegistrationSpec{
Enabled: new(true),
ClientCertSecretName: "argocd-agent-shared-client-cert",
ClusterSecretLabels: map[string]string{"env": "production", "team": "platform"},
}
Expect(k8sClient.Create(ctx, argoCD)).To(Succeed())

Expand All @@ -1075,12 +1076,25 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() {
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")
By("Verify cluster secret labels env var contains all expected key=value pairs")

var secretLabelsEnvValue string
for _, env := range container.Env {
if env.Name == argocdagent.EnvArgoCDPrincipalSelfRegistrationSecretLabels {
secretLabelsEnvValue = env.Value
break
}
}
Expect(secretLabelsEnvValue).To(ContainSubstring("env=production"), "Cluster secret labels should contain env=production")
Expect(secretLabelsEnvValue).To(ContainSubstring("team=platform"), "Cluster secret labels should contain team=platform")

By("Update self-registration configuration: disable, change client cert secret, and update labels")

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"
ac.Spec.ArgoCDAgent.Principal.SelfRegistration.ClusterSecretLabels = map[string]string{"foo": "bar"}
})

By("Wait for the client cert secret update to propagate, then verify all env vars")
Expand Down Expand Up @@ -1108,6 +1122,17 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() {
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("Verify updated cluster secret labels env var")

secretLabelsEnvValue = ""
for _, env := range container.Env {
if env.Name == argocdagent.EnvArgoCDPrincipalSelfRegistrationSecretLabels {
secretLabelsEnvValue = env.Value
break
}
}
Expect(secretLabelsEnvValue).To(Equal("foo=bar"), "Cluster secret labels should be updated to foo=bar")
})
})
})
Loading