Skip to content
Draft
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
18 changes: 18 additions & 0 deletions argocd-operator/api/v1alpha1/argocd_conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,9 @@ func TestAlphaToBetaConversion(t *testing.T) {
Server: &PrincipalServerSpec{
Service: ArgoCDAgentPrincipalServiceSpec{
Type: corev1.ServiceTypeClusterIP,
Annotations: map[string]string{
"metallb.universe.tf/address-pool": "address-pool",
},
},
Route: ArgoCDAgentPrincipalRouteSpec{
Enabled: new(true),
Expand All @@ -548,6 +551,9 @@ func TestAlphaToBetaConversion(t *testing.T) {
Server: &v1beta1.PrincipalServerSpec{
Service: v1beta1.ArgoCDAgentPrincipalServiceSpec{
Type: corev1.ServiceTypeClusterIP,
Annotations: map[string]string{
"metallb.universe.tf/address-pool": "address-pool",
},
},
Route: v1beta1.ArgoCDAgentPrincipalRouteSpec{
Enabled: new(true),
Expand Down Expand Up @@ -940,6 +946,9 @@ func TestBetaToAlphaConversion(t *testing.T) {
Server: &v1beta1.PrincipalServerSpec{
Service: v1beta1.ArgoCDAgentPrincipalServiceSpec{
Type: corev1.ServiceTypeNodePort,
Annotations: map[string]string{
"metallb.universe.tf/address-pool": "address-pool",
},
},
Route: v1beta1.ArgoCDAgentPrincipalRouteSpec{
Enabled: new(true),
Expand All @@ -957,6 +966,9 @@ func TestBetaToAlphaConversion(t *testing.T) {
Server: &PrincipalServerSpec{
Service: ArgoCDAgentPrincipalServiceSpec{
Type: corev1.ServiceTypeNodePort,
Annotations: map[string]string{
"metallb.universe.tf/address-pool": "address-pool",
},
},
Route: ArgoCDAgentPrincipalRouteSpec{
Enabled: new(true),
Expand Down Expand Up @@ -990,6 +1002,9 @@ func TestBetaToAlphaConversion(t *testing.T) {
KeepAliveMinInterval: "30s",
Service: v1beta1.ArgoCDAgentPrincipalServiceSpec{
Type: corev1.ServiceTypeExternalName,
Annotations: map[string]string{
"metallb.universe.tf/address-pool": "address-pool",
},
},
Route: v1beta1.ArgoCDAgentPrincipalRouteSpec{
Enabled: new(false),
Expand Down Expand Up @@ -1043,6 +1058,9 @@ func TestBetaToAlphaConversion(t *testing.T) {
KeepAliveMinInterval: "30s",
Service: ArgoCDAgentPrincipalServiceSpec{
Type: corev1.ServiceTypeExternalName,
Annotations: map[string]string{
"metallb.universe.tf/address-pool": "address-pool",
},
},
Route: ArgoCDAgentPrincipalRouteSpec{
Enabled: new(false),
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 @@ -1365,6 +1365,9 @@ type ArgoCDAgentPrincipalServiceSpec struct {
// Type is the ServiceType to use for the Service resource.
// If not set, type ClusterIP will be used by default.
Type corev1.ServiceType `json:"type"`

// Annotations is the map of annotations to apply to the Service.
Annotations map[string]string `json:"annotations,omitempty"`
}

// ArgoCDAgentPrincipalRouteSpec defines the options for the Route backing the ArgoCD Agent Principal component.
Expand Down
9 changes: 8 additions & 1 deletion 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 @@ -1562,6 +1562,9 @@ type ArgoCDAgentPrincipalServiceSpec struct {
// Type is the ServiceType to use for the Service resource.
// If not set, type ClusterIP will be used by default.
Type corev1.ServiceType `json:"type"`

// Annotations is the map of annotations to apply to the Service.
Annotations map[string]string `json:"annotations,omitempty"`
}

// ArgoCDAgentPrincipalRouteSpec defines the options for the Route backing the ArgoCD Agent Principal component.
Expand Down
9 changes: 8 additions & 1 deletion 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.

12 changes: 12 additions & 0 deletions argocd-operator/bundle/manifests/argoproj.io_argocds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1051,6 +1051,12 @@ spec:
Service defines the options for the Service backing the ArgoCD Agent component.
If not set, type ClusterIP will be used by default.
properties:
annotations:
additionalProperties:
type: string
description: Annotations is the map of annotations
to apply to the Service.
type: object
type:
description: |-
Type is the ServiceType to use for the Service resource.
Expand Down Expand Up @@ -14212,6 +14218,12 @@ spec:
Service defines the options for the Service backing the ArgoCD Agent component.
If not set, type ClusterIP will be used by default.
properties:
annotations:
additionalProperties:
type: string
description: Annotations is the map of annotations
to apply to the Service.
type: object
type:
description: |-
Type is the ServiceType to use for the Service resource.
Expand Down
5 changes: 5 additions & 0 deletions argocd-operator/common/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,9 @@ const (
// AnnotationOpenShiftOriginatingServiceName is the annotation on secrets used to
// identify the service that created the secret.
AnnotationOpenShiftOriginatingServiceName = "service.beta.openshift.io/originating-service-name"

// AnnotationOwnedPrincipalServiceAnnotations is the annotation on the principal
// service that tracks which annotation keys were applied by the operator from the
// ArgoCD CR spec, so only those keys are removed when the spec changes.
AnnotationOwnedPrincipalServiceAnnotations = "argocds.argoproj.io/owned-principal-service-annotations"
)
12 changes: 12 additions & 0 deletions argocd-operator/config/crd/bases/argoproj.io_argocds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1040,6 +1040,12 @@ spec:
Service defines the options for the Service backing the ArgoCD Agent component.
If not set, type ClusterIP will be used by default.
properties:
annotations:
additionalProperties:
type: string
description: Annotations is the map of annotations
to apply to the Service.
type: object
type:
description: |-
Type is the ServiceType to use for the Service resource.
Expand Down Expand Up @@ -14201,6 +14207,12 @@ spec:
Service defines the options for the Service backing the ArgoCD Agent component.
If not set, type ClusterIP will be used by default.
properties:
annotations:
additionalProperties:
type: string
description: Annotations is the map of annotations
to apply to the Service.
type: object
type:
description: |-
Type is the ServiceType to use for the Service resource.
Expand Down
91 changes: 91 additions & 0 deletions argocd-operator/controllers/argocdagent/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import (
"context"
"fmt"
"reflect"
"sort"
"strings"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
Expand Down Expand Up @@ -91,14 +93,22 @@ func ReconcilePrincipalService(client client.Client, compName string, cr *argopr
return nil
}

needsUpdate := false
if !reflect.DeepEqual(service.Spec.Ports, expectedSpec.Ports) ||
!reflect.DeepEqual(service.Spec.Selector, expectedSpec.Selector) ||
!reflect.DeepEqual(service.Spec.Type, expectedSpec.Type) {

service.Spec.Type = expectedSpec.Type
service.Spec.Ports = expectedSpec.Ports
service.Spec.Selector = expectedSpec.Selector
needsUpdate = true
}

if reconcilePrincipalServiceAnnotations(service, desiredPrincipalServiceAnnotations(cr)) {
needsUpdate = true
}

if needsUpdate {
argoutil.LogResourceUpdate(log, service, "updating principal service spec")
if err := client.Update(context.TODO(), service); err != nil {
return fmt.Errorf("failed to update principal service %s: %v", service.Name, err)
Expand All @@ -119,6 +129,7 @@ func ReconcilePrincipalService(client client.Client, compName string, cr *argopr
service.Spec.Type = expectedSpec.Type
service.Spec.Ports = expectedSpec.Ports
service.Spec.Selector = expectedSpec.Selector
reconcilePrincipalServiceAnnotations(service, desiredPrincipalServiceAnnotations(cr))

argoutil.LogResourceCreation(log, service)
if err := client.Create(context.TODO(), service); err != nil {
Expand Down Expand Up @@ -375,6 +386,50 @@ func ReconcilePrincipalHealthzService(client client.Client, compName string, cr
return nil
}

// reconcilePrincipalServiceAnnotations adds, updates, or removes operator-managed annotations on
// the principal service. Only annotation keys previously applied by the operator are removed
// when they are no longer present in the CR spec; other annotations on the service are preserved.
// Returns true when the service annotations were modified.
func reconcilePrincipalServiceAnnotations(svc *corev1.Service, desired map[string]string) bool {
if desired == nil {
desired = map[string]string{}
}
if svc.Annotations == nil {
svc.Annotations = make(map[string]string)
}

previouslyOwned := parseOwnedAnnotationKeys(svc.Annotations[common.AnnotationOwnedPrincipalServiceAnnotations])

changed := false
for _, key := range previouslyOwned {
if _, stillDesired := desired[key]; !stillDesired {
if _, exists := svc.Annotations[key]; exists {
delete(svc.Annotations, key)
changed = true
}
}
}

for key, value := range desired {
if svc.Annotations[key] != value {
svc.Annotations[key] = value
changed = true
}
}

newOwnedValue := formatOwnedAnnotationKeys(desired)
if svc.Annotations[common.AnnotationOwnedPrincipalServiceAnnotations] != newOwnedValue {
if newOwnedValue == "" {
delete(svc.Annotations, common.AnnotationOwnedPrincipalServiceAnnotations)
} else {
svc.Annotations[common.AnnotationOwnedPrincipalServiceAnnotations] = newOwnedValue
}
changed = true
}

return changed
}

func buildPrincipalServiceSpec(compName string, cr *argoproj.ArgoCD) corev1.ServiceSpec {
return corev1.ServiceSpec{
Ports: []corev1.ServicePort{
Expand Down Expand Up @@ -480,3 +535,39 @@ func getPrincipalServiceType(cr *argoproj.ArgoCD) corev1.ServiceType {
}
return corev1.ServiceTypeClusterIP
}

// desiredPrincipalServiceAnnotations returns the principal service annotations from the CR spec.
func desiredPrincipalServiceAnnotations(cr *argoproj.ArgoCD) map[string]string {
if !hasServer(cr) {
return nil
}
return cr.Spec.ArgoCDAgent.Principal.Server.Service.Annotations
}

func parseOwnedAnnotationKeys(owned string) []string {
if owned == "" {
return nil
}

keys := strings.Split(owned, ",")
result := make([]string, 0, len(keys))
for _, key := range keys {
if key != "" {
result = append(result, key)
}
}
return result
}

func formatOwnedAnnotationKeys(desired map[string]string) string {
if len(desired) == 0 {
return ""
}

keys := make([]string, 0, len(desired))
for key := range desired {
keys = append(keys, key)
}
sort.Strings(keys)
return strings.Join(keys, ",")
}
Loading
Loading