From c543482c9a1fb916b674a672a2dff573e2d1d05b Mon Sep 17 00:00:00 2001 From: Philipp Kolberg Date: Mon, 6 Apr 2026 18:40:39 +0200 Subject: [PATCH 1/2] feat: allow skipping external-dns target updates --- internal/controller/servicesync_controller.go | 6 ++++- .../controller/servicesync_controller_test.go | 25 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/internal/controller/servicesync_controller.go b/internal/controller/servicesync_controller.go index 538bd48..8f40b51 100644 --- a/internal/controller/servicesync_controller.go +++ b/internal/controller/servicesync_controller.go @@ -59,6 +59,10 @@ const ( // (and historical) prefix with this suffix, instead of inferring it from the // Service's currently assigned IP. AnnotationSuffix = "dynamic-prefix.io/suffix" + + // AnnotationNoExternalDNSTargetUpdate disables managed updates of the + // external-dns target annotation in HA mode. + AnnotationNoExternalDNSTargetUpdate = "dynamic-prefix.io/no-external-dns-target-update" ) // ServiceSyncReconciler reconciles LoadBalancer Services for HA mode prefix transitions. @@ -167,7 +171,7 @@ func (r *ServiceSyncReconciler) Reconcile(ctx context.Context, req ctrl.Request) preservedTargets := extractUnmanagedIPs(existingTarget, managedPrefixes) finalTargets := append(preservedTargets, currentIP) finalTargetStr := strings.Join(finalTargets, ",") - if annotations[AnnotationExternalDNSTarget] != finalTargetStr { + if annotations[AnnotationNoExternalDNSTargetUpdate] != "true" && annotations[AnnotationExternalDNSTarget] != finalTargetStr { newAnnotations[AnnotationExternalDNSTarget] = finalTargetStr updated = true } diff --git a/internal/controller/servicesync_controller_test.go b/internal/controller/servicesync_controller_test.go index 83ef5c8..f2ce8ed 100644 --- a/internal/controller/servicesync_controller_test.go +++ b/internal/controller/servicesync_controller_test.go @@ -243,6 +243,26 @@ var _ = Describe("ServiceSync Controller", func() { Expect(ipsAnnotation).To(ContainSubstring("2001:db8:1::beef:42")) Expect(ipsAnnotation).To(ContainSubstring("2001:db8:2::beef:42")) }) + + It("should skip external-dns target updates when explicitly disabled", func() { + svc := &corev1.Service{} + Expect(k8sClient.Get(ctx, types.NamespacedName{Name: serviceName, Namespace: serviceNS}, svc)).To(Succeed()) + + annotations := svc.GetAnnotations() + annotations[AnnotationNoExternalDNSTargetUpdate] = "true" + annotations[AnnotationExternalDNSTarget] = "existing.example.com" + svc.SetAnnotations(annotations) + Expect(k8sClient.Update(ctx, svc)).To(Succeed()) + + reconciler := &ServiceSyncReconciler{Client: k8sClient, Scheme: k8sClient.Scheme()} + _, err := reconciler.Reconcile(ctx, reconcile.Request{NamespacedName: types.NamespacedName{Name: serviceName, Namespace: serviceNS}}) + Expect(err).NotTo(HaveOccurred()) + + Expect(k8sClient.Get(ctx, types.NamespacedName{Name: serviceName, Namespace: serviceNS}, svc)).To(Succeed()) + Expect(svc.GetAnnotations()[AnnotationExternalDNSTarget]).To(Equal("existing.example.com")) + Expect(svc.GetAnnotations()[AnnotationCiliumIPs]).To(ContainSubstring(currentIP)) + Expect(svc.GetAnnotations()[AnnotationCiliumIPs]).To(ContainSubstring(historicalIP)) + }) }) Context("When reconciling a Service in simple mode", func() { @@ -812,6 +832,11 @@ func TestServiceSyncAnnotationConstants(t *testing.T) { constant: AnnotationSuffix, expected: "dynamic-prefix.io/suffix", }, + { + name: "AnnotationNoExternalDNSTargetUpdate", + constant: AnnotationNoExternalDNSTargetUpdate, + expected: "dynamic-prefix.io/no-external-dns-target-update", + }, } for _, tt := range tests { From 44cc33d4eed21c592da6b20a5866ee1afc84937a Mon Sep 17 00:00:00 2001 From: Philipp Kolberg Date: Mon, 6 Apr 2026 19:13:56 +0200 Subject: [PATCH 2/2] refactor: rename skip external-dns annotation --- internal/controller/servicesync_controller.go | 6 +++--- internal/controller/servicesync_controller_test.go | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/controller/servicesync_controller.go b/internal/controller/servicesync_controller.go index 8f40b51..be4e879 100644 --- a/internal/controller/servicesync_controller.go +++ b/internal/controller/servicesync_controller.go @@ -60,9 +60,9 @@ const ( // Service's currently assigned IP. AnnotationSuffix = "dynamic-prefix.io/suffix" - // AnnotationNoExternalDNSTargetUpdate disables managed updates of the + // AnnotationSkipExternalDNSUpdate disables managed updates of the // external-dns target annotation in HA mode. - AnnotationNoExternalDNSTargetUpdate = "dynamic-prefix.io/no-external-dns-target-update" + AnnotationSkipExternalDNSUpdate = "dynamic-prefix.io/skip-external-dns-update" ) // ServiceSyncReconciler reconciles LoadBalancer Services for HA mode prefix transitions. @@ -171,7 +171,7 @@ func (r *ServiceSyncReconciler) Reconcile(ctx context.Context, req ctrl.Request) preservedTargets := extractUnmanagedIPs(existingTarget, managedPrefixes) finalTargets := append(preservedTargets, currentIP) finalTargetStr := strings.Join(finalTargets, ",") - if annotations[AnnotationNoExternalDNSTargetUpdate] != "true" && annotations[AnnotationExternalDNSTarget] != finalTargetStr { + if annotations[AnnotationSkipExternalDNSUpdate] != "true" && annotations[AnnotationExternalDNSTarget] != finalTargetStr { newAnnotations[AnnotationExternalDNSTarget] = finalTargetStr updated = true } diff --git a/internal/controller/servicesync_controller_test.go b/internal/controller/servicesync_controller_test.go index f2ce8ed..8cc088d 100644 --- a/internal/controller/servicesync_controller_test.go +++ b/internal/controller/servicesync_controller_test.go @@ -249,7 +249,7 @@ var _ = Describe("ServiceSync Controller", func() { Expect(k8sClient.Get(ctx, types.NamespacedName{Name: serviceName, Namespace: serviceNS}, svc)).To(Succeed()) annotations := svc.GetAnnotations() - annotations[AnnotationNoExternalDNSTargetUpdate] = "true" + annotations[AnnotationSkipExternalDNSUpdate] = "true" annotations[AnnotationExternalDNSTarget] = "existing.example.com" svc.SetAnnotations(annotations) Expect(k8sClient.Update(ctx, svc)).To(Succeed()) @@ -833,9 +833,9 @@ func TestServiceSyncAnnotationConstants(t *testing.T) { expected: "dynamic-prefix.io/suffix", }, { - name: "AnnotationNoExternalDNSTargetUpdate", - constant: AnnotationNoExternalDNSTargetUpdate, - expected: "dynamic-prefix.io/no-external-dns-target-update", + name: "AnnotationSkipExternalDNSUpdate", + constant: AnnotationSkipExternalDNSUpdate, + expected: "dynamic-prefix.io/skip-external-dns-update", }, }