From ba74177e6b8a47101beed4be316f9bb74dc08ea9 Mon Sep 17 00:00:00 2001 From: ivan katliarchuk Date: Sat, 31 Jan 2026 15:08:47 +0000 Subject: [PATCH 1/3] fix(annotations): allow resetting annotation prefix to default value Signed-off-by: ivan katliarchuk --- provider/cloudflare/cloudflare_test.go | 1 - source/annotations/annotations.go | 49 ++++++++++++++----------- source/annotations/annotations_test.go | 51 +++++++++++++++++++------- source/annotations/processors_test.go | 2 - source/service_test.go | 7 ---- 5 files changed, 64 insertions(+), 46 deletions(-) diff --git a/provider/cloudflare/cloudflare_test.go b/provider/cloudflare/cloudflare_test.go index d9b6de2eaf..8d76c1edd4 100644 --- a/provider/cloudflare/cloudflare_test.go +++ b/provider/cloudflare/cloudflare_test.go @@ -45,7 +45,6 @@ import ( // TestMain initializes annotation keys before running tests. // This is needed because init() was removed from annotations package. func TestMain(m *testing.M) { - annotations.SetAnnotationPrefix("external-dns.alpha.kubernetes.io/") m.Run() } diff --git a/source/annotations/annotations.go b/source/annotations/annotations.go index ebe4ba548c..95e62eb6e3 100644 --- a/source/annotations/annotations.go +++ b/source/annotations/annotations.go @@ -15,6 +15,7 @@ package annotations import ( "math" + "strings" ) const ( @@ -31,47 +32,51 @@ var ( AnnotationKeyPrefix = DefaultAnnotationPrefix // CloudflareProxiedKey The annotation used for determining if traffic will go through Cloudflare - CloudflareProxiedKey string - CloudflareCustomHostnameKey string - CloudflareRegionKey string - CloudflareRecordCommentKey string - CloudflareTagsKey string + CloudflareProxiedKey = AnnotationKeyPrefix + "cloudflare-proxied" + CloudflareCustomHostnameKey = AnnotationKeyPrefix + "cloudflare-custom-hostname" + CloudflareRegionKey = AnnotationKeyPrefix + "cloudflare-region-key" + CloudflareRecordCommentKey = AnnotationKeyPrefix + "cloudflare-record-comment" + CloudflareTagsKey = AnnotationKeyPrefix + "cloudflare-tags" - AWSPrefix string - CoreDNSPrefix string - SCWPrefix string - WebhookPrefix string - CloudflarePrefix string + AWSPrefix = AnnotationKeyPrefix + "aws-" + CoreDNSPrefix = AnnotationKeyPrefix + "coredns-" + SCWPrefix = AnnotationKeyPrefix + "scw-" + WebhookPrefix = AnnotationKeyPrefix + "webhook-" + CloudflarePrefix = AnnotationKeyPrefix + "cloudflare-" - TtlKey string - SetIdentifierKey string - AliasKey string - TargetKey string + TtlKey = AnnotationKeyPrefix + "ttl" + SetIdentifierKey = AnnotationKeyPrefix + "set-identifier" + AliasKey = AnnotationKeyPrefix + "alias" + TargetKey = AnnotationKeyPrefix + "target" // ControllerKey The annotation used for figuring out which controller is responsible - ControllerKey string + ControllerKey = AnnotationKeyPrefix + "controller" // HostnameKey The annotation used for defining the desired hostname - HostnameKey string + HostnameKey = AnnotationKeyPrefix + "hostname" // AccessKey The annotation used for specifying whether the public or private interface address is used - AccessKey string + AccessKey = AnnotationKeyPrefix + "access" // EndpointsTypeKey The annotation used for specifying the type of endpoints to use for headless services - EndpointsTypeKey string + EndpointsTypeKey = AnnotationKeyPrefix + "endpoints-type" // Ingress the annotation used to determine if the gateway is implemented by an Ingress object - Ingress string + Ingress = AnnotationKeyPrefix + "ingress" // IngressHostnameSourceKey The annotation used to determine the source of hostnames for ingresses. This is an optional field - all // available hostname sources are used if not specified. - IngressHostnameSourceKey string + IngressHostnameSourceKey = AnnotationKeyPrefix + "ingress-hostname-source" // ControllerValue The value of the controller annotation so that we feel responsible ControllerValue = "dns-controller" // InternalHostnameKey The annotation used for defining the desired hostname - InternalHostnameKey string + InternalHostnameKey = AnnotationKeyPrefix + "internal-hostname" // The annotation used for defining the desired hostname source for gateways - GatewayHostnameSourceKey string + GatewayHostnameSourceKey = AnnotationKeyPrefix + "gateway-hostname-source" ) // SetAnnotationPrefix sets a custom annotation prefix and rebuilds all annotation keys. // This must be called before any sources are initialized. // The prefix must end with '/'. func SetAnnotationPrefix(prefix string) { + prefix = strings.TrimSpace(prefix) + if prefix == "" || !strings.HasSuffix(prefix, "/") { + return + } AnnotationKeyPrefix = prefix // Cloudflare annotations diff --git a/source/annotations/annotations_test.go b/source/annotations/annotations_test.go index 79fde9f1d7..c82a71737b 100644 --- a/source/annotations/annotations_test.go +++ b/source/annotations/annotations_test.go @@ -23,9 +23,7 @@ import ( ) func TestSetAnnotationPrefix(t *testing.T) { - // Save original values - originalPrefix := AnnotationKeyPrefix - defer SetAnnotationPrefix(originalPrefix) + t.Cleanup(func() { SetAnnotationPrefix(DefaultAnnotationPrefix) }) // Test custom prefix customPrefix := "custom.io/" @@ -59,17 +57,17 @@ func TestSetAnnotationPrefix(t *testing.T) { } func TestDefaultAnnotationPrefix(t *testing.T) { - assert.Equal(t, "external-dns.alpha.kubernetes.io/", AnnotationKeyPrefix) - assert.Equal(t, "external-dns.alpha.kubernetes.io/hostname", HostnameKey) - assert.Equal(t, "external-dns.alpha.kubernetes.io/internal-hostname", InternalHostnameKey) - assert.Equal(t, "external-dns.alpha.kubernetes.io/ttl", TtlKey) - assert.Equal(t, "external-dns.alpha.kubernetes.io/controller", ControllerKey) + t.Cleanup(func() { SetAnnotationPrefix(DefaultAnnotationPrefix) }) + SetAnnotationPrefix(DefaultAnnotationPrefix) + assert.Equal(t, DefaultAnnotationPrefix, AnnotationKeyPrefix) + assert.Equal(t, DefaultAnnotationPrefix+"hostname", HostnameKey) + assert.Equal(t, DefaultAnnotationPrefix+"internal-hostname", InternalHostnameKey) + assert.Equal(t, DefaultAnnotationPrefix+"ttl", TtlKey) + assert.Equal(t, DefaultAnnotationPrefix+"controller", ControllerKey) } func TestSetAnnotationPrefixMultipleTimes(t *testing.T) { - // Save original values - originalPrefix := AnnotationKeyPrefix - defer SetAnnotationPrefix(originalPrefix) + t.Cleanup(func() { SetAnnotationPrefix(DefaultAnnotationPrefix) }) // Set first custom prefix SetAnnotationPrefix("first.io/") @@ -82,7 +80,32 @@ func TestSetAnnotationPrefixMultipleTimes(t *testing.T) { assert.Equal(t, "second.io/hostname", HostnameKey) // Restore to default - SetAnnotationPrefix("external-dns.alpha.kubernetes.io/") - assert.Equal(t, "external-dns.alpha.kubernetes.io/", AnnotationKeyPrefix) - assert.Equal(t, "external-dns.alpha.kubernetes.io/hostname", HostnameKey) + SetAnnotationPrefix(DefaultAnnotationPrefix) + assert.Equal(t, DefaultAnnotationPrefix, AnnotationKeyPrefix) + assert.Equal(t, DefaultAnnotationPrefix+"hostname", HostnameKey) +} + +func TestSetAnnotationPrefix_AllWhitespace_IsIgnored(t *testing.T) { + t.Cleanup(func() { SetAnnotationPrefix(DefaultAnnotationPrefix) }) + + // Set a non-default prefix so we can detect changes + SetAnnotationPrefix("nondefault.io/") + + // All whitespace turns into empty after TrimSpace -> early return -> unchanged + SetAnnotationPrefix(" \n\t ") + + assert.Equal(t, "nondefault.io/", AnnotationKeyPrefix) +} + +func TestSetAnnotationPrefix_RequiresTrailingSlash(t *testing.T) { + t.Cleanup(func() { SetAnnotationPrefix(DefaultAnnotationPrefix) }) + + // Start from a known non-default state to catch accidental changes. + SetAnnotationPrefix("nondefault.io/") + assert.Equal(t, "nondefault.io/", AnnotationKeyPrefix) + + // Does not end with '/' after trimming and should be ignored. + SetAnnotationPrefix(" invalid.io ") + assert.Equal(t, "nondefault.io/", AnnotationKeyPrefix) + assert.Equal(t, "nondefault.io/hostname", HostnameKey) } diff --git a/source/annotations/processors_test.go b/source/annotations/processors_test.go index 1333000247..89c02ec03f 100644 --- a/source/annotations/processors_test.go +++ b/source/annotations/processors_test.go @@ -366,8 +366,6 @@ func TestInternalHostnamesFromAnnotations(t *testing.T) { } func TestIsControllerMismatch(t *testing.T) { - SetAnnotationPrefix(DefaultAnnotationPrefix) - tests := []struct { name string annotations map[string]string diff --git a/source/service_test.go b/source/service_test.go index 18886ebc18..e6666dd376 100644 --- a/source/service_test.go +++ b/source/service_test.go @@ -45,13 +45,6 @@ import ( "sigs.k8s.io/external-dns/source/informers" ) -// TestMain initializes annotation keys before running tests. -// This is needed because init() was removed from annotations package. -func TestMain(m *testing.M) { - annotations.SetAnnotationPrefix("external-dns.alpha.kubernetes.io/") - m.Run() -} - type ServiceSuite struct { suite.Suite sc Source From f3964bb42bc96fcf125811c55981faeab64d5633 Mon Sep 17 00:00:00 2001 From: ivan katliarchuk Date: Sat, 31 Jan 2026 22:23:27 +0000 Subject: [PATCH 2/3] fix(annotations): allow resetting annotation prefix to default value Signed-off-by: ivan katliarchuk --- source/annotations/annotations.go | 5 ----- source/annotations/annotations_test.go | 25 ------------------------- 2 files changed, 30 deletions(-) diff --git a/source/annotations/annotations.go b/source/annotations/annotations.go index 95e62eb6e3..5248c69bf4 100644 --- a/source/annotations/annotations.go +++ b/source/annotations/annotations.go @@ -15,7 +15,6 @@ package annotations import ( "math" - "strings" ) const ( @@ -73,10 +72,6 @@ var ( // This must be called before any sources are initialized. // The prefix must end with '/'. func SetAnnotationPrefix(prefix string) { - prefix = strings.TrimSpace(prefix) - if prefix == "" || !strings.HasSuffix(prefix, "/") { - return - } AnnotationKeyPrefix = prefix // Cloudflare annotations diff --git a/source/annotations/annotations_test.go b/source/annotations/annotations_test.go index c82a71737b..def2506fe7 100644 --- a/source/annotations/annotations_test.go +++ b/source/annotations/annotations_test.go @@ -84,28 +84,3 @@ func TestSetAnnotationPrefixMultipleTimes(t *testing.T) { assert.Equal(t, DefaultAnnotationPrefix, AnnotationKeyPrefix) assert.Equal(t, DefaultAnnotationPrefix+"hostname", HostnameKey) } - -func TestSetAnnotationPrefix_AllWhitespace_IsIgnored(t *testing.T) { - t.Cleanup(func() { SetAnnotationPrefix(DefaultAnnotationPrefix) }) - - // Set a non-default prefix so we can detect changes - SetAnnotationPrefix("nondefault.io/") - - // All whitespace turns into empty after TrimSpace -> early return -> unchanged - SetAnnotationPrefix(" \n\t ") - - assert.Equal(t, "nondefault.io/", AnnotationKeyPrefix) -} - -func TestSetAnnotationPrefix_RequiresTrailingSlash(t *testing.T) { - t.Cleanup(func() { SetAnnotationPrefix(DefaultAnnotationPrefix) }) - - // Start from a known non-default state to catch accidental changes. - SetAnnotationPrefix("nondefault.io/") - assert.Equal(t, "nondefault.io/", AnnotationKeyPrefix) - - // Does not end with '/' after trimming and should be ignored. - SetAnnotationPrefix(" invalid.io ") - assert.Equal(t, "nondefault.io/", AnnotationKeyPrefix) - assert.Equal(t, "nondefault.io/hostname", HostnameKey) -} From 04dfe8a41fd28877b001b8c6e9ecdf7cdd3b7fc6 Mon Sep 17 00:00:00 2001 From: ivan katliarchuk Date: Sat, 31 Jan 2026 22:27:39 +0000 Subject: [PATCH 3/3] fix(annotations): allow resetting annotation prefix to default value Signed-off-by: ivan katliarchuk --- provider/cloudflare/cloudflare_test.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/provider/cloudflare/cloudflare_test.go b/provider/cloudflare/cloudflare_test.go index 8d76c1edd4..7f06c7fa60 100644 --- a/provider/cloudflare/cloudflare_test.go +++ b/provider/cloudflare/cloudflare_test.go @@ -42,12 +42,6 @@ import ( "sigs.k8s.io/external-dns/source/annotations" ) -// TestMain initializes annotation keys before running tests. -// This is needed because init() was removed from annotations package. -func TestMain(m *testing.M) { - m.Run() -} - // newCloudflareError creates a cloudflare.Error suitable for testing. // The v5 SDK's Error type panics when .Error() is called with nil Request/Response fields, // so this helper initializes them properly.