diff --git a/argocd-operator/api/v1alpha1/argocd_conversion_test.go b/argocd-operator/api/v1alpha1/argocd_conversion_test.go index bdf5ce082d7..4fcdcb8802f 100644 --- a/argocd-operator/api/v1alpha1/argocd_conversion_test.go +++ b/argocd-operator/api/v1alpha1/argocd_conversion_test.go @@ -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), @@ -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), @@ -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), @@ -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), @@ -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), @@ -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), diff --git a/argocd-operator/api/v1alpha1/argocd_types.go b/argocd-operator/api/v1alpha1/argocd_types.go index 782061e16f5..a4e6901a3a4 100644 --- a/argocd-operator/api/v1alpha1/argocd_types.go +++ b/argocd-operator/api/v1alpha1/argocd_types.go @@ -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. diff --git a/argocd-operator/api/v1alpha1/zz_generated.deepcopy.go b/argocd-operator/api/v1alpha1/zz_generated.deepcopy.go index 2c2bea14a8f..ff4777b66d4 100644 --- a/argocd-operator/api/v1alpha1/zz_generated.deepcopy.go +++ b/argocd-operator/api/v1alpha1/zz_generated.deepcopy.go @@ -186,6 +186,13 @@ func (in *ArgoCDAgentPrincipalRouteSpec) DeepCopy() *ArgoCDAgentPrincipalRouteSp // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ArgoCDAgentPrincipalServiceSpec) DeepCopyInto(out *ArgoCDAgentPrincipalServiceSpec) { *out = *in + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArgoCDAgentPrincipalServiceSpec. @@ -1801,7 +1808,7 @@ func (in *PrincipalServerSpec) DeepCopyInto(out *PrincipalServerSpec) { *out = new(bool) **out = **in } - out.Service = in.Service + in.Service.DeepCopyInto(&out.Service) in.Route.DeepCopyInto(&out.Route) } diff --git a/argocd-operator/api/v1beta1/argocd_types.go b/argocd-operator/api/v1beta1/argocd_types.go index 93863ca768c..e01aac3fe12 100644 --- a/argocd-operator/api/v1beta1/argocd_types.go +++ b/argocd-operator/api/v1beta1/argocd_types.go @@ -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. diff --git a/argocd-operator/api/v1beta1/zz_generated.deepcopy.go b/argocd-operator/api/v1beta1/zz_generated.deepcopy.go index a8daa076a82..acb1c15a185 100644 --- a/argocd-operator/api/v1beta1/zz_generated.deepcopy.go +++ b/argocd-operator/api/v1beta1/zz_generated.deepcopy.go @@ -201,6 +201,13 @@ func (in *ArgoCDAgentPrincipalRouteSpec) DeepCopy() *ArgoCDAgentPrincipalRouteSp // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ArgoCDAgentPrincipalServiceSpec) DeepCopyInto(out *ArgoCDAgentPrincipalServiceSpec) { *out = *in + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArgoCDAgentPrincipalServiceSpec. @@ -2015,7 +2022,7 @@ func (in *PrincipalServerSpec) DeepCopyInto(out *PrincipalServerSpec) { *out = new(bool) **out = **in } - out.Service = in.Service + in.Service.DeepCopyInto(&out.Service) in.Route.DeepCopyInto(&out.Route) } diff --git a/argocd-operator/bundle/manifests/argoproj.io_argocds.yaml b/argocd-operator/bundle/manifests/argoproj.io_argocds.yaml index 139972b1867..7ef2cde99df 100644 --- a/argocd-operator/bundle/manifests/argoproj.io_argocds.yaml +++ b/argocd-operator/bundle/manifests/argoproj.io_argocds.yaml @@ -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. @@ -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. diff --git a/argocd-operator/common/annotations.go b/argocd-operator/common/annotations.go index 9a7ff3377c7..628752c1300 100644 --- a/argocd-operator/common/annotations.go +++ b/argocd-operator/common/annotations.go @@ -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" ) diff --git a/argocd-operator/config/crd/bases/argoproj.io_argocds.yaml b/argocd-operator/config/crd/bases/argoproj.io_argocds.yaml index 40fe001b11d..93cea5b4b06 100644 --- a/argocd-operator/config/crd/bases/argoproj.io_argocds.yaml +++ b/argocd-operator/config/crd/bases/argoproj.io_argocds.yaml @@ -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. @@ -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. diff --git a/argocd-operator/controllers/argocdagent/service.go b/argocd-operator/controllers/argocdagent/service.go index f0e307520ee..72a05ae52d1 100644 --- a/argocd-operator/controllers/argocdagent/service.go +++ b/argocd-operator/controllers/argocdagent/service.go @@ -18,6 +18,8 @@ import ( "context" "fmt" "reflect" + "sort" + "strings" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -91,6 +93,7 @@ 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) { @@ -98,7 +101,14 @@ func ReconcilePrincipalService(client client.Client, compName string, cr *argopr 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) @@ -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 { @@ -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{ @@ -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, ",") +} diff --git a/argocd-operator/controllers/argocdagent/service_test.go b/argocd-operator/controllers/argocdagent/service_test.go index 3c9451b3aea..aaa9260c361 100644 --- a/argocd-operator/controllers/argocdagent/service_test.go +++ b/argocd-operator/controllers/argocdagent/service_test.go @@ -1436,6 +1436,128 @@ func withServiceType(serviceType corev1.ServiceType) argoCDOpt { } } +func withServiceAnnotations(annotations map[string]string) argoCDOpt { + return func(a *argoproj.ArgoCD) { + if a.Spec.ArgoCDAgent.Principal.Server == nil { + a.Spec.ArgoCDAgent.Principal.Server = &argoproj.PrincipalServerSpec{} + } + a.Spec.ArgoCDAgent.Principal.Server.Service.Annotations = annotations + } +} + +func TestReconcilePrincipalService_ServiceAnnotations_Set(t *testing.T) { + annotations := map[string]string{ + "metallb.universe.tf/address-pool": "address-pool", + } + cr := makeTestArgoCD(withPrincipalEnabled(true), withServiceAnnotations(annotations)) + + resObjs := []client.Object{cr} + sch := makeTestReconcilerScheme() + cl := makeTestReconcilerClient(sch, resObjs) + + err := ReconcilePrincipalService(cl, testCompName, cr, sch) + assert.NoError(t, err) + + svc := &corev1.Service{} + err = cl.Get(context.TODO(), types.NamespacedName{ + Name: generateAgentResourceName(cr.Name, testCompName), + Namespace: testNamespace, + }, svc) + assert.NoError(t, err) + assert.Equal(t, "address-pool", svc.Annotations["metallb.universe.tf/address-pool"]) + assert.Equal(t, "metallb.universe.tf/address-pool", svc.Annotations[common.AnnotationOwnedPrincipalServiceAnnotations]) +} + +func TestReconcilePrincipalService_ServiceAnnotations_RemoveOneOfMany(t *testing.T) { + cr := makeTestArgoCD(withPrincipalEnabled(true), withServiceAnnotations(map[string]string{ + "a.example.io/key": "1", + "b.example.io/key": "2", + "c.example.io/key": "3", + })) + + resObjs := []client.Object{cr} + sch := makeTestReconcilerScheme() + cl := makeTestReconcilerClient(sch, resObjs) + + err := ReconcilePrincipalService(cl, testCompName, cr, sch) + assert.NoError(t, err) + + cr.Spec.ArgoCDAgent.Principal.Server.Service.Annotations = map[string]string{ + "a.example.io/key": "1", + "b.example.io/key": "2", + } + err = ReconcilePrincipalService(cl, testCompName, cr, sch) + assert.NoError(t, err) + + svc := &corev1.Service{} + err = cl.Get(context.TODO(), types.NamespacedName{ + Name: generateAgentResourceName(cr.Name, testCompName), + Namespace: testNamespace, + }, svc) + assert.NoError(t, err) + assert.Equal(t, "1", svc.Annotations["a.example.io/key"]) + assert.Equal(t, "2", svc.Annotations["b.example.io/key"]) + assert.NotContains(t, svc.Annotations, "c.example.io/key") + assert.Equal(t, "a.example.io/key,b.example.io/key", svc.Annotations[common.AnnotationOwnedPrincipalServiceAnnotations]) +} + +func TestReconcilePrincipalService_ServiceAnnotations_PreserveThirdPartyOnClear(t *testing.T) { + cr := makeTestArgoCD(withPrincipalEnabled(true), withServiceAnnotations(map[string]string{ + "a.example.io/key": "1", + })) + + existingSvc := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: generateAgentResourceName(cr.Name, testCompName), + Namespace: testNamespace, + Labels: buildLabelsForAgentPrincipal(cr.Name, testCompName), + Annotations: map[string]string{ + "a.example.io/key": "1", + "external.example.io/key": "keep-me", + common.AnnotationOwnedPrincipalServiceAnnotations: "a.example.io/key", + }, + }, + Spec: buildPrincipalServiceSpec(testCompName, cr), + } + + resObjs := []client.Object{cr, existingSvc} + sch := makeTestReconcilerScheme() + cl := makeTestReconcilerClient(sch, resObjs) + + cr.Spec.ArgoCDAgent.Principal.Server.Service.Annotations = nil + err := ReconcilePrincipalService(cl, testCompName, cr, sch) + assert.NoError(t, err) + + svc := &corev1.Service{} + err = cl.Get(context.TODO(), types.NamespacedName{ + Name: generateAgentResourceName(cr.Name, testCompName), + Namespace: testNamespace, + }, svc) + assert.NoError(t, err) + assert.NotContains(t, svc.Annotations, "a.example.io/key") + assert.Equal(t, "keep-me", svc.Annotations["external.example.io/key"]) + assert.NotContains(t, svc.Annotations, common.AnnotationOwnedPrincipalServiceAnnotations) +} + +func TestReconcilePrincipalService_ServiceAnnotations_Default(t *testing.T) { + cr := makeTestArgoCD(withPrincipalEnabled(true)) + + resObjs := []client.Object{cr} + sch := makeTestReconcilerScheme() + cl := makeTestReconcilerClient(sch, resObjs) + + err := ReconcilePrincipalService(cl, testCompName, cr, sch) + assert.NoError(t, err) + + svc := &corev1.Service{} + err = cl.Get(context.TODO(), types.NamespacedName{ + Name: generateAgentResourceName(cr.Name, testCompName), + Namespace: testNamespace, + }, svc) + assert.NoError(t, err) + assert.Empty(t, svc.Annotations) +} + func TestReconcilePrincipalService_ServiceType_ClusterIP(t *testing.T) { // Test case: Service type is explicitly set to ClusterIP // Expected behavior: Should create service with ClusterIP type 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..7ef2cde99df 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 @@ -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. @@ -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. 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..fa1bf213362 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 @@ -682,6 +682,9 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { argoCD.Spec.ArgoCDAgent.Principal.Server.Service = argov1beta1api.ArgoCDAgentPrincipalServiceSpec{ Type: corev1.ServiceTypeLoadBalancer, + Annotations: map[string]string{ + "test.argocd.io/annotation": "test", + }, } Expect(k8sClient.Create(ctx, argoCD)).To(Succeed()) @@ -698,7 +701,68 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() { }, } Eventually(principalService).Should(k8sFixture.ExistByName()) - Expect(principalService.Spec.Type).To(Equal(corev1.ServiceTypeLoadBalancer)) + Eventually(principalService.Spec.Type).Should(Equal(corev1.ServiceTypeLoadBalancer)) + Eventually(func() map[string]string { + err := k8sClient.Get(ctx, client.ObjectKey{Name: argoCDAgentPrincipalName, Namespace: ns.Name}, principalService) + if err != nil { + return nil + } + return principalService.Annotations + }, "30s", "2s").Should(HaveKeyWithValue("test.argocd.io/annotation", "test")) + + By("Update service annotations") + 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.Server.Service.Annotations = map[string]string{ + "test.argocd.io/annotation": "test2", + } + }) + + By("Verify principal service has updated annotations") + + principalService = &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: argoCDAgentPrincipalName, + Namespace: ns.Name, + }, + } + Eventually(principalService).Should(k8sFixture.ExistByName()) + Eventually(func() map[string]string { + err := k8sClient.Get(ctx, client.ObjectKey{Name: argoCDAgentPrincipalName, Namespace: ns.Name}, principalService) + if err != nil { + return nil + } + return principalService.Annotations + }, "30s", "2s").Should(HaveKeyWithValue("test.argocd.io/annotation", "test2")) + + By("Remove service annotations") + 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.Server.Service = argov1beta1api.ArgoCDAgentPrincipalServiceSpec{ + Type: corev1.ServiceTypeLoadBalancer, + } + }) + + By("Verify principal service has no annotations") + + principalService = &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: argoCDAgentPrincipalName, + Namespace: ns.Name, + }, + } + + Eventually(principalService).Should(k8sFixture.ExistByName()) + Eventually(func() map[string]string { + err := k8sClient.Get(ctx, client.ObjectKey{Name: argoCDAgentPrincipalName, Namespace: ns.Name}, principalService) + if err != nil { + return nil + } + return principalService.Annotations + }, "30s", "2s").ShouldNot(HaveKey("test.argocd.io/annotation")) + }) It("should handle service type updates correctly", func() { diff --git a/bundle/manifests/argoproj.io_argocds.yaml b/bundle/manifests/argoproj.io_argocds.yaml index dc6d750f00e..59949372ff0 100644 --- a/bundle/manifests/argoproj.io_argocds.yaml +++ b/bundle/manifests/argoproj.io_argocds.yaml @@ -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. @@ -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. diff --git a/config/crd/bases/argoproj.io_argocds.yaml b/config/crd/bases/argoproj.io_argocds.yaml index 40fe001b11d..93cea5b4b06 100644 --- a/config/crd/bases/argoproj.io_argocds.yaml +++ b/config/crd/bases/argoproj.io_argocds.yaml @@ -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. @@ -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.