Repository: kubernetes-sigs/external-dns

Issue:
cloudflare: support comment field
<!-- Please only use this template for submitting enhancement requests. This can be something like a new provider or a new gateway.  -->

**What would you like to be added**:
cloudflare offer comments attached to their DNS records, up to 100 characters. It would be great to be able to configure this using a `external-dns.alpha.kubernetes.io/cloudflare-` style annotation, or potentially a CLI flag.

**Why is this needed**:
This would help to make explicit the DNS records that are externally managed, the last time they were updated, perhaps their cluster, etc.

Hints from issue comments:
@MaGaroo: I can work on this issue. Let me know if it is accepted.@johngmyers: Sounds like that would be a good place to put the labels, so one could use the noop registry.@davidegirardi: Aren't [record-tags](https://developers.cloudflare.com/dns/manage-dns-records/reference/record-attributes/#record-tags) better to store labels then?@johngmyers: May well be. I don't know much about Cloudflare.@davidegirardi: The `comment` is a string and `tags` is an array of strings in the [request body](https://developers.cloudflare.com/api/operations/dns-records-for-a-zone-create-dns-record#request-body). They have [limitations](https://developers.cloudflare.com/dns/manage-dns-records/reference/record-attributes/#record-comments) depending on the subscription in use.@morremeyer: I would very much like to be able to use the `comment` field instead of TXT records as registry. To make this compatible with all Cloudflare subcriptions, it should however be <= 100 characters.@matrix-root: Is somebody working on this?@k8s-triage-robot: The Kubernetes project currently lacks enough contributors to adequately respond to all issues.

This bot triages un-triaged issues according to the following rules:
- After 90d of inactivity, `lifecycle/stale` is applied
- After 30d of inactivity since `lifecycle/stale` was applied, `lifecycle/rotten` is applied
- After 30d of inactivity since `lifecycle/rotten` was applied, the issue is closed

You can:
- Mark this issue as fresh with `/remove-lifecycle stale`
- Close this issue with `/close`
- Offer to help out with [Issue Triage][1]

Please send feedback to sig-contributor-experience at [kubernetes/community](https://github.com/kubernetes/community).

/lifecycle stale

[1]: https://www.kubernetes.dev/docs/guide/issue-triage/@morremeyer: Not stale, I might get to work on this soon-ish.@morremeyer: /remove-lifecycle stale@andyr8939: It would be really great to have support for Cloudflare Tags as well, as these are more useful because of being able to search and group based on them.   Example when you have a large zone where not everything is managed by external-dns, being able to see quickly at a glance what is managed by _external-dns_ and what is _terraform_ via tags would be useful.

Example....
![image](https://github.com/kubernetes-sigs/external-dns/assets/28910317/9a337a75-91e1-4f3c-a944-baa0e9175762)
@stepanbaghdasaryan: Is somebody working on this?@k8s-triage-robot: The Kubernetes project currently lacks enough contributors to adequately respond to all issues.

This bot triages un-triaged issues according to the following rules:
- After 90d of inactivity, `lifecycle/stale` is applied
- After 30d of inactivity since `lifecycle/stale` was applied, `lifecycle/rotten` is applied
- After 30d of inactivity since `lifecycle/rotten` was applied, the issue is closed

You can:
- Mark this issue as fresh with `/remove-lifecycle stale`
- Close this issue with `/close`
- Offer to help out with [Issue Triage][1]

Please send feedback to sig-contributor-experience at [kubernetes/community](https://github.com/kubernetes/community).

/lifecycle stale

[1]: https://www.kubernetes.dev/docs/guide/issue-triage/@morremeyer: /remove-lifecycle stale@xaoo: Hi, is this being considered? Can we make a contribution here?
In the end even if you have an enterprise level subscription, tags and comments can be optional.@k8s-triage-robot: The Kubernetes project currently lacks enough contributors to adequately respond to all issues.

This bot triages un-triaged issues according to the following rules:
- After 90d of inactivity, `lifecycle/stale` is applied
- After 30d of inactivity since `lifecycle/stale` was applied, `lifecycle/rotten` is applied
- After 30d of inactivity since `lifecycle/rotten` was applied, the issue is closed

You can:
- Mark this issue as fresh with `/remove-lifecycle stale`
- Close this issue with `/close`
- Offer to help out with [Issue Triage][1]

Please send feedback to sig-contributor-experience at [kubernetes/community](https://github.com/kubernetes/community).

/lifecycle stale

[1]: https://www.kubernetes.dev/docs/guide/issue-triage/@morremeyer: /remove-lifecycle stale@k8s-triage-robot: The Kubernetes project currently lacks enough contributors to adequately respond to all issues.

This bot triages un-triaged issues according to the following rules:
- After 90d of inactivity, `lifecycle/stale` is applied
- After 30d of inactivity since `lifecycle/stale` was applied, `lifecycle/rotten` is applied
- After 30d of inactivity since `lifecycle/rotten` was applied, the issue is closed

You can:
- Mark this issue as fresh with `/remove-lifecycle stale`
- Close this issue with `/close`
- Offer to help out with [Issue Triage][1]

Please send feedback to sig-contributor-experience at [kubernetes/community](https://github.com/kubernetes/community).

/lifecycle stale

[1]: https://www.kubernetes.dev/docs/guide/issue-triage/@k8s-triage-robot: The Kubernetes project currently lacks enough active contributors to adequately respond to all issues.

This bot triages un-triaged issues according to the following rules:
- After 90d of inactivity, `lifecycle/stale` is applied
- After 30d of inactivity since `lifecycle/stale` was applied, `lifecycle/rotten` is applied
- After 30d of inactivity since `lifecycle/rotten` was applied, the issue is closed

You can:
- Mark this issue as fresh with `/remove-lifecycle rotten`
- Close this issue with `/close`
- Offer to help out with [Issue Triage][1]

Please send feedback to sig-contributor-experience at [kubernetes/community](https://github.com/kubernetes/community).

/lifecycle rotten

[1]: https://www.kubernetes.dev/docs/guide/issue-triage/@bryanhorstmann: /remove-lifecycle rotten@7onn: Just opened https://github.com/kubernetes-sigs/external-dns/pull/5359 drafting such feature :) Let's see what the maintainers will say.

Solution patch:
diff --git a/.golangci.yml b/.golangci.yml
index 17669081..7b12c19c 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -14,9 +14,12 @@ linters:
     - unconvert
     - unused
     - whitespace
+    - decorder # Check declaration order and count of types, constants, variables and functions. https://golangci-lint.run/usage/linters/#decorder
+    - tagalign # Check that struct tags are well aligned. https://golangci-lint.run/usage/linters/#tagalign
     - predeclared # Find code that shadows one of Go's predeclared identifiers
     - sloglint # Ensure consistent code style when using log/slog
     - asciicheck  # Checks that all code identifiers does not have non-ASCII symbols in the name
+    - nilnil # Checks that there is no simultaneous return of nil error and an nil value. ref: https://golangci-lint.run/usage/linters/#nilnil
   settings:
     exhaustive:
       default-signifies-exhaustive: false
@@ -51,6 +54,10 @@ linters:
           - varcheck
           - whitespace
         path: _test\.go
+      # TODO: skiip as will require design changes
+      - linters:
+          - nilnil
+        path: istio_virtualservice.go|fqdn.go|cloudflare.go
     paths:
       - endpoint/zz_generated.deepcopy.go
       - third_party$
diff --git a/controller/execute.go b/controller/execute.go
index 4113fd64..6ae1cf5b 100644
--- a/controller/execute.go
+++ b/controller/execute.go
@@ -186,9 +186,9 @@ func Execute() {
 		}
 		p, err = awssd.NewAWSSDProvider(domainFilter, cfg.AWSZoneType, cfg.DryRun, cfg.AWSSDServiceCleanup, cfg.TXTOwnerID, cfg.AWSSDCreateTag, sd.NewFromConfig(aws.CreateDefaultV2Config(cfg)))
 	case "azure-dns", "azure":
-		p, err = azure.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.AzureZonesCacheDuration, cfg.DryRun)
+		p, err = azure.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.AzureZonesCacheDuration, cfg.AzureMaxRetriesCount, cfg.DryRun)
 	case "azure-private-dns":
-		p, err = azure.NewAzurePrivateDNSProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.AzureZonesCacheDuration, cfg.DryRun)
+		p, err = azure.NewAzurePrivateDNSProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.AzureZonesCacheDuration, cfg.AzureMaxRetriesCount, cfg.DryRun)
 	case "ultradns":
 		p, err = ultradns.NewUltraDNSProvider(domainFilter, cfg.DryRun)
 	case "civo":
@@ -199,12 +199,15 @@ func Execute() {
 			zoneIDFilter,
 			cfg.CloudflareProxied,
 			cfg.DryRun,
-			cfg.CloudflareDNSRecordsPerPage,
 			cfg.CloudflareRegionKey,
 			cloudflare.CustomHostnamesConfig{
 				Enabled:              cfg.CloudflareCustomHostnames,
 				MinTLSVersion:        cfg.CloudflareCustomHostnamesMinTLSVersion,
 				CertificateAuthority: cfg.CloudflareCustomHostnamesCertificateAuthority,
+			},
+			cloudflare.DNSRecordsConfig{
+				PerPage: cfg.CloudflareDNSRecordsPerPage,
+				Comment: cfg.CloudflareDNSRecordsComment,
 			})
 	case "google":
 		p, err = google.NewGoogleProvider(ctx, cfg.GoogleProject, domainFilter, zoneIDFilter, cfg.GoogleBatchChangeSize, cfg.GoogleBatchChangeInterval, cfg.GoogleZoneVisibility, cfg.DryRun)
diff --git a/docs/flags.md b/docs/flags.md
index b9adbb5a..00a9abda 100644
--- a/docs/flags.md
+++ b/docs/flags.md
@@ -86,6 +86,7 @@
 | `--azure-subscription-id=""` | When using the Azure provider, override the Azure subscription to use (optional) |
 | `--azure-user-assigned-identity-client-id=""` | When using the Azure provider, override the client id of user assigned identity in config file (optional) |
 | `--azure-zones-cache-duration=0s` | When using the Azure provider, set the zones list cache TTL (0s to disable). |
+| `--azure-maxretries-count=3` | When using the Azure provider, set the number of retries for API calls (When less than 0, it disables retries). (optional) |
 | `--tencent-cloud-config-file="/etc/kubernetes/tencent-cloud.json"` | When using the Tencent Cloud provider, specify the Tencent Cloud configuration file (required when --provider=tencentcloud) |
 | `--tencent-cloud-zone-type=` | When using the Tencent Cloud provider, filter for zones with visibility (optional, options: public, private) |
 | `--[no-]cloudflare-proxied` | When using the Cloudflare provider, specify if the proxy mode must be enabled (default: disabled) |
@@ -94,6 +95,7 @@
 | `--cloudflare-custom-hostnames-certificate-authority=google` | When using the Cloudflare provider with the Custom Hostnames, specify which Cerrtificate Authority will be used by default. (default: google, options: google, ssl_com, lets_encrypt) |
 | `--cloudflare-dns-records-per-page=100` | When using the Cloudflare provider, specify how many DNS records listed per page, max possible 5,000 (default: 100) |
 | `--cloudflare-region-key=CLOUDFLARE-REGION-KEY` | When using the Cloudflare provider, specify the region (default: earth) |
+| `--cloudflare-record-comment=""` | When using the Cloudflare provider, specify the comment for the DNS records (default: '') |
 | `--coredns-prefix="/skydns/"` | When using the CoreDNS provider, specify the prefix name |
 | `--akamai-serviceconsumerdomain=""` | When using the Akamai provider, specify the base URL (required when --provider=akamai and edgerc-path not specified) |
 | `--akamai-client-token=""` | When using the Akamai provider, specify the client token (required when --provider=akamai and edgerc-path not specified) |
diff --git a/docs/tutorials/azure-private-dns.md b/docs/tutorials/azure-private-dns.md
index 0f55edd6..984b78d0 100644
--- a/docs/tutorials/azure-private-dns.md
+++ b/docs/tutorials/azure-private-dns.md
@@ -108,6 +108,7 @@ $ az role assignment create --role "Private DNS Zone Contributor" --assignee <ap
 ## Throttling
 
 When the ExternalDNS managed zones list doesn't change frequently, one can set `--azure-zones-cache-duration` (zones list cache time-to-live). The zones list cache is disabled by default, with a value of 0s.
+Also, one can leverage the built-in retry policies of the Azure SDK. The flag --azure-maxretries-count can be specified in the manifest yaml to configure behavior. The default value of Azure SDK retry is 3.
 
 ## Deploy ExternalDNS
 
@@ -151,6 +152,7 @@ spec:
         - --provider=azure-private-dns
         - --azure-resource-group=externaldns
         - --azure-subscription-id=<use the id of your subscription>
+        - --azure-maxretries-count=1  # (optional) specifies the maxRetires value to be used by the Azure SDK. Default is 3.
         volumeMounts:
         - name: azure-config-file
           mountPath: /etc/kubernetes
@@ -223,6 +225,7 @@ spec:
         - --provider=azure-private-dns
         - --azure-resource-group=externaldns
         - --azure-subscription-id=<use the id of your subscription>
+        - --azure-maxretries-count=1  # (optional) specifies the maxRetires value to be used by the Azure SDK. Default is 3.
         volumeMounts:
         - name: azure-config-file
           mountPath: /etc/kubernetes
@@ -295,6 +298,7 @@ spec:
         - --provider=azure-private-dns
         - --azure-resource-group=externaldns
         - --azure-subscription-id=<use the id of your subscription>
+        - --azure-maxretries-count=1  # (optional) specifies the maxRetires value to be used by the Azure SDK. Default is 3.
         volumeMounts:
         - name: azure-config-file
           mountPath: /etc/kubernetes
diff --git a/docs/tutorials/azure.md b/docs/tutorials/azure.md
index d9a201ff..0e537622 100644
--- a/docs/tutorials/azure.md
+++ b/docs/tutorials/azure.md
@@ -493,6 +493,7 @@ NOTE: make sure the pod is restarted whenever you make a configuration change.
 ## Throttling
 
 When the ExternalDNS managed zones list doesn't change frequently, one can set `--azure-zones-cache-duration` (zones list cache time-to-live). The zones list cache is disabled by default, with a value of 0s.
+Also, one can leverage the built-in retry policies of the Azure SDK with a tunable maxRetries value. Environment variable AZURE_SDK_MAX_RETRIES can be specified in the manifest yaml to configure behavior. The defualt value of Azure SDK retry is 3.
 
 ## Ingress used with ExternalDNS
 
@@ -540,6 +541,7 @@ spec:
         - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
         - --provider=azure
         - --azure-resource-group=MyDnsResourceGroup # (optional) use the DNS zones from the tutorial's resource group
+        - --azure-maxretries-count=1  # (optional) specifies the maxRetires value to be used by the Azure SDK. Default is 3.
         volumeMounts:
         - name: azure-config-file
           mountPath: /etc/kubernetes
@@ -609,6 +611,7 @@ spec:
             - --provider=azure
             - --azure-resource-group=MyDnsResourceGroup # (optional) use the DNS zones from the tutorial's resource group
             - --txt-prefix=externaldns-
+            - --azure-maxretries-count=1  # (optional) specifies the maxRetires value to be used by the Azure SDK. Default is 3.
           volumeMounts:
             - name: azure-config-file
               mountPath: /etc/kubernetes
@@ -680,6 +683,7 @@ spec:
             - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
             - --provider=azure
             - --azure-resource-group=MyDnsResourceGroup # (optional) use the DNS zones from the tutorial's resource group
+            - --azure-maxretries-count=1  # (optional) specifies the maxRetires value to be used by the Azure SDK. Default is 3.
           volumeMounts:
             - name: azure-config-file
               mountPath: /etc/kubernetes
diff --git a/docs/tutorials/cloudflare.md b/docs/tutorials/cloudflare.md
index cd62e1c1..abfb95ee 100644
--- a/docs/tutorials/cloudflare.md
+++ b/docs/tutorials/cloudflare.md
@@ -129,6 +129,7 @@ spec:
             - --cloudflare-proxied # (optional) enable the proxy feature of Cloudflare (DDOS protection, CDN...)
             - --cloudflare-dns-records-per-page=5000 # (optional) configure how many DNS records to fetch per request
             - --cloudflare-region-key="eu" # (optional) configure which region can decrypt HTTPS requests
+            - --cloudflare-record-comment="provisioned by external-dns" # (optional) configure comments for provisioned records; <=100 chars for free zones; <=500 chars for paid zones
          env:
             - name: CF_API_KEY
               valueFrom:
@@ -205,6 +206,7 @@ spec:
             - --cloudflare-proxied # (optional) enable the proxy feature of Cloudflare (DDOS protection, CDN...)
             - --cloudflare-dns-records-per-page=5000 # (optional) configure how many DNS records to fetch per request
             - --cloudflare-region-key="eu" # (optional) configure which region can decrypt HTTPS requests
+            - --cloudflare-record-comment="provisioned by external-dns" # (optional) configure comments for provisioned records; <=100 chars for free zones; <=500 chars for paid zones
           env:
             - name: CF_API_KEY
               valueFrom:
diff --git a/go.mod b/go.mod
index 15623faa..cb505369 100644
--- a/go.mod
+++ b/go.mod
@@ -3,15 +3,15 @@ module sigs.k8s.io/external-dns
 go 1.24.2
 
 require (
-	cloud.google.com/go/compute/metadata v0.6.0
+	cloud.google.com/go/compute/metadata v0.7.0
 	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0
-	github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0
+	github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0
 	github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0
 	github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0
 	github.com/F5Networks/k8s-bigip-ctlr/v2 v2.19.1
 	github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.7.2
 	github.com/IBM/go-sdk-core/v5 v5.19.1
-	github.com/IBM/networking-go-sdk v0.51.4
+	github.com/IBM/networking-go-sdk v0.51.5
 	github.com/Yamashou/gqlgenc v0.32.1
 	github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2
 	github.com/alecthomas/kingpin/v2 v2.4.0
@@ -26,12 +26,12 @@ require (
 	github.com/aws/aws-sdk-go-v2/service/sts v1.33.19
 	github.com/bodgit/tsig v1.2.2
 	github.com/cenkalti/backoff/v5 v5.0.2
-	github.com/civo/civogo v0.4.1
+	github.com/civo/civogo v0.5.1
 	github.com/cloudflare/cloudflare-go v0.115.0
 	github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381
 	github.com/datawire/ambassador v1.12.4
 	github.com/denverdino/aliyungo v0.0.0-20230411124812-ab98a9173ace
-	github.com/digitalocean/godo v1.146.0
+	github.com/digitalocean/godo v1.148.0
 	github.com/dnsimple/dnsimple-go v1.7.0
 	github.com/exoscale/egoscale v0.102.3
 	github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99
@@ -47,34 +47,34 @@ require (
 	github.com/onsi/ginkgo v1.16.5
 	github.com/openshift/api v0.0.0-20230607130528-611114dca681
 	github.com/openshift/client-go v0.0.0-20230607134213-3cd0021bbee3
-	github.com/oracle/oci-go-sdk/v65 v65.90.0
+	github.com/oracle/oci-go-sdk/v65 v65.91.0
 	github.com/ovh/go-ovh v1.7.0
 	github.com/patrickmn/go-cache v2.1.0+incompatible
 	github.com/pluralsh/gqlclient v1.12.2
-	github.com/projectcontour/contour v1.31.0
+	github.com/projectcontour/contour v1.32.0
 	github.com/prometheus/client_golang v1.22.0
 	github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33
 	github.com/sirupsen/logrus v1.9.3
 	github.com/stretchr/testify v1.10.0
-	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1162
-	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136
+	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1166
+	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1165
 	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1145
 	github.com/transip/gotransip/v6 v6.26.0
 	github.com/ultradns/ultradns-sdk-go v1.3.7
-	go.etcd.io/etcd/client/v3 v3.5.21
+	go.etcd.io/etcd/client/v3 v3.6.0
 	go.uber.org/ratelimit v0.3.1
 	golang.org/x/net v0.40.0
 	golang.org/x/oauth2 v0.30.0
 	golang.org/x/sync v0.14.0
 	golang.org/x/text v0.25.0
 	golang.org/x/time v0.11.0
-	google.golang.org/api v0.232.0
+	google.golang.org/api v0.233.0
 	gopkg.in/ns1/ns1-go.v2 v2.14.3
 	istio.io/api v1.26.0
 	istio.io/client-go v1.26.0
-	k8s.io/api v0.33.0
-	k8s.io/apimachinery v0.33.0
-	k8s.io/client-go v0.33.0
+	k8s.io/api v0.33.1
+	k8s.io/apimachinery v0.33.1
+	k8s.io/client-go v0.33.1
 	k8s.io/klog/v2 v2.130.1
 	sigs.k8s.io/gateway-api v1.3.0
 )
@@ -136,6 +136,7 @@ require (
 	github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
 	github.com/googleapis/gax-go/v2 v2.14.1 // indirect
 	github.com/gopherjs/gopherjs v1.17.2 // indirect
+	github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
 	github.com/hashicorp/errwrap v1.1.0 // indirect
 	github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
 	github.com/hashicorp/go-multierror v1.1.1 // indirect
@@ -170,7 +171,7 @@ require (
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
 	github.com/prometheus/client_model v0.6.2 // indirect
-	github.com/prometheus/common v0.63.0 // indirect
+	github.com/prometheus/common v0.64.0 // indirect
 	github.com/prometheus/procfs v0.15.1 // indirect
 	github.com/rivo/uniseg v0.2.0 // indirect
 	github.com/schollz/progressbar/v3 v3.8.6 // indirect
@@ -184,8 +185,8 @@ require (
 	github.com/x448/float16 v0.8.4 // indirect
 	github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
 	github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
-	go.etcd.io/etcd/api/v3 v3.5.21 // indirect
-	go.etcd.io/etcd/client/pkg/v3 v3.5.21 // indirect
+	go.etcd.io/etcd/api/v3 v3.6.0 // indirect
+	go.etcd.io/etcd/client/pkg/v3 v3.6.0 // indirect
 	go.mongodb.org/mongo-driver v1.17.2 // indirect
 	go.opentelemetry.io/auto/sdk v1.1.0 // indirect
 	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
@@ -201,8 +202,8 @@ require (
 	golang.org/x/term v0.32.0 // indirect
 	golang.org/x/tools v0.32.0 // indirect
 	google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect
-	google.golang.org/grpc v1.72.0 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2 // indirect
+	google.golang.org/grpc v1.72.1 // indirect
 	google.golang.org/protobuf v1.36.6 // indirect
 	gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
diff --git a/go.sum b/go.sum
index 50bdfba5..a31ea75b 100644
--- a/go.sum
+++ b/go.sum
@@ -6,8 +6,8 @@ cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU=
 cloud.google.com/go/auth v0.16.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI=
 cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
 cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
-cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
-cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
+cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU=
+cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo=
 code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f h1:UrKzEwTgeiff9vxdrfdqxibzpWjxLnuXDI5m6z3GJAk=
 code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f/go.mod h1:sk5LnIjB/nIEU7yP5sDQExVm62wu0pBh3yrElngUisI=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
@@ -18,8 +18,8 @@ github.com/Azure/azure-sdk-for-go v16.2.1+incompatible h1:KnPIugL51v3N3WwvaSmZbx
 github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 h1:OVoM452qUFBrX+URdH3VpR299ma4kfom0yB0URYky9g=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0/go.mod h1:kUjrAo8bgEwLeZ/CmHqNl3Z/kPm7y6FKfxxK0izYUg4=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 h1:j8BorDEigD8UFOSZQiSqAMOOleyQOOQPnUAwV+Ls1gA=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4=
 github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
 github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
 github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
@@ -55,8 +55,8 @@ github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.7.2 h1:eW5o8NpblAyqPjwOlZ+XISdhlYynjf7
 github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.7.2/go.mod h1:HulyrJLLc9FSZlwKQ9vu5Jq83thNlUfg1afonOdhrRA=
 github.com/IBM/go-sdk-core/v5 v5.19.1 h1:sleVks1O4XjgF4YEGvyDh6PZbP6iZhlTPeDkQc8nWDs=
 github.com/IBM/go-sdk-core/v5 v5.19.1/go.mod h1:Q3BYO6iDA2zweQPDGbNTtqft5tDcEpm6RTuqMlPcvbw=
-github.com/IBM/networking-go-sdk v0.51.4 h1:rkbR+gUkksLKjNYL5YEWEAMv3qddR0mUkxObDMa4l/s=
-github.com/IBM/networking-go-sdk v0.51.4/go.mod h1:gjCFEp+UVP7FUlcq2C1RaoZAXFcD39CQdlUk7uVKko4=
+github.com/IBM/networking-go-sdk v0.51.5 h1:75lKAx17y++hirXK5GcEM23mTRhHnhsv6gmhz70ex1Q=
+github.com/IBM/networking-go-sdk v0.51.5/go.mod h1:wyEnRnBnROgGmSn5UrryycIrbBujHKXf0PmI1NSwcjY=
 github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
 github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
 github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
@@ -188,8 +188,8 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
-github.com/civo/civogo v0.4.1 h1:C+lwZ7hBqKy6eKy6qgviuselF0V5Z/um0x7X/eLEQ64=
-github.com/civo/civogo v0.4.1/go.mod h1:LaEbkszc+9nXSh4YNG0sYXFGYqdQFmXXzQg0gESs2hc=
+github.com/civo/civogo v0.5.1 h1:WH6v+47uJybpJOtf+/Jopjp2R5lLwi4/PANp714d+zc=
+github.com/civo/civogo v0.5.1/go.mod h1:LaEbkszc+9nXSh4YNG0sYXFGYqdQFmXXzQg0gESs2hc=
 github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cloudflare/cloudflare-go v0.115.0 h1:84/dxeeXweCc0PN5Cto44iTA8AkG1fyT11yPO5ZB7sM=
@@ -258,8 +258,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
 github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
-github.com/digitalocean/godo v1.146.0 h1:0CDU1/vxvlvMFjIOk1UfC8Ku6pwfZFOM1Uf/ZEke4oc=
-github.com/digitalocean/godo v1.146.0/go.mod h1:tYeiWY5ZXVpU48YaFv0M5irUFHXGorZpDNm7zzdWMzM=
+github.com/digitalocean/godo v1.148.0 h1:th91q+6bZY+Slgs9eZxBupa2+aUUYn1qT7gPICFmtPA=
+github.com/digitalocean/godo v1.148.0/go.mod h1:tYeiWY5ZXVpU48YaFv0M5irUFHXGorZpDNm7zzdWMzM=
 github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
 github.com/dnsimple/dnsimple-go v1.7.0 h1:JKu9xJtZ3SqOC+BuYgAWeab7+EEx0sz422vu8j611ZY=
 github.com/dnsimple/dnsimple-go v1.7.0/go.mod h1:EKpuihlWizqYafSnQHGCd/gyvy3HkEQJ7ODB4KdV8T8=
@@ -556,6 +556,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de
 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
 github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
 github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
 github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
 github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
@@ -848,8 +850,8 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS
 github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
 github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
 github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
-github.com/oracle/oci-go-sdk/v65 v65.90.0 h1:XGoPkq0oThnIzmreyWOG3PkP3r1I1JrEWxOlGocJXt0=
-github.com/oracle/oci-go-sdk/v65 v65.90.0/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA=
+github.com/oracle/oci-go-sdk/v65 v65.91.0 h1:maO6AxKxVfszH0X4tbbtN21jOk03lCRR3IqiA8/FzZc=
+github.com/oracle/oci-go-sdk/v65 v65.91.0/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA=
 github.com/ovh/go-ovh v1.7.0 h1:V14nF7FwDjQrZt9g7jzcvAAQ3HN6DNShRFRMC3jLoPw=
 github.com/ovh/go-ovh v1.7.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c=
 github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 h1:CXwSGu/LYmbjEab5aMCs5usQRVBGThelUKBNnoSOuso=
@@ -884,8 +886,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
 github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
-github.com/projectcontour/contour v1.31.0 h1:y6PNQTqPAa0mInTld50bxdDuf69PZmxCP3RobfjiSNk=
-github.com/projectcontour/contour v1.31.0/go.mod h1:nigS15X+bsL/vYLYe7vBzUriIKjR8XrOeQAKqXypWZg=
+github.com/projectcontour/contour v1.32.0 h1:mOy8JEa6im5FOToJJugVVJYVcNp/mkb72H3ymdTg1PM=
+github.com/projectcontour/contour v1.32.0/go.mod h1:e7mw7MqbQeyv+PYnD7CbdRTtQbBfvGYyTrEvmwSKVnk=
 github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
@@ -911,8 +913,8 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
 github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
-github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k=
-github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18=
+github.com/prometheus/common v0.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQPGO4=
+github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
 github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
@@ -925,8 +927,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
 github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
-github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
-github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
+github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI=
+github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
 github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
@@ -1019,12 +1021,12 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
 github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
 github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1136/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1145/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1162 h1:bscCBygP9JRl6iNabF+vmBOhY+xayFFGYV5Wa0NzH0A=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1162/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136 h1:kMIdSU5IvpOROh27ToVQ3hlm6ym3lCRs9tnGCOBoZqk=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136/go.mod h1:FpyIz3mymKaExVs6Fz27kxDBS42jqZn7vbACtxdeEH4=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1165/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1166 h1:WgdCsNxde/flDGxOy+dXZqm2wrUQA/4kzaaY69XC/2A=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1166/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1165 h1:bHT5sLou90UwrVm7Km2nQ7hyBk0+LK4xi7JOZXoDQ2c=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1165/go.mod h1:6pN5Nh0PuYJ+XHkjiV7SQqXgc1gecB9FHd456W9ZNdE=
 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1145 h1:K5N0Uxqm9kM7KU6DFBekCTKbldlXq6UD1ekOyXn4zEc=
 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1145/go.mod h1:mgwxarWzLxIylWtHmkoMg0dX8aqhO5lwfrHkK4IGLKE=
 github.com/terra-farm/udnssdk v1.3.5 h1:MNR3adfuuEK/l04+jzo8WW/0fnorY+nW515qb3vEr6I=
@@ -1081,12 +1083,12 @@ github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wK
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
-go.etcd.io/etcd/api/v3 v3.5.21 h1:A6O2/JDb3tvHhiIz3xf9nJ7REHvtEFJJ3veW3FbCnS8=
-go.etcd.io/etcd/api/v3 v3.5.21/go.mod h1:c3aH5wcvXv/9dqIw2Y810LDXJfhSYdHQ0vxmP3CCHVY=
-go.etcd.io/etcd/client/pkg/v3 v3.5.21 h1:lPBu71Y7osQmzlflM9OfeIV2JlmpBjqBNlLtcoBqUTc=
-go.etcd.io/etcd/client/pkg/v3 v3.5.21/go.mod h1:BgqT/IXPjK9NkeSDjbzwsHySX3yIle2+ndz28nVsjUs=
-go.etcd.io/etcd/client/v3 v3.5.21 h1:T6b1Ow6fNjOLOtM0xSoKNQt1ASPCLWrF9XMHcH9pEyY=
-go.etcd.io/etcd/client/v3 v3.5.21/go.mod h1:mFYy67IOqmbRf/kRUvsHixzo3iG+1OF2W2+jVIQRAnU=
+go.etcd.io/etcd/api/v3 v3.6.0 h1:vdbkcUBGLf1vfopoGE/uS3Nv0KPyIpUV/HM6w9yx2kM=
+go.etcd.io/etcd/api/v3 v3.6.0/go.mod h1:Wt5yZqEmxgTNJGHob7mTVBJDZNXiHPtXTcPab37iFOw=
+go.etcd.io/etcd/client/pkg/v3 v3.6.0 h1:nchnPqpuxvv3UuGGHaz0DQKYi5EIW5wOYsgUNRc365k=
+go.etcd.io/etcd/client/pkg/v3 v3.6.0/go.mod h1:Jv5SFWMnGvIBn8o3OaBq/PnT0jjsX8iNokAUessNjoA=
+go.etcd.io/etcd/client/v3 v3.6.0 h1:/yjKzD+HW5v/3DVj9tpwFxzNbu8hjcKID183ug9duWk=
+go.etcd.io/etcd/client/v3 v3.6.0/go.mod h1:Jzk/Knqe06pkOZPHXsQ0+vNDvMQrgIqJ0W8DwPdMJMg=
 go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
 go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
 go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
@@ -1383,8 +1385,8 @@ gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZ
 google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
 google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
 google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
-google.golang.org/api v0.232.0 h1:qGnmaIMf7KcuwHOlF3mERVzChloDYwRfOJOrHt8YC3I=
-google.golang.org/api v0.232.0/go.mod h1:p9QCfBWZk1IJETUdbTKloR5ToFdKbYh2fkjsUL6vNoY=
+google.golang.org/api v0.233.0 h1:iGZfjXAJiUFSSaekVB7LzXl6tRfEKhUN7FkZN++07tI=
+google.golang.org/api v0.233.0/go.mod h1:TCIVLLlcwunlMpZIhIp7Ltk77W+vUSdUKAAIlbxY44c=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1400,8 +1402,8 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98
 google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 h1:hE3bRWtU6uceqlh4fhrSnUyjKHMKB9KrTLLG+bc0ddM=
 google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463/go.mod h1:U90ffi8eUL9MwPcrJylN5+Mk2v3vuPDptd5yyNUiRR8=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 h1:h6p3mQqrmT1XkHVTfzLdNz1u7IhINeZkz67/xTbOuWs=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2 h1:IqsN8hx+lWLqlN+Sc3DoMy/watjofWiU8sRFgQ8fhKM=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
 google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
 google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@@ -1415,8 +1417,8 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ
 google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
-google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
-google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
+google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
+google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -1490,16 +1492,16 @@ istio.io/client-go v1.26.0/go.mod h1:1nRkn8XmI8AZVKElaLsC79jrVqS+PPIu1EczNvmGst8
 k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8=
 k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78=
 k8s.io/api v0.18.4/go.mod h1:lOIQAKYgai1+vz9J7YcDZwC26Z0zQewYOGWdyIPUUQ4=
-k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU=
-k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM=
+k8s.io/api v0.33.1 h1:tA6Cf3bHnLIrUK4IqEgb2v++/GYUtqiu9sRVk3iBXyw=
+k8s.io/api v0.33.1/go.mod h1:87esjTn9DRSRTD4fWMXamiXxJhpOIREjWOSjsW1kEHw=
 k8s.io/apiextensions-apiserver v0.18.0/go.mod h1:18Cwn1Xws4xnWQNC00FLq1E350b9lUF+aOdIWDOZxgo=
 k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY=
 k8s.io/apiextensions-apiserver v0.18.4/go.mod h1:NYeyeYq4SIpFlPxSAB6jHPIdvu3hL0pc36wuRChybio=
 k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
 k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
 k8s.io/apimachinery v0.18.4/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
-k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ=
-k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
+k8s.io/apimachinery v0.33.1 h1:mzqXWV8tW9Rw4VeW9rEkqvnxj59k1ezDUl20tFK/oM4=
+k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
 k8s.io/apiserver v0.18.0/go.mod h1:3S2O6FeBBd6XTo0njUrLxiqk8GNy6wWOftjhJcXYnjw=
 k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw=
 k8s.io/apiserver v0.18.4/go.mod h1:q+zoFct5ABNnYkGIaGQ3bcbUNdmPyOCoEBcg51LChY8=
@@ -1508,8 +1510,8 @@ k8s.io/cli-runtime v0.18.4/go.mod h1:9/hS/Cuf7NVzWR5F/5tyS6xsnclxoPLVtwhnkJG1Y4g
 k8s.io/client-go v0.18.0/go.mod h1:uQSYDYs4WhVZ9i6AIoEZuwUggLVEF64HOD37boKAtF8=
 k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU=
 k8s.io/client-go v0.18.4/go.mod h1:f5sXwL4yAZRkAtzOxRWUhA/N8XzGCb+nPZI8PfobZ9g=
-k8s.io/client-go v0.33.0 h1:UASR0sAYVUzs2kYuKn/ZakZlcs2bEHaizrrHUZg0G98=
-k8s.io/client-go v0.33.0/go.mod h1:kGkd+l/gNGg8GYWAPr0xF1rRKvVWvzh9vmZAMXtaKOg=
+k8s.io/client-go v0.33.1 h1:ZZV/Ks2g92cyxWkRRnfUDsnhNn28eFpt26aGc8KbXF4=
+k8s.io/client-go v0.33.1/go.mod h1:JAsUrl1ArO7uRVFWfcj6kOomSlCv+JpvIsp6usAGefA=
 k8s.io/code-generator v0.18.0/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
 k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
 k8s.io/code-generator v0.18.4/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c=
diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go
index 103e54e9..fefdfaf7 100644
--- a/pkg/apis/externaldns/types.go
+++ b/pkg/apis/externaldns/types.go
@@ -106,12 +106,15 @@ type Config struct {
 	AzureUserAssignedIdentityClientID             string
 	AzureActiveDirectoryAuthorityHost             string
 	AzureZonesCacheDuration                       time.Duration
+	AzureMaxRetriesCount                          int
 	CloudflareProxied                             bool
 	CloudflareCustomHostnames                     bool
+	CloudflareDNSRecordsPerPage                   int
+	CloudflareDNSRecordsComment                   string
 	CloudflareCustomHostnamesMinTLSVersion        string
 	CloudflareCustomHostnamesCertificateAuthority string
-	CloudflareDNSRecordsPerPage                   int
 	CloudflareRegionKey                           string
+	CloudflareRecordComment                       string
 	CoreDNSPrefix                                 string
 	AkamaiServiceConsumerDomain                   string
 	AkamaiClientToken                             string
@@ -247,6 +250,7 @@ var defaultConfig = &Config{
 	AzureResourceGroup:          "",
 	AzureSubscriptionID:         "",
 	AzureZonesCacheDuration:     0 * time.Second,
+	AzureMaxRetriesCount:        3,
 	CFAPIEndpoint:               "",
 	CFPassword:                  "",
 	CFUsername:                  "",
@@ -527,6 +531,7 @@ func App(cfg *Config) *kingpin.Application {
 	app.Flag("azure-subscription-id", "When using the Azure provider, override the Azure subscription to use (optional)").Default(defaultConfig.AzureSubscriptionID).StringVar(&cfg.AzureSubscriptionID)
 	app.Flag("azure-user-assigned-identity-client-id", "When using the Azure provider, override the client id of user assigned identity in config file (optional)").Default("").StringVar(&cfg.AzureUserAssignedIdentityClientID)
 	app.Flag("azure-zones-cache-duration", "When using the Azure provider, set the zones list cache TTL (0s to disable).").Default(defaultConfig.AzureZonesCacheDuration.String()).DurationVar(&cfg.AzureZonesCacheDuration)
+	app.Flag("azure-maxretries-count", "When using the Azure provider, set the number of retries for API calls (When less than 0, it disables retries). (optional)").Default(strconv.Itoa(defaultConfig.AzureMaxRetriesCount)).IntVar(&cfg.AzureMaxRetriesCount)
 	app.Flag("tencent-cloud-config-file", "When using the Tencent Cloud provider, specify the Tencent Cloud configuration file (required when --provider=tencentcloud)").Default(defaultConfig.TencentCloudConfigFile).StringVar(&cfg.TencentCloudConfigFile)
 	app.Flag("tencent-cloud-zone-type", "When using the Tencent Cloud provider, filter for zones with visibility (optional, options: public, private)").Default(defaultConfig.TencentCloudZoneType).EnumVar(&cfg.TencentCloudZoneType, "", "public", "private")
 
@@ -536,6 +541,8 @@ func App(cfg *Config) *kingpin.Application {
 	app.Flag("cloudflare-custom-hostnames-certificate-authority", "When using the Cloudflare provider with the Custom Hostnames, specify which Cerrtificate Authority will be used by default. (default: google, options: google, ssl_com, lets_encrypt)").Default("google").EnumVar(&cfg.CloudflareCustomHostnamesCertificateAuthority, "google", "ssl_com", "lets_encrypt")
 	app.Flag("cloudflare-dns-records-per-page", "When using the Cloudflare provider, specify how many DNS records listed per page, max possible 5,000 (default: 100)").Default(strconv.Itoa(defaultConfig.CloudflareDNSRecordsPerPage)).IntVar(&cfg.CloudflareDNSRecordsPerPage)
 	app.Flag("cloudflare-region-key", "When using the Cloudflare provider, specify the region (default: earth)").StringVar(&cfg.CloudflareRegionKey)
+	app.Flag("cloudflare-record-comment", "When using the Cloudflare provider, specify the comment for the DNS records (default: '')").Default("").StringVar(&cfg.CloudflareRecordComment)
+
 	app.Flag("coredns-prefix", "When using the CoreDNS provider, specify the prefix name").Default(defaultConfig.CoreDNSPrefix).StringVar(&cfg.CoreDNSPrefix)
 	app.Flag("akamai-serviceconsumerdomain", "When using the Akamai provider, specify the base URL (required when --provider=akamai and edgerc-path not specified)").Default(defaultConfig.AkamaiServiceConsumerDomain).StringVar(&cfg.AkamaiServiceConsumerDomain)
 	app.Flag("akamai-client-token", "When using the Akamai provider, specify the client token (required when --provider=akamai and edgerc-path not specified)").Default(defaultConfig.AkamaiClientToken).StringVar(&cfg.AkamaiClientToken)
diff --git a/pkg/tlsutils/tlsconfig.go b/pkg/tlsutils/tlsconfig.go
index 5275fad1..e6e4373b 100644
--- a/pkg/tlsutils/tlsconfig.go
+++ b/pkg/tlsutils/tlsconfig.go
@@ -35,14 +35,10 @@ func CreateTLSConfig(prefix string) (*tls.Config, error) {
 	serverName := os.Getenv(fmt.Sprintf("%s_TLS_SERVER_NAME", prefix))
 	isInsecureStr := strings.ToLower(os.Getenv(fmt.Sprintf("%s_TLS_INSECURE", prefix)))
 	isInsecure := isInsecureStr == "true" || isInsecureStr == "yes" || isInsecureStr == "1"
-	tlsConfig, err := NewTLSConfig(certFile, keyFile, caFile, serverName, isInsecure, defaultMinVersion)
-	if err != nil {
-		return nil, err
-	}
-	return tlsConfig, nil
+	return NewTLSConfig(certFile, keyFile, caFile, serverName, isInsecure, defaultMinVersion)
 }
 
-// NewTLSConfig creates a tls.Config instance from directly-passed parameters, loading the ca, cert, and key from disk
+// NewTLSConfig creates a tls.Config instance from directly passed parameters, loading the ca, cert, and key from disk
 func NewTLSConfig(certPath, keyPath, caPath, serverName string, insecure bool, minVersion uint16) (*tls.Config, error) {
 	if certPath != "" && keyPath == "" || certPath == "" && keyPath != "" {
 		return nil, errors.New("either both cert and key or none must be provided")
@@ -55,15 +51,21 @@ func NewTLSConfig(certPath, keyPath, caPath, serverName string, insecure bool, m
 		}
 		certificates = append(certificates, cert)
 	}
-	roots, err := loadRoots(caPath)
-	if err != nil {
-		return nil, err
+	// If rootCAs is nil, TLS uses the host's root CA set.
+	var rootCAs *x509.CertPool
+	var err error
+
+	if caPath != "" {
+		rootCAs, err = loadRoots(caPath)
+		if err != nil {
+			return nil, err
+		}
 	}
 
 	return &tls.Config{
 		MinVersion:         minVersion,
 		Certificates:       certificates,
-		RootCAs:            roots,
+		RootCAs:            rootCAs,
 		InsecureSkipVerify: insecure,
 		ServerName:         serverName,
 	}, nil
@@ -71,10 +73,6 @@ func NewTLSConfig(certPath, keyPath, caPath, serverName string, insecure bool, m
 
 // loads CA cert
 func loadRoots(caPath string) (*x509.CertPool, error) {
-	if caPath == "" {
-		return nil, nil
-	}
-
 	roots := x509.NewCertPool()
 	pem, err := os.ReadFile(caPath)
 	if err != nil {
diff --git a/provider/alibabacloud/alibaba_cloud.go b/provider/alibabacloud/alibaba_cloud.go
index 16318385..9ff690c3 100644
--- a/provider/alibabacloud/alibaba_cloud.go
+++ b/provider/alibabacloud/alibaba_cloud.go
@@ -85,13 +85,13 @@ type AlibabaCloudProvider struct {
 }
 
 type alibabaCloudConfig struct {
-	RegionID        string    `json:"regionId" yaml:"regionId"`
-	AccessKeyID     string    `json:"accessKeyId" yaml:"accessKeyId"`
+	RegionID        string    `json:"regionId"        yaml:"regionId"`
+	AccessKeyID     string    `json:"accessKeyId"     yaml:"accessKeyId"`
 	AccessKeySecret string    `json:"accessKeySecret" yaml:"accessKeySecret"`
-	VPCID           string    `json:"vpcId" yaml:"vpcId"`
-	RoleName        string    `json:"-" yaml:"-"` // For ECS RAM role only
-	StsToken        string    `json:"-" yaml:"-"`
-	ExpireTime      time.Time `json:"-" yaml:"-"`
+	VPCID           string    `json:"vpcId"           yaml:"vpcId"`
+	RoleName        string    `json:"-"               yaml:"-"` // For ECS RAM role only
+	StsToken        string    `json:"-"               yaml:"-"`
+	ExpireTime      time.Time `json:"-"               yaml:"-"`
 }
 
 // NewAlibabaCloudProvider creates a new Alibaba Cloud provider.
diff --git a/provider/azure/azure.go b/provider/azure/azure.go
index c71e638e..0d68fe47 100644
--- a/provider/azure/azure.go
+++ b/provider/azure/azure.go
@@ -63,17 +63,19 @@ type AzureProvider struct {
 	zonesClient                  ZonesClient
 	zonesCache                   *zonesCache[dns.Zone]
 	recordSetsClient             RecordSetsClient
+	maxRetriesCount              int
 }
 
 // NewAzureProvider creates a new Azure provider.
 //
 // Returns the provider or an error if a provider could not be created.
-func NewAzureProvider(configFile string, domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, subscriptionID string, resourceGroup string, userAssignedIdentityClientID string, activeDirectoryAuthorityHost string, zonesCacheDuration time.Duration, dryRun bool) (*AzureProvider, error) {
+func NewAzureProvider(configFile string, domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, subscriptionID string, resourceGroup string, userAssignedIdentityClientID string, activeDirectoryAuthorityHost string, zonesCacheDuration time.Duration, maxRetriesCount int, dryRun bool) (*AzureProvider, error) {
 	cfg, err := getConfig(configFile, subscriptionID, resourceGroup, userAssignedIdentityClientID, activeDirectoryAuthorityHost)
 	if err != nil {
 		return nil, fmt.Errorf("failed to read Azure config file '%s': %v", configFile, err)
 	}
-	cred, clientOpts, err := getCredentials(*cfg)
+
+	cred, clientOpts, err := getCredentials(*cfg, maxRetriesCount)
 	if err != nil {
 		return nil, fmt.Errorf("failed to get credentials: %w", err)
 	}
@@ -97,6 +99,7 @@ func NewAzureProvider(configFile string, domainFilter endpoint.DomainFilter, zon
 		zonesClient:                  zonesClient,
 		zonesCache:                   &zonesCache[dns.Zone]{duration: zonesCacheDuration},
 		recordSetsClient:             recordSetsClient,
+		maxRetriesCount:              maxRetriesCount,
 	}, nil
 }
 
diff --git a/provider/azure/azure_private_dns.go b/provider/azure/azure_private_dns.go
index d31fa20a..c199d95a 100644
--- a/provider/azure/azure_private_dns.go
+++ b/provider/azure/azure_private_dns.go
@@ -58,17 +58,19 @@ type AzurePrivateDNSProvider struct {
 	zonesClient                  PrivateZonesClient
 	zonesCache                   *zonesCache[privatedns.PrivateZone]
 	recordSetsClient             PrivateRecordSetsClient
+	maxRetriesCount              int
 }
 
 // NewAzurePrivateDNSProvider creates a new Azure Private DNS provider.
 //
 // Returns the provider or an error if a provider could not be created.
-func NewAzurePrivateDNSProvider(configFile string, domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, subscriptionID string, resourceGroup string, userAssignedIdentityClientID string, activeDirectoryAuthorityHost string, zonesCacheDuration time.Duration, dryRun bool) (*AzurePrivateDNSProvider, error) {
+func NewAzurePrivateDNSProvider(configFile string, domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, subscriptionID string, resourceGroup string, userAssignedIdentityClientID string, activeDirectoryAuthorityHost string, zonesCacheDuration time.Duration, maxRetriesCount int, dryRun bool) (*AzurePrivateDNSProvider, error) {
 	cfg, err := getConfig(configFile, subscriptionID, resourceGroup, userAssignedIdentityClientID, activeDirectoryAuthorityHost)
 	if err != nil {
 		return nil, fmt.Errorf("failed to read Azure config file '%s': %v", configFile, err)
 	}
-	cred, clientOpts, err := getCredentials(*cfg)
+
+	cred, clientOpts, err := getCredentials(*cfg, maxRetriesCount)
 	if err != nil {
 		return nil, fmt.Errorf("failed to get credentials: %w", err)
 	}
@@ -92,6 +94,7 @@ func NewAzurePrivateDNSProvider(configFile string, domainFilter endpoint.DomainF
 		zonesClient:                  zonesClient,
 		zonesCache:                   &zonesCache[privatedns.PrivateZone]{duration: zonesCacheDuration},
 		recordSetsClient:             recordSetsClient,
+		maxRetriesCount:              maxRetriesCount,
 	}, nil
 }
 
diff --git a/provider/azure/config.go b/provider/azure/config.go
index ac24f0b4..e64f3857 100644
--- a/provider/azure/config.go
+++ b/provider/azure/config.go
@@ -17,30 +17,34 @@ limitations under the License.
 package azure
 
 import (
+	"context"
 	"encoding/json"
 	"fmt"
+	"net/http"
 	"os"
 	"strings"
 
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
 	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
+	"github.com/google/uuid"
 	log "github.com/sirupsen/logrus"
 )
 
 // config represents common config items for Azure DNS and Azure Private DNS
 type config struct {
-	Cloud                        string `json:"cloud" yaml:"cloud"`
-	TenantID                     string `json:"tenantId" yaml:"tenantId"`
-	SubscriptionID               string `json:"subscriptionId" yaml:"subscriptionId"`
-	ResourceGroup                string `json:"resourceGroup" yaml:"resourceGroup"`
-	Location                     string `json:"location" yaml:"location"`
-	ClientID                     string `json:"aadClientId" yaml:"aadClientId"`
-	ClientSecret                 string `json:"aadClientSecret" yaml:"aadClientSecret"`
-	UseManagedIdentityExtension  bool   `json:"useManagedIdentityExtension" yaml:"useManagedIdentityExtension"`
+	Cloud                        string `json:"cloud"                        yaml:"cloud"`
+	TenantID                     string `json:"tenantId"                     yaml:"tenantId"`
+	SubscriptionID               string `json:"subscriptionId"               yaml:"subscriptionId"`
+	ResourceGroup                string `json:"resourceGroup"                yaml:"resourceGroup"`
+	Location                     string `json:"location"                     yaml:"location"`
+	ClientID                     string `json:"aadClientId"                  yaml:"aadClientId"`
+	ClientSecret                 string `json:"aadClientSecret"              yaml:"aadClientSecret"`
+	UseManagedIdentityExtension  bool   `json:"useManagedIdentityExtension"  yaml:"useManagedIdentityExtension"`
 	UseWorkloadIdentityExtension bool   `json:"useWorkloadIdentityExtension" yaml:"useWorkloadIdentityExtension"`
-	UserAssignedIdentityID       string `json:"userAssignedIdentityID" yaml:"userAssignedIdentityID"`
+	UserAssignedIdentityID       string `json:"userAssignedIdentityID"       yaml:"userAssignedIdentityID"`
 	ActiveDirectoryAuthorityHost string `json:"activeDirectoryAuthorityHost" yaml:"activeDirectoryAuthorityHost"`
 }
 
@@ -72,15 +76,57 @@ func getConfig(configFile, subscriptionID, resourceGroup, userAssignedIdentityCl
 	return cfg, nil
 }
 
+// ctxKey is a type for context keys
+// This is used to avoid collisions with other packages that may use the same key in the context.
+type ctxKey string
+
+const (
+	// Context key for request ID
+	clientRequestIDKey ctxKey = "client-request-id"
+	// Azure API Headers
+	msRequestIDHeader          = "x-ms-request-id"
+	msCorrelationRequestHeader = "x-ms-correlation-request-id"
+	msClientRequestIDHeader    = "x-ms-client-request-id"
+)
+
+// customHeaderPolicy adds UUID to request headers
+type customHeaderPolicy struct{}
+
+func (p *customHeaderPolicy) Do(req *policy.Request) (*http.Response, error) {
+	id := req.Raw().Header.Get(msClientRequestIDHeader)
+	if id == "" {
+		id = uuid.New().String()
+		req.Raw().Header.Set(msClientRequestIDHeader, id)
+		newCtx := context.WithValue(req.Raw().Context(), clientRequestIDKey, id)
+		*req.Raw() = *req.Raw().WithContext(newCtx)
+	}
+	return req.Next()
+}
+func CustomHeaderPolicynew() policy.Policy { return &customHeaderPolicy{} }
+
 // getCredentials retrieves Azure API credentials.
-func getCredentials(cfg config) (azcore.TokenCredential, *arm.ClientOptions, error) {
+func getCredentials(cfg config, maxRetries int) (azcore.TokenCredential, *arm.ClientOptions, error) {
 	cloudCfg, err := getCloudConfiguration(cfg.Cloud)
 	if err != nil {
 		return nil, nil, fmt.Errorf("failed to get cloud configuration: %w", err)
 	}
 	clientOpts := azcore.ClientOptions{
 		Cloud: cloudCfg,
+		Retry: policy.RetryOptions{
+			MaxRetries: int32(maxRetries),
+		},
+		Logging: policy.LogOptions{
+			AllowedHeaders: []string{
+				msRequestIDHeader,
+				msCorrelationRequestHeader,
+				msClientRequestIDHeader,
+			},
+		},
+		PerCallPolicies: []policy.Policy{
+			CustomHeaderPolicynew(),
+		},
 	}
+	log.Debugf("Configured Azure client with maxRetries: %d", clientOpts.Retry.MaxRetries)
 	armClientOpts := &arm.ClientOptions{
 		ClientOptions: clientOpts,
 	}
diff --git a/provider/cloudflare/cloudflare.go b/provider/cloudflare/cloudflare.go
index 87c41ad2..29dd8000 100644
--- a/provider/cloudflare/cloudflare.go
+++ b/provider/cloudflare/cloudflare.go
@@ -28,8 +28,9 @@ import (
 	"strconv"
 	"strings"
 
-	cloudflare "github.com/cloudflare/cloudflare-go"
+	"github.com/cloudflare/cloudflare-go"
 	log "github.com/sirupsen/logrus"
+	"golang.org/x/net/publicsuffix"
 
 	"sigs.k8s.io/external-dns/endpoint"
 	"sigs.k8s.io/external-dns/plan"
@@ -46,6 +47,10 @@ const (
 	cloudFlareUpdate = "UPDATE"
 	// defaultTTL 1 = automatic
 	defaultTTL = 1
+
+	// Cloudflare tier limitations https://developers.cloudflare.com/dns/manage-dns-records/reference/record-attributes/#availability
+	freeZoneMaxCommentLength = 100
+	paidZoneMaxCommentLength = 500
 )
 
 // We have to use pointers to bools now, as the upstream cloudflare-go library requires them
@@ -190,6 +195,45 @@ func (z zoneService) CreateCustomHostname(ctx context.Context, zoneID string, ch
 	return z.service.CreateCustomHostname(ctx, zoneID, ch)
 }
 
+type DNSRecordsConfig struct {
+	PerPage int
+	Comment string
+}
+
+func (c *DNSRecordsConfig) trimAndValidateComment(dnsName, comment string, paidZone func(string) bool) string {
+	if len(comment) > freeZoneMaxCommentLength {
+		if !paidZone(dnsName) {
+			log.Warnf("DNS record comment is invalid. Trimming comment of %s. To avoid endless syncs, please set it to less than %d chars.", dnsName, freeZoneMaxCommentLength)
+			return comment[:freeZoneMaxCommentLength]
+		} else if len(comment) > paidZoneMaxCommentLength {
+			log.Warnf("DNS record comment is invalid. Trimming comment of %s. To avoid endless syncs, please set it to less than %d chars.", dnsName, paidZoneMaxCommentLength)
+			return comment[:paidZoneMaxCommentLength]
+		}
+	}
+	return comment
+}
+
+func (p *CloudFlareProvider) ZoneHasPaidPlan(hostname string) bool {
+	zone, err := publicsuffix.EffectiveTLDPlusOne(hostname)
+	if err != nil {
+		log.Errorf("Failed to get effective TLD+1 for hostname %s %v", hostname, err)
+		return false
+	}
+	zoneID, err := p.Client.ZoneIDByName(zone)
+	if err != nil {
+		log.Errorf("Failed to get zone %s by name %v", zone, err)
+		return false
+	}
+
+	zoneDetails, err := p.Client.ZoneDetails(context.Background(), zoneID)
+	if err != nil {
+		log.Errorf("Failed to get zone %s details %v", zone, err)
+		return false
+	}
+
+	return zoneDetails.Plan.IsSubscribed
+}
+
 // CloudFlareProvider is an implementation of Provider for CloudFlare DNS.
 type CloudFlareProvider struct {
 	provider.BaseProvider
@@ -198,9 +242,9 @@ type CloudFlareProvider struct {
 	domainFilter          endpoint.DomainFilter
 	zoneIDFilter          provider.ZoneIDFilter
 	proxiedByDefault      bool
-	CustomHostnamesConfig CustomHostnamesConfig
 	DryRun                bool
-	DNSRecordsPerPage     int
+	CustomHostnamesConfig CustomHostnamesConfig
+	DNSRecordsConfig      DNSRecordsConfig
 	RegionKey             string
 }
 
@@ -257,7 +301,7 @@ func getCreateDNSRecordParam(cfc cloudFlareChange) cloudflare.CreateDNSRecordPar
 }
 
 // NewCloudFlareProvider initializes a new CloudFlare DNS based Provider.
-func NewCloudFlareProvider(domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, proxiedByDefault bool, dryRun bool, dnsRecordsPerPage int, regionKey string, customHostnamesConfig CustomHostnamesConfig) (*CloudFlareProvider, error) {
+func NewCloudFlareProvider(domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, proxiedByDefault bool, dryRun bool, regionKey string, customHostnamesConfig CustomHostnamesConfig, dnsRecordsConfig DNSRecordsConfig) (*CloudFlareProvider, error) {
 	// initialize via chosen auth method and returns new API object
 	var (
 		config *cloudflare.API
@@ -287,8 +331,8 @@ func NewCloudFlareProvider(domainFilter endpoint.DomainFilter, zoneIDFilter prov
 		proxiedByDefault:      proxiedByDefault,
 		CustomHostnamesConfig: customHostnamesConfig,
 		DryRun:                dryRun,
-		DNSRecordsPerPage:     dnsRecordsPerPage,
 		RegionKey:             regionKey,
+		DNSRecordsConfig:      dnsRecordsConfig,
 	}, nil
 }
 
@@ -825,6 +869,18 @@ func (p *CloudFlareProvider) newCloudFlareChange(action string, ep *endpoint.End
 			RegionKey: regionKey,
 		}
 	}
+
+	// Load comment from program flag
+	comment := p.DNSRecordsConfig.Comment
+	if val, ok := ep.GetProviderSpecificProperty(annotations.CloudflareRecordCommentKey); ok {
+		// Replace comment with Ingress annotation
+		comment = val
+	}
+
+	if len(comment) > freeZoneMaxCommentLength {
+		comment = p.DNSRecordsConfig.trimAndValidateComment(ep.DNSName, comment, p.ZoneHasPaidPlan)
+	}
+
 	return &cloudFlareChange{
 		Action: action,
 		ResourceRecord: cloudflare.DNSRecord{
@@ -835,6 +891,7 @@ func (p *CloudFlareProvider) newCloudFlareChange(action string, ep *endpoint.End
 			Proxied: &proxied,
 			Type:    ep.RecordType,
 			Content: target,
+			Comment: comment,
 		},
 		RegionalHostname:    regionalHostname,
 		CustomHostnamesPrev: prevCustomHostnames,
@@ -850,7 +907,7 @@ func newDNSRecordIndex(r cloudflare.DNSRecord) DNSRecordIndex {
 func (p *CloudFlareProvider) listDNSRecordsWithAutoPagination(ctx context.Context, zoneID string) (DNSRecordsMap, error) {
 	// for faster getRecordID lookup
 	records := make(DNSRecordsMap)
-	resultInfo := cloudflare.ResultInfo{PerPage: p.DNSRecordsPerPage, Page: 1}
+	resultInfo := cloudflare.ResultInfo{PerPage: p.DNSRecordsConfig.PerPage, Page: 1}
 	params := cloudflare.ListDNSRecordsParams{ResultInfo: resultInfo}
 	for {
 		pageRecords, resultInfo, err := p.Client.ListDNSRecords(ctx, cloudflare.ZoneIdentifier(zoneID), params)
@@ -1021,6 +1078,10 @@ func groupByNameAndTypeWithCustomHostnames(records DNSRecordsMap, chs CustomHost
 			e = e.WithProviderSpecific(annotations.CloudflareCustomHostnameKey, strings.Join(customHostnames, ","))
 		}
 
+		if records[0].Comment != "" {
+			e = e.WithProviderSpecific(annotations.CloudflareRecordCommentKey, records[0].Comment)
+		}
+
 		endpoints = append(endpoints, e)
 	}
 
diff --git a/provider/coredns/coredns.go b/provider/coredns/coredns.go
index 18dc9cea..21fb2929 100644
--- a/provider/coredns/coredns.go
+++ b/provider/coredns/coredns.go
@@ -18,8 +18,6 @@ package coredns
 
 import (
 	"context"
-	"crypto/tls"
-	"crypto/x509"
 	"encoding/json"
 	"errors"
 	"fmt"
@@ -32,6 +30,8 @@ import (
 	log "github.com/sirupsen/logrus"
 	etcdcv3 "go.etcd.io/etcd/client/v3"
 
+	"sigs.k8s.io/external-dns/pkg/tlsutils"
+
 	"sigs.k8s.io/external-dns/endpoint"
 	"sigs.k8s.io/external-dns/plan"
 	"sigs.k8s.io/external-dns/provider"
@@ -153,50 +153,6 @@ func (c etcdClient) DeleteService(key string) error {
 	return err
 }
 
-// loads TLS artifacts and builds tls.Config object
-func newTLSConfig(certPath, keyPath, caPath, serverName string, insecure bool) (*tls.Config, error) {
-	if certPath != "" && keyPath == "" || certPath == "" && keyPath != "" {
-		return nil, errors.New("either both cert and key or none must be provided")
-	}
-	var certificates []tls.Certificate
-	if certPath != "" {
-		cert, err := tls.LoadX509KeyPair(certPath, keyPath)
-		if err != nil {
-			return nil, fmt.Errorf("could not load TLS cert: %w", err)
-		}
-		certificates = append(certificates, cert)
-	}
-	roots, err := loadRoots(caPath)
-	if err != nil {
-		return nil, err
-	}
-
-	return &tls.Config{
-		Certificates:       certificates,
-		RootCAs:            roots,
-		InsecureSkipVerify: insecure,
-		ServerName:         serverName,
-	}, nil
-}
-
-// loads CA cert
-func loadRoots(caPath string) (*x509.CertPool, error) {
-	if caPath == "" {
-		return nil, nil
-	}
-
-	roots := x509.NewCertPool()
-	pem, err := os.ReadFile(caPath)
-	if err != nil {
-		return nil, fmt.Errorf("error reading %s: %w", caPath, err)
-	}
-	ok := roots.AppendCertsFromPEM(pem)
-	if !ok {
-		return nil, fmt.Errorf("could not read root certs: %w", err)
-	}
-	return roots, nil
-}
-
 // builds etcd client config depending on connection scheme and TLS parameters
 func getETCDConfig() (*etcdcv3.Config, error) {
 	etcdURLsStr := os.Getenv("ETCD_URLS")
@@ -210,16 +166,11 @@ func getETCDConfig() (*etcdcv3.Config, error) {
 	if strings.HasPrefix(firstURL, "http://") {
 		return &etcdcv3.Config{Endpoints: etcdURLs, Username: etcdUsername, Password: etcdPassword}, nil
 	} else if strings.HasPrefix(firstURL, "https://") {
-		caFile := os.Getenv("ETCD_CA_FILE")
-		certFile := os.Getenv("ETCD_CERT_FILE")
-		keyFile := os.Getenv("ETCD_KEY_FILE")
-		serverName := os.Getenv("ETCD_TLS_SERVER_NAME")
-		isInsecureStr := strings.ToLower(os.Getenv("ETCD_TLS_INSECURE"))
-		isInsecure := isInsecureStr == "true" || isInsecureStr == "yes" || isInsecureStr == "1"
-		tlsConfig, err := newTLSConfig(certFile, keyFile, caFile, serverName, isInsecure)
+		tlsConfig, err := tlsutils.CreateTLSConfig("ETCD")
 		if err != nil {
 			return nil, err
 		}
+		log.Debug("using TLS for etcd")
 		return &etcdcv3.Config{
 			Endpoints: etcdURLs,
 			TLS:       tlsConfig,
@@ -231,7 +182,7 @@ func getETCDConfig() (*etcdcv3.Config, error) {
 	}
 }
 
-// newETCDClient is an etcd client constructor
+// the newETCDClient is an etcd client constructor
 func newETCDClient() (coreDNSClient, error) {
 	cfg, err := getETCDConfig()
 	if err != nil {
@@ -283,7 +234,7 @@ func findLabelInTargets(targets []string, label string) (string, bool) {
 
 // Records returns all DNS records found in CoreDNS etcd backend. Depending on the record fields
 // it may be mapped to one or two records of type A, CNAME, TXT, A+TXT, CNAME+TXT
-func (p coreDNSProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) {
+func (p coreDNSProvider) Records(_ context.Context) ([]*endpoint.Endpoint, error) {
 	var result []*endpoint.Endpoint
 	services, err := p.client.GetServices(p.coreDNSPrefix)
 	if err != nil {
diff --git a/provider/ibmcloud/ibmcloud.go b/provider/ibmcloud/ibmcloud.go
index 85bc8ae7..980789c8 100644
--- a/provider/ibmcloud/ibmcloud.go
+++ b/provider/ibmcloud/ibmcloud.go
@@ -186,11 +186,11 @@ type IBMCloudProvider struct {
 }
 
 type ibmcloudConfig struct {
-	Endpoint   string `json:"endpoint" yaml:"endpoint"`
-	APIKey     string `json:"apiKey" yaml:"apiKey"`
+	Endpoint   string `json:"endpoint"    yaml:"endpoint"`
+	APIKey     string `json:"apiKey"      yaml:"apiKey"`
 	CRN        string `json:"instanceCrn" yaml:"instanceCrn"`
-	IAMURL     string `json:"iamUrl" yaml:"iamUrl"`
-	InstanceID string `json:"-" yaml:"-"`
+	IAMURL     string `json:"iamUrl"      yaml:"iamUrl"`
+	InstanceID string `json:"-"           yaml:"-"`
 }
 
 // ibmcloudChange differentiates between ChangActions
diff --git a/provider/tencentcloud/tencent_cloud.go b/provider/tencentcloud/tencent_cloud.go
index e52a4df8..f4435a01 100644
--- a/provider/tencentcloud/tencent_cloud.go
+++ b/provider/tencentcloud/tencent_cloud.go
@@ -75,10 +75,10 @@ type TencentCloudProvider struct {
 }
 
 type tencentCloudConfig struct {
-	RegionId         string `json:"regionId" yaml:"regionId"`
-	SecretId         string `json:"secretId" yaml:"secretId"`
-	SecretKey        string `json:"secretKey" yaml:"secretKey"`
-	VPCId            string `json:"vpcId" yaml:"vpcId"`
+	RegionId         string `json:"regionId"         yaml:"regionId"`
+	SecretId         string `json:"secretId"         yaml:"secretId"`
+	SecretKey        string `json:"secretKey"        yaml:"secretKey"`
+	VPCId            string `json:"vpcId"            yaml:"vpcId"`
 	InternetEndpoint bool   `json:"internetEndpoint" yaml:"internetEndpoint"`
 }
 
diff --git a/source/annotations/annotations.go b/source/annotations/annotations.go
index fdec57b7..5c98516e 100644
--- a/source/annotations/annotations.go
+++ b/source/annotations/annotations.go
@@ -22,6 +22,7 @@ const (
 	CloudflareProxiedKey        = "external-dns.alpha.kubernetes.io/cloudflare-proxied"
 	CloudflareCustomHostnameKey = "external-dns.alpha.kubernetes.io/cloudflare-custom-hostname"
 	CloudflareRegionKey         = "external-dns.alpha.kubernetes.io/cloudflare-region-key"
+	CloudflareRecordCommentKey  = "external-dns.alpha.kubernetes.io/cloudflare-record-comment"
 
 	AWSPrefix        = "external-dns.alpha.kubernetes.io/aws-"
 	SCWPrefix        = "external-dns.alpha.kubernetes.io/scw-"
diff --git a/source/annotations/provider_specific.go b/source/annotations/provider_specific.go
index 0fb65d9d..99d932c3 100644
--- a/source/annotations/provider_specific.go
+++ b/source/annotations/provider_specific.go
@@ -74,6 +74,11 @@ func ProviderSpecificAnnotations(annotations map[string]string) (endpoint.Provid
 					Name:  CloudflareRegionKey,
 					Value: v,
 				})
+			} else if strings.Contains(k, CloudflareRecordCommentKey) {
+				providerSpecificAnnotations = append(providerSpecificAnnotations, endpoint.ProviderSpecificProperty{
+					Name:  CloudflareRecordCommentKey,
+					Value: v,
+				})
 			}
 		}
 	}
diff --git a/source/compatibility.go b/source/compatibility.go
index 373daba0..fc54552f 100644
--- a/source/compatibility.go
+++ b/source/compatibility.go
@@ -159,7 +159,7 @@ func legacyEndpointsFromDNSControllerNodePortService(svc *v1.Service, sc *servic
 			for _, address := range node.Status.Addresses {
 				recordType := suitableType(address.Address)
 				// IPv6 addresses are labeled as NodeInternalIP despite being usable externally as well.
-				if isExternal && (address.Type == v1.NodeExternalIP || (address.Type == v1.NodeInternalIP && recordType == endpoint.RecordTypeAAAA)) {
+				if isExternal && (address.Type == v1.NodeExternalIP || (sc.exposeInternalIPv6 && address.Type == v1.NodeInternalIP && recordType == endpoint.RecordTypeAAAA)) {
 					endpoints = append(endpoints, endpoint.NewEndpoint(hostname, recordType, address.Address))
 				}
 				if isInternal && address.Type == v1.NodeInternalIP {
diff --git a/source/istio_virtualservice.go b/source/istio_virtualservice.go
index be9407d6..7e76f597 100644
--- a/source/istio_virtualservice.go
+++ b/source/istio_virtualservice.go
@@ -17,6 +17,7 @@ limitations under the License.
 package source
 
 import (
+	"cmp"
 	"context"
 	"fmt"
 	"sort"
@@ -193,7 +194,7 @@ func (sc *virtualServiceSource) Endpoints(ctx context.Context) ([]*endpoint.Endp
 }
 
 // AddEventHandler adds an event handler that should be triggered if the watched Istio VirtualService changes.
-func (sc *virtualServiceSource) AddEventHandler(ctx context.Context, handler func()) {
+func (sc *virtualServiceSource) AddEventHandler(_ context.Context, handler func()) {
 	log.Debug("Adding event handler for Istio VirtualService")
 
 	sc.virtualserviceInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
@@ -210,21 +211,19 @@ func (sc *virtualServiceSource) getGateway(_ context.Context, gatewayStr string,
 		log.Debugf("Failed parsing gatewayStr %s of VirtualService %s/%s", gatewayStr, virtualService.Namespace, virtualService.Name)
 		return nil, err
 	}
-	if namespace == "" {
-		namespace = virtualService.Namespace
-	}
+	namespace = cmp.Or(namespace, virtualService.Namespace)
 
 	gateway, err := sc.gatewayInformer.Lister().Gateways(namespace).Get(name)
 	if errors.IsNotFound(err) {
 		log.Warnf("VirtualService (%s/%s) references non-existent gateway: %s ", virtualService.Namespace, virtualService.Name, gatewayStr)
-		return nil, nil
+		return gateway, nil
 	} else if err != nil {
 		log.Errorf("Failed retrieving gateway %s referenced by VirtualService %s/%s: %v", gatewayStr, virtualService.Namespace, virtualService.Name, err)
 		return nil, err
 	}
 	if gateway == nil {
 		log.Debugf("Gateway %s referenced by VirtualService %s/%s not found: %v", gatewayStr, virtualService.Namespace, virtualService.Name, err)
-		return nil, nil
+		return gateway, nil
 	}
 	return gateway, nil
 }
@@ -290,17 +289,17 @@ func (sc *virtualServiceSource) targetsFromVirtualService(ctx context.Context, v
 	var targets []string
 	// for each host we need to iterate through the gateways because each host might match for only one of the gateways
 	for _, gateway := range virtualService.Spec.Gateways {
-		gateway, err := sc.getGateway(ctx, gateway, virtualService)
+		gw, err := sc.getGateway(ctx, gateway, virtualService)
 		if err != nil {
 			return nil, err
 		}
-		if gateway == nil {
+		if gw == nil {
 			continue
 		}
-		if !virtualServiceBindsToGateway(virtualService, gateway, vsHost) {
+		if !virtualServiceBindsToGateway(virtualService, gw, vsHost) {
 			continue
 		}
-		tgs, err := sc.targetsFromGateway(ctx, gateway)
+		tgs, err := sc.targetsFromGateway(ctx, gw)
 		if err != nil {
 			return targets, err
 		}
diff --git a/source/node.go b/source/node.go
index 055cd38a..3f76e665 100644
--- a/source/node.go
+++ b/source/node.go
@@ -43,7 +43,7 @@ type nodeSource struct {
 	nodeInformer         coreinformers.NodeInformer
 	labelSelector        labels.Selector
 	excludeUnschedulable bool
-	exposeInternalIPV6   bool
+	exposeInternalIPv6   bool
 }
 
 // NewNodeSource creates a new nodeSource with the given config.
@@ -81,7 +81,7 @@ func NewNodeSource(ctx context.Context, kubeClient kubernetes.Interface, annotat
 		nodeInformer:         nodeInformer,
 		labelSelector:        labelSelector,
 		excludeUnschedulable: excludeUnschedulable,
-		exposeInternalIPV6:   exposeInternalIPv6,
+		exposeInternalIPv6:   exposeInternalIPv6,
 	}, nil
 }
 
@@ -193,7 +193,7 @@ func (ns *nodeSource) nodeAddresses(node *v1.Node) ([]string, error) {
 	}
 
 	if len(addresses[v1.NodeExternalIP]) > 0 {
-		if ns.exposeInternalIPV6 {
+		if ns.exposeInternalIPv6 {
 			log.Warn(warningMsg)
 			return append(addresses[v1.NodeExternalIP], internalIpv6Addresses...), nil
 		}
diff --git a/source/service.go b/source/service.go
index 2c33dcea..c341d3f8 100644
--- a/source/service.go
+++ b/source/service.go
@@ -65,10 +65,11 @@ type serviceSource struct {
 	nodeInformer                   coreinformers.NodeInformer
 	serviceTypeFilter              map[string]struct{}
 	labelSelector                  labels.Selector
+	exposeInternalIPv6             bool
 }
 
 // NewServiceSource creates a new serviceSource with the given config.
-func NewServiceSource(ctx context.Context, kubeClient kubernetes.Interface, namespace, annotationFilter, fqdnTemplate string, combineFqdnAnnotation bool, compatibility string, publishInternal, publishHostIP, alwaysPublishNotReadyAddresses bool, serviceTypeFilter []string, ignoreHostnameAnnotation bool, labelSelector labels.Selector, resolveLoadBalancerHostname, listenEndpointEvents bool) (Source, error) {
+func NewServiceSource(ctx context.Context, kubeClient kubernetes.Interface, namespace, annotationFilter, fqdnTemplate string, combineFqdnAnnotation bool, compatibility string, publishInternal, publishHostIP, alwaysPublishNotReadyAddresses bool, serviceTypeFilter []string, ignoreHostnameAnnotation bool, labelSelector labels.Selector, resolveLoadBalancerHostname, listenEndpointEvents bool, exposeInternalIPv6 bool) (Source, error) {
 	tmpl, err := fqdn.ParseTemplate(fqdnTemplate)
 	if err != nil {
 		return nil, err
@@ -141,6 +142,7 @@ func NewServiceSource(ctx context.Context, kubeClient kubernetes.Interface, name
 		labelSelector:                  labelSelector,
 		resolveLoadBalancerHostname:    resolveLoadBalancerHostname,
 		listenEndpointEvents:           listenEndpointEvents,
+		exposeInternalIPv6:             exposeInternalIPv6,
 	}, nil
 }
 
@@ -319,7 +321,7 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri
 							return endpoints
 						}
 						for _, address := range node.Status.Addresses {
-							if address.Type == v1.NodeExternalIP || (address.Type == v1.NodeInternalIP && suitableType(address.Address) == endpoint.RecordTypeAAAA) {
+							if address.Type == v1.NodeExternalIP || (sc.exposeInternalIPv6 && address.Type == v1.NodeInternalIP && suitableType(address.Address) == endpoint.RecordTypeAAAA) {
 								targets = append(targets, address.Address)
 								log.Debugf("Generating matching endpoint %s with NodeExternalIP %s", headlessDomain, address.Address)
 							}
diff --git a/source/store.go b/source/store.go
index 8a9149ba..f8ef39b5 100644
--- a/source/store.go
+++ b/source/store.go
@@ -273,7 +273,7 @@ func BuildWithConfig(ctx context.Context, source string, p ClientGenerator, cfg
 		if err != nil {
 			return nil, err
 		}
-		return NewServiceSource(ctx, client, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.Compatibility, cfg.PublishInternal, cfg.PublishHostIP, cfg.AlwaysPublishNotReadyAddresses, cfg.ServiceTypeFilter, cfg.IgnoreHostnameAnnotation, cfg.LabelFilter, cfg.ResolveLoadBalancerHostname, cfg.ListenEndpointEvents)
+		return NewServiceSource(ctx, client, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.Compatibility, cfg.PublishInternal, cfg.PublishHostIP, cfg.AlwaysPublishNotReadyAddresses, cfg.ServiceTypeFilter, cfg.IgnoreHostnameAnnotation, cfg.LabelFilter, cfg.ResolveLoadBalancerHostname, cfg.ListenEndpointEvents, cfg.ExposeInternalIPv6)
 	case "ingress":
 		client, err := p.KubeClient()
 		if err != nil {



Test patch:
diff --git a/endpoint/endpoint_test.go b/endpoint/endpoint_test.go
index 6c805ecf..6776b81a 100644
--- a/endpoint/endpoint_test.go
+++ b/endpoint/endpoint_test.go
@@ -17,6 +17,7 @@ limitations under the License.
 package endpoint
 
 import (
+	"fmt"
 	"reflect"
 	"testing"
 
@@ -38,6 +39,39 @@ func TestNewEndpoint(t *testing.T) {
 	}
 }
 
+func TestNewTargets(t *testing.T) {
+	cases := []struct {
+		name     string
+		input    []string
+		expected Targets
+	}{
+		{
+			name:     "no targets",
+			input:    []string{},
+			expected: Targets{},
+		},
+		{
+			name:     "single target",
+			input:    []string{"1.2.3.4"},
+			expected: Targets{"1.2.3.4"},
+		},
+		{
+			name:     "multiple targets",
+			input:    []string{"example.com", "8.8.8.8", "::0001"},
+			expected: Targets{"example.com", "8.8.8.8", "::0001"},
+		},
+	}
+
+	for _, c := range cases {
+		t.Run(c.name, func(t *testing.T) {
+			Targets := NewTargets(c.input...)
+			changedTarget := Targets.String()
+			assert.Equal(t, c.expected.String(), changedTarget)
+
+		})
+	}
+}
+
 func TestTargetsSame(t *testing.T) {
 	tests := []Targets{
 		{""},
@@ -155,6 +189,249 @@ func TestIsLess(t *testing.T) {
 	}
 }
 
+func TestGetProviderSpecificProperty(t *testing.T) {
+	e := &Endpoint{
+		ProviderSpecific: []ProviderSpecificProperty{
+			ProviderSpecificProperty{
+				Name:  "name",
+				Value: "value",
+			},
+		},
+	}
+
+	t.Run("key is not present in provider specific", func(t *testing.T) {
+		val, ok := e.GetProviderSpecificProperty("hello")
+		assert.Equal(t, false, ok)
+		assert.Empty(t, val)
+	})
+
+	t.Run("key is present in provider specific", func(t *testing.T) {
+		val, ok := e.GetProviderSpecificProperty("name")
+		assert.Equal(t, true, ok)
+		assert.NotEmpty(t, val)
+
+	})
+}
+
+func TestSetProviderSpecficProperty(t *testing.T) {
+	cases := []struct {
+		name               string
+		endpoint           Endpoint
+		key                string
+		value              string
+		expectedIdentifier string
+		expected           []ProviderSpecificProperty
+	}{
+		{
+			name:     "endpoint is empty",
+			endpoint: Endpoint{},
+			key:      "key1",
+			value:    "value1",
+			expected: []ProviderSpecificProperty{
+				ProviderSpecificProperty{
+					Name:  "key1",
+					Value: "value1",
+				},
+			},
+		},
+		{
+			name: "name and key are not matching",
+			endpoint: Endpoint{
+				DNSName:       "example.org",
+				RecordTTL:     TTL(0),
+				RecordType:    RecordTypeA,
+				SetIdentifier: "newIdentifier",
+				Targets: Targets{
+					"example.org", "example.com", "1.2.4.5",
+				},
+				ProviderSpecific: []ProviderSpecificProperty{
+					ProviderSpecificProperty{
+						Name:  "name1",
+						Value: "value1",
+					},
+				},
+			},
+			expectedIdentifier: "newIdentifier",
+			key:                "name2",
+			value:              "value2",
+
+			expected: []ProviderSpecificProperty{
+				ProviderSpecificProperty{
+					Name:  "name1",
+					Value: "value1",
+				},
+				ProviderSpecificProperty{
+					Name:  "name2",
+					Value: "value2",
+				},
+			},
+		},
+		{
+			name: "some keys are matching and some are not matching ",
+			endpoint: Endpoint{
+				DNSName:       "example.org",
+				RecordTTL:     TTL(0),
+				RecordType:    RecordTypeA,
+				SetIdentifier: "newIdentifier",
+				Targets: Targets{
+					"example.org", "example.com", "1.2.4.5",
+				},
+				ProviderSpecific: []ProviderSpecificProperty{
+					ProviderSpecificProperty{
+						Name:  "name1",
+						Value: "value1",
+					},
+					ProviderSpecificProperty{
+						Name:  "name2",
+						Value: "value2",
+					},
+					ProviderSpecificProperty{
+						Name:  "name3",
+						Value: "value3",
+					},
+				},
+			},
+			key:                "name2",
+			value:              "value2",
+			expectedIdentifier: "newIdentifier",
+			expected: []ProviderSpecificProperty{
+				ProviderSpecificProperty{
+					Name:  "name1",
+					Value: "value1",
+				},
+				ProviderSpecificProperty{
+					Name:  "name2",
+					Value: "value2",
+				},
+				ProviderSpecificProperty{
+					Name:  "name3",
+					Value: "value3",
+				},
+			},
+		},
+		{
+			name: "name and key are not matching",
+			endpoint: Endpoint{
+				DNSName:       "example.org",
+				RecordTTL:     TTL(0),
+				RecordType:    RecordTypeA,
+				SetIdentifier: "identifier",
+				Targets: Targets{
+					"example.org", "example.com", "1.2.4.5",
+				},
+				ProviderSpecific: []ProviderSpecificProperty{
+					ProviderSpecificProperty{
+						Name:  "name1",
+						Value: "value1",
+					},
+				},
+			},
+			key:                "name1",
+			value:              "value2",
+			expectedIdentifier: "identifier",
+			expected: []ProviderSpecificProperty{
+				ProviderSpecificProperty{
+					Name:  "name1",
+					Value: "value2",
+				},
+			},
+		},
+	}
+
+	for _, c := range cases {
+		t.Run(c.name, func(t *testing.T) {
+			c.endpoint.WithProviderSpecific(c.key, c.value)
+			expectedString := fmt.Sprintf("%s %d IN %s %s %s %s", c.endpoint.DNSName, c.endpoint.RecordTTL, c.endpoint.RecordType, c.endpoint.SetIdentifier, c.endpoint.Targets, c.endpoint.ProviderSpecific)
+			identifier := c.endpoint.WithSetIdentifier(c.endpoint.SetIdentifier)
+			assert.Equal(t, c.expectedIdentifier, identifier.SetIdentifier)
+			assert.Equal(t, expectedString, c.endpoint.String())
+			if !reflect.DeepEqual([]ProviderSpecificProperty(c.endpoint.ProviderSpecific), c.expected) {
+				t.Errorf("unexpected ProviderSpecific:\nGot:      %#v\nExpected: %#v", c.endpoint.ProviderSpecific, c.expected)
+			}
+		})
+	}
+}
+
+func TestDeleteProviderSpecificProperty(t *testing.T) {
+	cases := []struct {
+		name     string
+		endpoint Endpoint
+		key      string
+		expected []ProviderSpecificProperty
+	}{
+		{
+			name: "name and key are not matching",
+			endpoint: Endpoint{
+				ProviderSpecific: []ProviderSpecificProperty{
+					ProviderSpecificProperty{
+						Name:  "name1",
+						Value: "value1",
+					},
+				},
+			},
+			key: "name2",
+			expected: []ProviderSpecificProperty{
+				ProviderSpecificProperty{
+					Name:  "name1",
+					Value: "value1",
+				},
+			},
+		},
+		{
+			name: "some keys are matching and some keys are not matching",
+			endpoint: Endpoint{
+				ProviderSpecific: []ProviderSpecificProperty{
+					ProviderSpecificProperty{
+						Name:  "name1",
+						Value: "value1",
+					},
+					ProviderSpecificProperty{
+						Name:  "name2",
+						Value: "value2",
+					},
+					ProviderSpecificProperty{
+						Name:  "name3",
+						Value: "value3",
+					},
+				},
+			},
+			key: "name2",
+			expected: []ProviderSpecificProperty{
+				ProviderSpecificProperty{
+					Name:  "name1",
+					Value: "value1",
+				},
+				ProviderSpecificProperty{
+					Name:  "name3",
+					Value: "value3",
+				},
+			},
+		},
+		{
+			name: "name and key are matching",
+			endpoint: Endpoint{
+				ProviderSpecific: []ProviderSpecificProperty{
+					ProviderSpecificProperty{
+						Name:  "name1",
+						Value: "value1",
+					},
+				},
+			},
+			key:      "name1",
+			expected: []ProviderSpecificProperty{},
+		},
+	}
+
+	for _, c := range cases {
+		t.Run(c.name, func(t *testing.T) {
+			c.endpoint.DeleteProviderSpecificProperty(c.key)
+			if !reflect.DeepEqual([]ProviderSpecificProperty(c.endpoint.ProviderSpecific), c.expected) {
+				t.Errorf("unexpected ProviderSpecific:\nGot:      %#v\nExpected: %#v", c.endpoint.ProviderSpecific, c.expected)
+			}
+		})
+	}
+}
+
 func TestFilterEndpointsByOwnerIDWithRecordTypeA(t *testing.T) {
 	foo1 := &Endpoint{
 		DNSName:    "foo.com",
diff --git a/internal/testutils/env.go b/internal/testutils/env.go
new file mode 100644
index 00000000..7f90597a
--- /dev/null
+++ b/internal/testutils/env.go
@@ -0,0 +1,29 @@
+/*
+Copyright 2025 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package testutils
+
+import (
+	"testing"
+)
+
+func TestHelperEnvSetter(t *testing.T, envs map[string]string) {
+	t.Helper()
+
+	for name, value := range envs {
+		t.Setenv(name, value)
+	}
+}
diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go
index b9b257c8..305ca9cd 100644
--- a/pkg/apis/externaldns/types_test.go
+++ b/pkg/apis/externaldns/types_test.go
@@ -73,11 +73,13 @@ var (
 		AzureConfigFile:                        "/etc/kubernetes/azure.json",
 		AzureResourceGroup:                     "",
 		AzureSubscriptionID:                    "",
+		AzureMaxRetriesCount:                   3,
 		CloudflareProxied:                      false,
 		CloudflareCustomHostnames:              false,
 		CloudflareCustomHostnamesMinTLSVersion: "1.0",
 		CloudflareCustomHostnamesCertificateAuthority: "google",
 		CloudflareDNSRecordsPerPage:                   100,
+		CloudflareDNSRecordsComment:                   "",
 		CloudflareRegionKey:                           "",
 		CoreDNSPrefix:                                 "/skydns/",
 		AkamaiServiceConsumerDomain:                   "",
@@ -183,6 +185,7 @@ var (
 		AzureConfigFile:                        "azure.json",
 		AzureResourceGroup:                     "arg",
 		AzureSubscriptionID:                    "arg",
+		AzureMaxRetriesCount:                   4,
 		CloudflareProxied:                      true,
 		CloudflareCustomHostnames:              true,
 		CloudflareCustomHostnamesMinTLSVersion: "1.3",
@@ -296,6 +299,7 @@ func TestParseFlags(t *testing.T) {
 				"--azure-config-file=azure.json",
 				"--azure-resource-group=arg",
 				"--azure-subscription-id=arg",
+				"--azure-maxretries-count=4",
 				"--cloudflare-proxied",
 				"--cloudflare-custom-hostnames",
 				"--cloudflare-custom-hostnames-min-tls-version=1.3",
@@ -427,6 +431,7 @@ func TestParseFlags(t *testing.T) {
 				"EXTERNAL_DNS_AZURE_CONFIG_FILE":                                 "azure.json",
 				"EXTERNAL_DNS_AZURE_RESOURCE_GROUP":                              "arg",
 				"EXTERNAL_DNS_AZURE_SUBSCRIPTION_ID":                             "arg",
+				"EXTERNAL_DNS_AZURE_MAXRETRIES_COUNT":                            "4",
 				"EXTERNAL_DNS_CLOUDFLARE_PROXIED":                                "1",
 				"EXTERNAL_DNS_CLOUDFLARE_CUSTOM_HOSTNAMES":                       "1",
 				"EXTERNAL_DNS_CLOUDFLARE_CUSTOM_HOSTNAMES_MIN_TLS_VERSION":       "1.3",
diff --git a/provider/azure/azure_privatedns_test.go b/provider/azure/azure_privatedns_test.go
index cc922349..2f980ccd 100644
--- a/provider/azure/azure_privatedns_test.go
+++ b/provider/azure/azure_privatedns_test.go
@@ -224,13 +224,13 @@ func createPrivateMockRecordSetMultiWithTTL(name, recordType string, ttl int64,
 }
 
 // newMockedAzurePrivateDNSProvider creates an AzureProvider comprising the mocked clients for zones and recordsets
-func newMockedAzurePrivateDNSProvider(domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, resourceGroup string, zones []*privatedns.PrivateZone, recordSets []*privatedns.RecordSet) (*AzurePrivateDNSProvider, error) {
+func newMockedAzurePrivateDNSProvider(domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, resourceGroup string, zones []*privatedns.PrivateZone, recordSets []*privatedns.RecordSet, maxRetriesCount int) (*AzurePrivateDNSProvider, error) {
 	zonesClient := newMockPrivateZonesClient(zones)
 	recordSetsClient := newMockPrivateRecordSectsClient(recordSets)
-	return newAzurePrivateDNSProvider(domainFilter, zoneNameFilter, zoneIDFilter, dryRun, resourceGroup, &zonesClient, &recordSetsClient), nil
+	return newAzurePrivateDNSProvider(domainFilter, zoneNameFilter, zoneIDFilter, dryRun, resourceGroup, &zonesClient, &recordSetsClient, maxRetriesCount), nil
 }
 
-func newAzurePrivateDNSProvider(domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, resourceGroup string, privateZonesClient PrivateZonesClient, privateRecordsClient PrivateRecordSetsClient) *AzurePrivateDNSProvider {
+func newAzurePrivateDNSProvider(domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, resourceGroup string, privateZonesClient PrivateZonesClient, privateRecordsClient PrivateRecordSetsClient, maxRetriesCount int) *AzurePrivateDNSProvider {
 	return &AzurePrivateDNSProvider{
 		domainFilter:     domainFilter,
 		zoneNameFilter:   zoneNameFilter,
@@ -240,6 +240,7 @@ func newAzurePrivateDNSProvider(domainFilter endpoint.DomainFilter, zoneNameFilt
 		zonesClient:      privateZonesClient,
 		zonesCache:       &zonesCache[privatedns.PrivateZone]{duration: 0},
 		recordSetsClient: privateRecordsClient,
+		maxRetriesCount:  maxRetriesCount,
 	}
 }
 
@@ -259,7 +260,7 @@ func TestAzurePrivateDNSRecord(t *testing.T) {
 			createPrivateMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL),
 			createPrivateMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10),
 			createPrivateMockRecordSetWithTTL("mail", endpoint.RecordTypeMX, "10 example.com", 4000),
-		})
+		}, 3)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -298,7 +299,7 @@ func TestAzurePrivateDNSMultiRecord(t *testing.T) {
 			createPrivateMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL),
 			createPrivateMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10),
 			createPrivateMockRecordSetMultiWithTTL("mail", endpoint.RecordTypeMX, 4000, "10 example.com", "20 backup.example.com"),
-		})
+		}, 3)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -378,6 +379,7 @@ func testAzurePrivateDNSApplyChangesInternal(t *testing.T, dryRun bool, client P
 		"group",
 		&zonesClient,
 		client,
+		3,
 	)
 
 	createRecords := []*endpoint.Endpoint{
@@ -450,7 +452,7 @@ func TestAzurePrivateDNSNameFilter(t *testing.T) {
 			createPrivateMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL),
 			createPrivateMockRecordSetWithTTL("mail.nginx", endpoint.RecordTypeMX, "20 example.com", recordTTL),
 			createPrivateMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10),
-		})
+		}, 3)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -505,6 +507,7 @@ func testAzurePrivateDNSApplyChangesInternalZoneName(t *testing.T, dryRun bool,
 		"group",
 		&zonesClient,
 		client,
+		3,
 	)
 
 	createRecords := []*endpoint.Endpoint{
diff --git a/provider/azure/azure_test.go b/provider/azure/azure_test.go
index a27f2eb1..e7807d9c 100644
--- a/provider/azure/azure_test.go
+++ b/provider/azure/azure_test.go
@@ -237,13 +237,13 @@ func createMockRecordSetMultiWithTTL(name, recordType string, ttl int64, values
 }
 
 // newMockedAzureProvider creates an AzureProvider comprising the mocked clients for zones and recordsets
-func newMockedAzureProvider(domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, resourceGroup string, userAssignedIdentityClientID string, activeDirectoryAuthorityHost string, zones []*dns.Zone, recordSets []*dns.RecordSet) (*AzureProvider, error) {
+func newMockedAzureProvider(domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, resourceGroup string, userAssignedIdentityClientID string, activeDirectoryAuthorityHost string, zones []*dns.Zone, recordSets []*dns.RecordSet, maxRetriesCount int) (*AzureProvider, error) {
 	zonesClient := newMockZonesClient(zones)
 	recordSetsClient := newMockRecordSetsClient(recordSets)
-	return newAzureProvider(domainFilter, zoneNameFilter, zoneIDFilter, dryRun, resourceGroup, userAssignedIdentityClientID, activeDirectoryAuthorityHost, &zonesClient, &recordSetsClient), nil
+	return newAzureProvider(domainFilter, zoneNameFilter, zoneIDFilter, dryRun, resourceGroup, userAssignedIdentityClientID, activeDirectoryAuthorityHost, &zonesClient, &recordSetsClient, maxRetriesCount), nil
 }
 
-func newAzureProvider(domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, resourceGroup string, userAssignedIdentityClientID string, activeDirectoryAuthorityHost string, zonesClient ZonesClient, recordsClient RecordSetsClient) *AzureProvider {
+func newAzureProvider(domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, resourceGroup string, userAssignedIdentityClientID string, activeDirectoryAuthorityHost string, zonesClient ZonesClient, recordsClient RecordSetsClient, maxRetriesCount int) *AzureProvider {
 	return &AzureProvider{
 		domainFilter:                 domainFilter,
 		zoneNameFilter:               zoneNameFilter,
@@ -255,6 +255,7 @@ func newAzureProvider(domainFilter endpoint.DomainFilter, zoneNameFilter endpoin
 		zonesClient:                  zonesClient,
 		zonesCache:                   &zonesCache[dns.Zone]{duration: 0},
 		recordSetsClient:             recordsClient,
+		maxRetriesCount:              maxRetriesCount,
 	}
 }
 
@@ -280,7 +281,7 @@ func TestAzureRecord(t *testing.T) {
 			createMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL),
 			createMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10),
 			createMockRecordSetMultiWithTTL("mail", endpoint.RecordTypeMX, 4000, "10 example.com"),
-		})
+		}, 3)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -325,7 +326,7 @@ func TestAzureMultiRecord(t *testing.T) {
 			createMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL),
 			createMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10),
 			createMockRecordSetMultiWithTTL("mail", endpoint.RecordTypeMX, 4000, "10 example.com", "20 backup.example.com"),
-		})
+		}, 3)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -415,6 +416,7 @@ func testAzureApplyChangesInternal(t *testing.T, dryRun bool, client RecordSetsC
 		"",
 		&zonesClient,
 		client,
+		3,
 	)
 
 	createRecords := []*endpoint.Endpoint{
@@ -497,7 +499,7 @@ func TestAzureNameFilter(t *testing.T) {
 			createMockRecordSetWithTTL("mail.nginx", endpoint.RecordTypeMX, "20 example.com", recordTTL),
 			createMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10),
 			createMockRecordSetWithTTL("hack", endpoint.RecordTypeNS, "ns1.example.com.", 3600),
-		})
+		}, 3)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -555,6 +557,7 @@ func testAzureApplyChangesInternalZoneName(t *testing.T, dryRun bool, client Rec
 		"",
 		&zonesClient,
 		client,
+		3,
 	)
 
 	createRecords := []*endpoint.Endpoint{
diff --git a/provider/azure/config_test.go b/provider/azure/config_test.go
index 956599a9..8e7f9c6c 100644
--- a/provider/azure/config_test.go
+++ b/provider/azure/config_test.go
@@ -17,11 +17,19 @@ limitations under the License.
 package azure
 
 import (
+	"context"
+	"fmt"
+	"io"
+	"net/http"
 	"path"
 	"runtime"
+	"strconv"
+	"strings"
 	"testing"
 
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
+	azruntime "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -59,3 +67,268 @@ func TestOverrideConfiguration(t *testing.T) {
 	assert.Equal(t, cfg.ResourceGroup, "rg-override")
 	assert.Equal(t, cfg.ActiveDirectoryAuthorityHost, "aad-endpoint-override")
 }
+
+// Test for custom header policy
+type transportFunc func(*http.Request) (*http.Response, error)
+
+func (f transportFunc) Do(req *http.Request) (*http.Response, error) {
+	return f(req)
+}
+
+func TestCustomHeaderPolicyWithRetries(t *testing.T) {
+	// Set up test environment
+	defaultRetries := 3
+	flagValue := "-6"
+	isSet := true
+
+	retries, err := parseMaxRetries(flagValue, defaultRetries)
+	if err != nil {
+		t.Fatalf("Failed to parse retries: %v", err)
+	}
+	maxRetries := int32(retries)
+	if !isSet || (isSet && flagValue == "0") {
+		// Use default if flag not provided OR if flag is "0"
+		maxRetries = int32(defaultRetries)
+		t.Logf("Using default value: %d (flag provided: %v, value: %q)",
+			defaultRetries, isSet, flagValue)
+	} else {
+		// Flag was provided with non-zero value
+		retries, err := parseMaxRetries(flagValue, defaultRetries)
+		if err != nil {
+			t.Fatalf("Failed to parse retries: %v", err)
+		}
+		maxRetries = int32(retries)
+		t.Logf("Using provided flag value: %d", retries)
+	}
+
+	var attempt int32
+	var firstRequestID string
+
+	// Create mock transport that simulates 429 responses
+	mockTransport := transportFunc(func(req *http.Request) (*http.Response, error) {
+		attempt++
+
+		// Get the request ID from header
+		requestID := req.Header.Get("x-ms-client-request-id")
+		if requestID == "" {
+			t.Fatalf("Request ID missing on attempt %d", attempt)
+		}
+
+		// On first attempt, store the request ID
+		if attempt == 1 {
+			firstRequestID = requestID
+			t.Logf("Initial request ID: %s", firstRequestID)
+		} else {
+			// On subsequent attempts, verify it matches the first request ID
+			if requestID != firstRequestID {
+				t.Fatalf("Request ID changed on retry %d: got %s, want %s",
+					attempt, requestID, firstRequestID)
+			} else {
+				t.Logf("Request ID preserved on attempt %d: %s", attempt, requestID)
+			}
+		}
+
+		// Verify the ID is also in the context
+		if ctxID, ok := req.Context().Value(clientRequestIDKey).(string); !ok || ctxID != requestID {
+			t.Errorf("Context ID mismatch on attempt %d: got %v, want %s",
+				attempt, ctxID, requestID)
+		}
+
+		// Return 429 for all but the last attempt
+		if maxRetries < 0 || attempt <= maxRetries {
+			t.Logf("Attempt %d: THROTTLED (429) - Request ID: %s", attempt, requestID)
+			return &http.Response{
+				StatusCode: http.StatusTooManyRequests,
+				Body:       io.NopCloser(strings.NewReader("Too many requests")),
+				Request:    req,
+				Header: http.Header{
+					"x-ms-client-request-id": []string{requestID},
+					"Retry-After":            []string{"1"},
+				},
+			}, nil
+		}
+
+		// Return 200 on final attempt
+		t.Logf("Attempt %d: SUCCESS (200) - Request ID: %s", attempt, requestID)
+		return &http.Response{
+			StatusCode: http.StatusOK,
+			Body:       io.NopCloser(strings.NewReader("Success")),
+			Request:    req,
+			Header: http.Header{
+				"x-ms-client-request-id": []string{requestID},
+			},
+		}, nil
+	})
+
+	// Create pipeline with retry policy and custom header policy
+	mockPipeline := azruntime.NewPipeline(
+		"testmodule",
+		"1.0",
+		azruntime.PipelineOptions{
+			PerCall: []policy.Policy{
+				CustomHeaderPolicynew(),
+			},
+		},
+		&policy.ClientOptions{
+			Retry: policy.RetryOptions{
+				MaxRetries: maxRetries,
+			},
+			Transport: mockTransport,
+		},
+	)
+	// Create request and execute
+	req, err := azruntime.NewRequest(context.Background(), http.MethodGet, "https://example.com")
+	if err != nil {
+		t.Fatalf("Failed to create request: %v", err)
+	}
+
+	resp, err := mockPipeline.Do(req)
+	if err != nil {
+		t.Fatalf("Request failed: %v", err)
+	}
+	defer resp.Body.Close()
+
+	// Verify we got the expected number of attempts
+	var expectedAttempts int32
+	if maxRetries < 0 {
+		expectedAttempts = 1 // For negative retries, only one attempt should be made
+	} else {
+		expectedAttempts = maxRetries + 1 // For zero or positive retries, attempts = retries + 1
+	}
+
+	if attempt != expectedAttempts {
+		t.Errorf("Wrong number of attempts: got %d, want %d", attempt, expectedAttempts)
+	}
+
+	t.Logf("Test completed with %d attempts, all with request ID: %s", attempt, firstRequestID)
+}
+
+func TestMaxRetriesCount(t *testing.T) {
+	defaultRetries := 3
+
+	tests := []struct {
+		name        string
+		input       string
+		isSet       bool // indicates if flag was provided
+		expected    int
+		shouldError bool
+		description string
+	}{
+		{
+			name:        "FlagNotProvided",
+			input:       "",
+			isSet:       false,
+			expected:    defaultRetries,
+			shouldError: false,
+			description: "When flag is not provided, should use default value",
+		},
+		{
+			name:        "FlagProvidedEmpty",
+			input:       "",
+			isSet:       true,
+			expected:    0,
+			shouldError: true,
+			description: "When flag is provided but empty, should error",
+		},
+		{
+			name:        "ValidPositive",
+			input:       "5",
+			isSet:       true,
+			expected:    5,
+			shouldError: false,
+			description: "Valid positive number should be accepted",
+		},
+		{
+			name:        "ZeroRetries",
+			input:       "0",
+			isSet:       true,
+			expected:    0,
+			shouldError: false,
+			description: "Zero should be accepted and handled by SDK",
+		},
+		{
+			name:        "NegativeRetries",
+			input:       "-2",
+			isSet:       true,
+			expected:    -2,
+			shouldError: false,
+			description: "Negative values should be accepted and  handled by SDK",
+		},
+		{
+			name:        "InvalidString",
+			input:       "abc",
+			isSet:       true,
+			expected:    0,
+			shouldError: true,
+			description: "Non-numeric string should error",
+		},
+		{
+			name:        "Whitespace",
+			input:       "   ",
+			isSet:       true,
+			expected:    0,
+			shouldError: true,
+			description: "Whitespace should error",
+		},
+		{
+			name:        "SpecialChars",
+			input:       "@#$%",
+			isSet:       true,
+			expected:    0,
+			shouldError: true,
+			description: "Special characters should error",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			t.Logf("=== Test Case: %s ===", tt.name)
+			t.Logf("Description: %s", tt.description)
+			t.Logf("Input: %q (flag provided: %v)", tt.input, tt.isSet)
+
+			// Handle flag not provided case
+			if !tt.isSet {
+				t.Logf("Using default value: %d", defaultRetries)
+				return
+			}
+
+			retries, err := parseMaxRetries(tt.input, defaultRetries)
+
+			// Check error condition
+			if tt.shouldError {
+				if err == nil {
+					t.Errorf("Expected error for input %q but got none", tt.input)
+				} else {
+					t.Logf("Got expected error: %v", err)
+				}
+			} else {
+				if err != nil {
+					t.Errorf("Unexpected error: %v", err)
+				}
+				if retries != tt.expected {
+					t.Errorf("Got %d retries, want %d", retries, tt.expected)
+				} else {
+					t.Logf("Got expected value: %d", retries)
+				}
+			}
+		})
+	}
+}
+
+// Helper function to parse max retries value
+func parseMaxRetries(value string, defaultValue int) (int, error) {
+	// Trim whitespace
+	value = strings.TrimSpace(value)
+
+	// Empty string or whitespace should error
+	if value == "" {
+		return 0, fmt.Errorf("retry count must be provided when flag is set")
+	}
+
+	retries, err := strconv.Atoi(value)
+	if err != nil {
+		return 0, fmt.Errorf("invalid retry count %q: %v", value, err)
+	}
+
+	return retries, nil
+}
diff --git a/provider/cloudflare/cloudflare_test.go b/provider/cloudflare/cloudflare_test.go
index b5c05cea..6bd93411 100644
--- a/provider/cloudflare/cloudflare_test.go
+++ b/provider/cloudflare/cloudflare_test.go
@@ -35,6 +35,7 @@ import (
 	"sigs.k8s.io/external-dns/internal/testutils"
 	"sigs.k8s.io/external-dns/plan"
 	"sigs.k8s.io/external-dns/provider"
+	"sigs.k8s.io/external-dns/source/annotations"
 )
 
 type MockAction struct {
@@ -65,6 +66,7 @@ var ExampleDomain = []cloudflare.DNSRecord{
 		TTL:     120,
 		Content: "1.2.3.4",
 		Proxied: proxyDisabled,
+		Comment: "valid comment",
 	},
 	{
 		ID:      "2345678901",
@@ -420,6 +422,7 @@ func (m *mockCloudFlareClient) ZoneDetails(ctx context.Context, zoneID string) (
 			return cloudflare.Zone{
 				ID:   zoneID,
 				Name: zoneName,
+				Plan: cloudflare.ZonePlan{IsSubscribed: strings.HasSuffix(zoneName, "bar.com")},
 			}, nil
 		}
 	}
@@ -979,8 +982,8 @@ func TestCloudflareRecords(t *testing.T) {
 
 	// Set DNSRecordsPerPage to 1 test the pagination behaviour
 	p := &CloudFlareProvider{
-		Client:            client,
-		DNSRecordsPerPage: 1,
+		Client:           client,
+		DNSRecordsConfig: DNSRecordsConfig{PerPage: 1},
 	}
 	ctx := context.Background()
 
@@ -1096,9 +1099,10 @@ func TestCloudflareProvider(t *testing.T) {
 				provider.NewZoneIDFilter([]string{""}),
 				false,
 				true,
-				5000,
 				"",
-				CustomHostnamesConfig{Enabled: false})
+				CustomHostnamesConfig{Enabled: false},
+				DNSRecordsConfig{PerPage: 5000, Comment: ""},
+			)
 			if err != nil && !tc.ShouldFail {
 				t.Errorf("should not fail, %s", err)
 			}
@@ -1764,9 +1768,10 @@ func TestCloudFlareProvider_Region(t *testing.T) {
 		provider.ZoneIDFilter{},
 		true,
 		false,
-		50,
 		"us",
-		CustomHostnamesConfig{Enabled: false})
+		CustomHostnamesConfig{Enabled: false},
+		DNSRecordsConfig{PerPage: 50, Comment: ""},
+	)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -1779,28 +1784,144 @@ func TestCloudFlareProvider_Region(t *testing.T) {
 func TestCloudFlareProvider_newCloudFlareChange(t *testing.T) {
 	_ = os.Setenv("CF_API_KEY", "xxxxxxxxxxxxxxxxx")
 	_ = os.Setenv("CF_API_EMAIL", "test@test.com")
-	provider, err := NewCloudFlareProvider(
+
+	p, err := NewCloudFlareProvider(
 		endpoint.NewDomainFilter([]string{"example.com"}),
 		provider.ZoneIDFilter{},
 		true,
 		false,
-		50,
 		"us",
-		CustomHostnamesConfig{Enabled: false})
+		CustomHostnamesConfig{Enabled: false},
+		DNSRecordsConfig{PerPage: 50},
+	)
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	endpoint := &endpoint.Endpoint{
+	ep := &endpoint.Endpoint{
 		DNSName:    "example.com",
 		RecordType: "A",
 		Targets:    []string{"192.0.2.1"},
 	}
 
-	change := provider.newCloudFlareChange(cloudFlareCreate, endpoint, endpoint.Targets[0], nil)
+	change := p.newCloudFlareChange(cloudFlareCreate, ep, ep.Targets[0], nil)
 	if change.RegionalHostname.RegionKey != "us" {
 		t.Errorf("expected region key to be 'us', but got '%s'", change.RegionalHostname.RegionKey)
 	}
+
+	var freeValidCommentBuilder strings.Builder
+	for range freeZoneMaxCommentLength {
+		freeValidCommentBuilder.WriteString("x")
+	}
+
+	var freeInvalidCommentBuilder strings.Builder
+	for range freeZoneMaxCommentLength + 1 {
+		freeInvalidCommentBuilder.WriteString("x")
+	}
+
+	var paidValidCommentBuilder strings.Builder
+	for range paidZoneMaxCommentLength {
+		paidValidCommentBuilder.WriteString("x")
+	}
+	var paidInvalidCommentBuilder strings.Builder
+	for range paidZoneMaxCommentLength + 1 {
+		paidInvalidCommentBuilder.WriteString("x")
+	}
+
+	paidProvider, err := NewCloudFlareProvider(
+		endpoint.NewDomainFilter([]string{"bar.com"}),
+		provider.ZoneIDFilter{},
+		true,
+		false,
+		"us",
+		CustomHostnamesConfig{Enabled: false},
+		DNSRecordsConfig{PerPage: 50, Comment: paidValidCommentBuilder.String()},
+	)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	paidProvider.Client = NewMockCloudFlareClient()
+	commentTestCases := []struct {
+		name     string
+		provider *CloudFlareProvider
+		endpoint *endpoint.Endpoint
+		expected int
+	}{
+		{
+			name:     "For free Zone respecting comment length, expect no trimming",
+			provider: p,
+			endpoint: &endpoint.Endpoint{
+				DNSName:    "example.com",
+				RecordType: "A",
+				Targets:    []string{"192.0.2.1"},
+				ProviderSpecific: endpoint.ProviderSpecific{
+					{
+						Name:  annotations.CloudflareRecordCommentKey,
+						Value: freeValidCommentBuilder.String(),
+					},
+				},
+			},
+			expected: len(freeValidCommentBuilder.String()),
+		},
+		{
+			name:     "For free Zones not respecting comment length, expect trimmed comments",
+			provider: p,
+			endpoint: &endpoint.Endpoint{
+				DNSName:    "example.com",
+				RecordType: "A",
+				Targets:    []string{"192.0.2.1"},
+				ProviderSpecific: endpoint.ProviderSpecific{
+					{
+						Name:  annotations.CloudflareRecordCommentKey,
+						Value: freeInvalidCommentBuilder.String(),
+					},
+				},
+			},
+			expected: freeZoneMaxCommentLength,
+		},
+		{
+			name:     "For paid Zones respecting comment length, expect no trimming",
+			provider: paidProvider,
+			endpoint: &endpoint.Endpoint{
+				DNSName:    "bar.com",
+				RecordType: "A",
+				Targets:    []string{"192.0.2.1"},
+				ProviderSpecific: endpoint.ProviderSpecific{
+					{
+						Name:  annotations.CloudflareRecordCommentKey,
+						Value: paidValidCommentBuilder.String(),
+					},
+				},
+			},
+			expected: len(paidValidCommentBuilder.String()),
+		},
+		{
+			name:     "For paid Zones not respecting comment length, expect trimmed comments",
+			provider: paidProvider,
+			endpoint: &endpoint.Endpoint{
+				DNSName:    "bar.com",
+				RecordType: "A",
+				Targets:    []string{"192.0.2.1"},
+				ProviderSpecific: endpoint.ProviderSpecific{
+					{
+						Name:  annotations.CloudflareRecordCommentKey,
+						Value: paidInvalidCommentBuilder.String(),
+					},
+				},
+			},
+			expected: paidZoneMaxCommentLength,
+		},
+	}
+
+	for _, test := range commentTestCases {
+		t.Run(test.name, func(t *testing.T) {
+			change := test.provider.newCloudFlareChange(cloudFlareCreate, test.endpoint, test.endpoint.Targets[0], nil)
+			if len(change.ResourceRecord.Comment) != test.expected {
+				t.Errorf("expected comment to be %d characters long, but got %d", test.expected, len(change.ResourceRecord.Comment))
+			}
+		})
+	}
 }
 
 func TestCloudFlareProvider_submitChangesCNAME(t *testing.T) {
@@ -3195,3 +3316,24 @@ func Test_dataLocalizationRegionalHostnamesChanges(t *testing.T) {
 		})
 	}
 }
+
+func TestZoneHasPaidPlan(t *testing.T) {
+	client := NewMockCloudFlareClient()
+	cfprovider := &CloudFlareProvider{
+		Client:       client,
+		domainFilter: endpoint.NewDomainFilter([]string{"foo.com", "bar.com"}),
+		zoneIDFilter: provider.NewZoneIDFilter([]string{""}),
+	}
+
+	assert.Equal(t, false, cfprovider.ZoneHasPaidPlan("subdomain.foo.com"))
+	assert.Equal(t, true, cfprovider.ZoneHasPaidPlan("subdomain.bar.com"))
+	assert.Equal(t, false, cfprovider.ZoneHasPaidPlan("invaliddomain"))
+
+	client.zoneDetailsError = errors.New("zone lookup failed")
+	cfproviderWithZoneError := &CloudFlareProvider{
+		Client:       client,
+		domainFilter: endpoint.NewDomainFilter([]string{"foo.com", "bar.com"}),
+		zoneIDFilter: provider.NewZoneIDFilter([]string{""}),
+	}
+	assert.Equal(t, false, cfproviderWithZoneError.ZoneHasPaidPlan("subdomain.foo.com"))
+}
diff --git a/provider/coredns/coredns_test.go b/provider/coredns/coredns_test.go
index baf5652f..92b781e8 100644
--- a/provider/coredns/coredns_test.go
+++ b/provider/coredns/coredns_test.go
@@ -18,13 +18,14 @@ package coredns
 
 import (
 	"context"
-	"os"
 	"reflect"
 	"strings"
 	"testing"
 
+	"github.com/stretchr/testify/assert"
 	etcdcv3 "go.etcd.io/etcd/client/v3"
 	"sigs.k8s.io/external-dns/endpoint"
+	"sigs.k8s.io/external-dns/internal/testutils"
 	"sigs.k8s.io/external-dns/plan"
 
 	"github.com/stretchr/testify/require"
@@ -87,37 +88,45 @@ func TestETCDConfig(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			closer := envSetter(tt.input)
+			testutils.TestHelperEnvSetter(t, tt.input)
 			cfg, _ := getETCDConfig()
 			if !reflect.DeepEqual(cfg, tt.want) {
 				t.Errorf("unexpected config. Got %v, want %v", cfg, tt.want)
 			}
-			t.Cleanup(closer)
 		})
 	}
-
 }
 
-func envSetter(envs map[string]string) (closer func()) {
-	originalEnvs := map[string]string{}
+func TestEtcdHttpsProtocol(t *testing.T) {
+	envs := map[string]string{
+		"ETCD_URLS": "https://example.com:2379",
+	}
+	testutils.TestHelperEnvSetter(t, envs)
 
-	for name, value := range envs {
-		if originalValue, ok := os.LookupEnv(name); ok {
-			originalEnvs[name] = originalValue
-		}
-		_ = os.Setenv(name, value)
+	cfg, err := getETCDConfig()
+	assert.NoError(t, err)
+	assert.NotNil(t, cfg)
+}
+
+func TestEtcdHttpsIncorrectConfigError(t *testing.T) {
+	envs := map[string]string{
+		"ETCD_URLS":     "https://example.com:2379",
+		"ETCD_KEY_FILE": "incorrect-path-to-etcd-tls-key",
 	}
+	testutils.TestHelperEnvSetter(t, envs)
 
-	return func() {
-		for name := range envs {
-			origValue, has := originalEnvs[name]
-			if has {
-				_ = os.Setenv(name, origValue)
-			} else {
-				_ = os.Unsetenv(name)
-			}
-		}
+	_, err := getETCDConfig()
+	assert.Errorf(t, err, "Error creating TLS config: either both cert and key or none must be provided")
+}
+
+func TestEtcdUnsupportedProtocolError(t *testing.T) {
+	envs := map[string]string{
+		"ETCD_URLS": "jdbc:ftp:RemoteHost=MyFTPServer",
 	}
+	testutils.TestHelperEnvSetter(t, envs)
+
+	_, err := getETCDConfig()
+	assert.Errorf(t, err, "etcd URLs must start with either http:// or https://")
 }
 
 func TestAServiceTranslation(t *testing.T) {
diff --git a/provider/digitalocean/digital_ocean_test.go b/provider/digitalocean/digital_ocean_test.go
index 7088b092..4fd6ec05 100644
--- a/provider/digitalocean/digital_ocean_test.go
+++ b/provider/digitalocean/digital_ocean_test.go
@@ -73,11 +73,11 @@ func (m *mockDigitalOceanClient) CreateRecord(context.Context, string, *godo.Dom
 }
 
 func (m *mockDigitalOceanClient) Delete(context.Context, string) (*godo.Response, error) {
-	return nil, nil
+	return nil, fmt.Errorf("failed to delete domain")
 }
 
 func (m *mockDigitalOceanClient) DeleteRecord(ctx context.Context, domain string, id int) (*godo.Response, error) {
-	return nil, nil
+	return nil, fmt.Errorf("failed to delete record")
 }
 
 func (m *mockDigitalOceanClient) EditRecord(ctx context.Context, domain string, id int, editRequest *godo.DomainRecordEditRequest) (*godo.DomainRecord, *godo.Response, error) {
@@ -160,11 +160,11 @@ func (m *mockDigitalOceanRecordsFail) CreateRecord(context.Context, string, *god
 }
 
 func (m *mockDigitalOceanRecordsFail) Delete(context.Context, string) (*godo.Response, error) {
-	return nil, nil
+	return nil, fmt.Errorf("failed to delete record")
 }
 
 func (m *mockDigitalOceanRecordsFail) DeleteRecord(ctx context.Context, domain string, id int) (*godo.Response, error) {
-	return nil, nil
+	return nil, fmt.Errorf("failed to delete record")
 }
 
 func (m *mockDigitalOceanRecordsFail) EditRecord(ctx context.Context, domain string, id int, editRequest *godo.DomainRecordEditRequest) (*godo.DomainRecord, *godo.Response, error) {
diff --git a/provider/ns1/ns1_test.go b/provider/ns1/ns1_test.go
index 6d529879..21e21fcb 100644
--- a/provider/ns1/ns1_test.go
+++ b/provider/ns1/ns1_test.go
@@ -39,15 +39,15 @@ type MockNS1DomainClient struct {
 }
 
 func (m *MockNS1DomainClient) CreateRecord(r *dns.Record) (*http.Response, error) {
-	return nil, nil
+	return &http.Response{}, nil
 }
 
 func (m *MockNS1DomainClient) DeleteRecord(zone string, domain string, t string) (*http.Response, error) {
-	return nil, nil
+	return &http.Response{}, nil
 }
 
 func (m *MockNS1DomainClient) UpdateRecord(r *dns.Record) (*http.Response, error) {
-	return nil, nil
+	return &http.Response{}, nil
 }
 
 func (m *MockNS1DomainClient) GetZone(zone string) (*dns.Zone, *http.Response, error) {
@@ -81,16 +81,16 @@ func (m *MockNS1DomainClient) ListZones() ([]*dns.Zone, *http.Response, error) {
 
 type MockNS1GetZoneFail struct{}
 
-func (m *MockNS1GetZoneFail) CreateRecord(r *dns.Record) (*http.Response, error) {
-	return nil, nil
+func (m *MockNS1GetZoneFail) CreateRecord(_ *dns.Record) (*http.Response, error) {
+	return &http.Response{}, nil
 }
 
-func (m *MockNS1GetZoneFail) DeleteRecord(zone string, domain string, t string) (*http.Response, error) {
-	return nil, nil
+func (m *MockNS1GetZoneFail) DeleteRecord(_ string, _ string, _ string) (*http.Response, error) {
+	return &http.Response{}, nil
 }
 
 func (m *MockNS1GetZoneFail) UpdateRecord(r *dns.Record) (*http.Response, error) {
-	return nil, nil
+	return &http.Response{}, nil
 }
 
 func (m *MockNS1GetZoneFail) GetZone(zone string) (*dns.Zone, *http.Response, error) {
@@ -107,20 +107,20 @@ func (m *MockNS1GetZoneFail) ListZones() ([]*dns.Zone, *http.Response, error) {
 
 type MockNS1ListZonesFail struct{}
 
-func (m *MockNS1ListZonesFail) CreateRecord(r *dns.Record) (*http.Response, error) {
-	return nil, nil
+func (m *MockNS1ListZonesFail) CreateRecord(_ *dns.Record) (*http.Response, error) {
+	return &http.Response{}, nil
 }
 
-func (m *MockNS1ListZonesFail) DeleteRecord(zone string, domain string, t string) (*http.Response, error) {
-	return nil, nil
+func (m *MockNS1ListZonesFail) DeleteRecord(_ string, _ string, _ string) (*http.Response, error) {
+	return &http.Response{}, nil
 }
 
 func (m *MockNS1ListZonesFail) UpdateRecord(r *dns.Record) (*http.Response, error) {
-	return nil, nil
+	return &http.Response{}, nil
 }
 
 func (m *MockNS1ListZonesFail) GetZone(zone string) (*dns.Zone, *http.Response, error) {
-	return &dns.Zone{}, nil, nil
+	return &dns.Zone{}, &http.Response{}, nil
 }
 
 func (m *MockNS1ListZonesFail) ListZones() ([]*dns.Zone, *http.Response, error) {
diff --git a/provider/pdns/pdns_test.go b/provider/pdns/pdns_test.go
index 773d4897..c8a22165 100644
--- a/provider/pdns/pdns_test.go
+++ b/provider/pdns/pdns_test.go
@@ -690,7 +690,7 @@ func (c *PDNSAPIClientStub) ListZone(zoneID string) (pgo.Zone, *http.Response, e
 }
 
 func (c *PDNSAPIClientStub) PatchZone(zoneID string, zoneStruct pgo.Zone) (*http.Response, error) {
-	return nil, nil
+	return &http.Response{}, nil
 }
 
 /******************************************************************************/
@@ -721,7 +721,7 @@ func (c *PDNSAPIClientStubEmptyZones) ListZone(zoneID string) (pgo.Zone, *http.R
 
 func (c *PDNSAPIClientStubEmptyZones) PatchZone(zoneID string, zoneStruct pgo.Zone) (*http.Response, error) {
 	c.patchedZones = append(c.patchedZones, zoneStruct)
-	return nil, nil
+	return &http.Response{}, nil
 }
 
 /******************************************************************************/
diff --git a/provider/pihole/clientV6_test.go b/provider/pihole/clientV6_test.go
index 69757179..ba140382 100644
--- a/provider/pihole/clientV6_test.go
+++ b/provider/pihole/clientV6_test.go
@@ -61,7 +61,7 @@ func TestIsValidIPv6(t *testing.T) {
 	}{
 		{"2001:0db8:85a3:0000:0000:8a2e:0370:7334", true},
 		{"2001:db8:85a3::8a2e:370:7334", true},
-		//IPV6 dual, the format is y:y:y:y:y:y:x.x.x.x.
+		//IPv6 dual, the format is y:y:y:y:y:y:x.x.x.x.
 		{"::ffff:192.168.20.3", true},
 		{"::1", true},
 		{"::", true},
diff --git a/provider/scaleway/scaleway_test.go b/provider/scaleway/scaleway_test.go
index 560687c7..45f09b32 100644
--- a/provider/scaleway/scaleway_test.go
+++ b/provider/scaleway/scaleway_test.go
@@ -106,8 +106,8 @@ func (m *mockScalewayDomain) ListDNSZoneRecords(req *domain.ListDNSZoneRecordsRe
 	}, nil
 }
 
-func (m *mockScalewayDomain) UpdateDNSZoneRecords(req *domain.UpdateDNSZoneRecordsRequest, opts ...scw.RequestOption) (*domain.UpdateDNSZoneRecordsResponse, error) {
-	return nil, nil
+func (m *mockScalewayDomain) UpdateDNSZoneRecords(_ *domain.UpdateDNSZoneRecordsRequest, _ ...scw.RequestOption) (*domain.UpdateDNSZoneRecordsResponse, error) {
+	return &domain.UpdateDNSZoneRecordsResponse{}, nil
 }
 
 func TestScalewayProvider_NewScalewayProvider(t *testing.T) {
diff --git a/provider/ultradns/ultradns_test.go b/provider/ultradns/ultradns_test.go
index 2d027ccc..380650e3 100644
--- a/provider/ultradns/ultradns_test.go
+++ b/provider/ultradns/ultradns_test.go
@@ -65,11 +65,11 @@ type mockUltraDNSRecord struct {
 	client *udnssdk.Client
 }
 
-func (m *mockUltraDNSRecord) Create(k udnssdk.RRSetKey, rrset udnssdk.RRSet) (*http.Response, error) {
-	return nil, nil
+func (m *mockUltraDNSRecord) Create(_ udnssdk.RRSetKey, _ udnssdk.RRSet) (*http.Response, error) {
+	return &http.Response{}, nil
 }
 
-func (m *mockUltraDNSRecord) Select(k udnssdk.RRSetKey) ([]udnssdk.RRSet, error) {
+func (m *mockUltraDNSRecord) Select(_ udnssdk.RRSetKey) ([]udnssdk.RRSet, error) {
 	return []udnssdk.RRSet{{
 		OwnerName: "test-ultradns-provider.com.",
 		RRType:    endpoint.RecordTypeA,
@@ -83,11 +83,11 @@ func (m *mockUltraDNSRecord) SelectWithOffset(k udnssdk.RRSetKey, offset int) ([
 }
 
 func (m *mockUltraDNSRecord) Update(udnssdk.RRSetKey, udnssdk.RRSet) (*http.Response, error) {
-	return nil, nil
+	return &http.Response{}, nil
 }
 
 func (m *mockUltraDNSRecord) Delete(k udnssdk.RRSetKey) (*http.Response, error) {
-	return nil, nil
+	return &http.Response{}, nil
 }
 
 func (m *mockUltraDNSRecord) SelectWithOffsetWithLimit(k udnssdk.RRSetKey, offset int, limit int) (rrsets []udnssdk.RRSet, ResultInfo udnssdk.ResultInfo, resp *http.Response, err error) {
diff --git a/source/annotations/provider_specific_test.go b/source/annotations/provider_specific_test.go
index 499935e3..e9fd7633 100644
--- a/source/annotations/provider_specific_test.go
+++ b/source/annotations/provider_specific_test.go
@@ -147,6 +147,14 @@ func TestGetProviderSpecificCloudflareAnnotations(t *testing.T) {
 			expectedKey:   CloudflareRegionKey,
 			expectedValue: "us",
 		},
+		{
+			title: "Cloudflare DNS record comment annotation is set correctly",
+			annotations: map[string]string{
+				CloudflareRecordCommentKey: "comment",
+			},
+			expectedKey:   CloudflareRecordCommentKey,
+			expectedValue: "comment",
+		},
 	} {
 		t.Run(tc.title, func(t *testing.T) {
 			providerSpecificAnnotations, _ := ProviderSpecificAnnotations(tc.annotations)
diff --git a/source/service_test.go b/source/service_test.go
index 5e9948ab..1eaebfe5 100644
--- a/source/service_test.go
+++ b/source/service_test.go
@@ -82,6 +82,7 @@ func (suite *ServiceSuite) SetupTest() {
 		labels.Everything(),
 		false,
 		false,
+		false,
 	)
 	suite.NoError(err, "should initialize service source")
 }
@@ -164,6 +165,7 @@ func testServiceSourceNewServiceSource(t *testing.T) {
 				labels.Everything(),
 				false,
 				false,
+				false,
 			)
 
 			if ti.expectError {
@@ -1136,6 +1138,7 @@ func testServiceSourceEndpoints(t *testing.T) {
 				sourceLabel,
 				tc.resolveLoadBalancerHostname,
 				false,
+				false,
 			)
 
 			require.NoError(t, err)
@@ -1351,6 +1354,7 @@ func testMultipleServicesEndpoints(t *testing.T) {
 				labels.Everything(),
 				false,
 				false,
+				false,
 			)
 			require.NoError(t, err)
 
@@ -1655,6 +1659,7 @@ func TestClusterIpServices(t *testing.T) {
 				labelSelector,
 				false,
 				false,
+				false,
 			)
 			require.NoError(t, err)
 
@@ -1686,6 +1691,7 @@ func TestServiceSourceNodePortServices(t *testing.T) {
 		compatibility            string
 		fqdnTemplate             string
 		ignoreHostnameAnnotation bool
+		exposeInternalIPv6       bool
 		labels                   map[string]string
 		annotations              map[string]string
 		lbs                      []string
@@ -2207,12 +2213,13 @@ func TestServiceSourceNodePortServices(t *testing.T) {
 			}},
 		},
 		{
-			title:            "node port services annotated with external DNS Controller annotations return an endpoint in compatibility mode",
-			svcNamespace:     "testing",
-			svcName:          "foo",
-			svcType:          v1.ServiceTypeNodePort,
-			svcTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeCluster,
-			compatibility:    "kops-dns-controller",
+			title:              "node port services annotated with external DNS Controller annotations return an endpoint in compatibility mode with exposeInternalIPv6 flag set",
+			svcNamespace:       "testing",
+			svcName:            "foo",
+			svcType:            v1.ServiceTypeNodePort,
+			svcTrafficPolicy:   v1.ServiceExternalTrafficPolicyTypeCluster,
+			compatibility:      "kops-dns-controller",
+			exposeInternalIPv6: true,
 			annotations: map[string]string{
 				kopsDNSControllerHostnameAnnotationKey: "foo.example.org., bar.example.org",
 			},
@@ -2374,6 +2381,7 @@ func TestServiceSourceNodePortServices(t *testing.T) {
 				labels.Everything(),
 				false,
 				false,
+				tc.exposeInternalIPv6,
 			)
 			require.NoError(t, err)
 
@@ -2403,6 +2411,7 @@ func TestHeadlessServices(t *testing.T) {
 		compatibility            string
 		fqdnTemplate             string
 		ignoreHostnameAnnotation bool
+		exposeInternalIPv6       bool
 		labels                   map[string]string
 		svcAnnotations           map[string]string
 		podAnnotations           map[string]string
@@ -2428,6 +2437,7 @@ func TestHeadlessServices(t *testing.T) {
 			"",
 			"",
 			false,
+			false,
 			map[string]string{"component": "foo"},
 			map[string]string{
 				hostnameAnnotationKey: "service.example.org",
@@ -2461,6 +2471,7 @@ func TestHeadlessServices(t *testing.T) {
 			"",
 			"",
 			false,
+			false,
 			map[string]string{"component": "foo"},
 			map[string]string{
 				hostnameAnnotationKey: "service.example.org",
@@ -2494,6 +2505,7 @@ func TestHeadlessServices(t *testing.T) {
 			"",
 			"",
 			true,
+			false,
 			map[string]string{"component": "foo"},
 			map[string]string{
 				hostnameAnnotationKey: "service.example.org",
@@ -2523,6 +2535,7 @@ func TestHeadlessServices(t *testing.T) {
 			"",
 			"",
 			false,
+			false,
 			map[string]string{"component": "foo"},
 			map[string]string{
 				hostnameAnnotationKey: "service.example.org",
@@ -2557,6 +2570,7 @@ func TestHeadlessServices(t *testing.T) {
 			"",
 			"",
 			false,
+			false,
 			map[string]string{"component": "foo"},
 			map[string]string{
 				hostnameAnnotationKey: "service.example.org",
@@ -2591,6 +2605,7 @@ func TestHeadlessServices(t *testing.T) {
 			"",
 			"",
 			false,
+			false,
 			map[string]string{"component": "foo"},
 			map[string]string{
 				hostnameAnnotationKey: "service.example.org",
@@ -2623,6 +2638,7 @@ func TestHeadlessServices(t *testing.T) {
 			"",
 			"",
 			false,
+			false,
 			map[string]string{"component": "foo"},
 			map[string]string{
 				hostnameAnnotationKey: "service.example.org",
@@ -2656,6 +2672,7 @@ func TestHeadlessServices(t *testing.T) {
 			"",
 			"",
 			false,
+			false,
 			map[string]string{"component": "foo"},
 			map[string]string{
 				hostnameAnnotationKey: "service.example.org",
@@ -2687,6 +2704,7 @@ func TestHeadlessServices(t *testing.T) {
 			"",
 			"",
 			false,
+			false,
 			map[string]string{"component": "foo"},
 			map[string]string{
 				hostnameAnnotationKey: "service.example.org",
@@ -2718,6 +2736,7 @@ func TestHeadlessServices(t *testing.T) {
 			"",
 			"",
 			false,
+			false,
 			map[string]string{"component": "foo"},
 			map[string]string{
 				hostnameAnnotationKey: "service.example.org",
@@ -2749,6 +2768,7 @@ func TestHeadlessServices(t *testing.T) {
 			"",
 			"",
 			false,
+			false,
 			map[string]string{"component": "foo"},
 			map[string]string{
 				hostnameAnnotationKey: "service.example.org",
@@ -2782,6 +2802,7 @@ func TestHeadlessServices(t *testing.T) {
 			"",
 			"",
 			false,
+			false,
 			map[string]string{"component": "foo"},
 			map[string]string{
 				hostnameAnnotationKey: "service.example.org",
@@ -2815,6 +2836,7 @@ func TestHeadlessServices(t *testing.T) {
 			"",
 			"",
 			false,
+			false,
 			map[string]string{"component": "foo"},
 			map[string]string{
 				hostnameAnnotationKey:      "service.example.org",
@@ -2850,7 +2872,7 @@ func TestHeadlessServices(t *testing.T) {
 			false,
 		},
 		{
-			"annotated Headless services return IPv6 targets from node external IP if endpoints-type annotation is set",
+			"annotated Headless services return only external IPv6 targets from node IP if endpoints-type annotation is set and exposeInternalIPv6 flag is unset",
 			"",
 			"testing",
 			"foo",
@@ -2858,6 +2880,55 @@ func TestHeadlessServices(t *testing.T) {
 			"",
 			"",
 			false,
+			false,
+			map[string]string{"component": "foo"},
+			map[string]string{
+				hostnameAnnotationKey:      "service.example.org",
+				endpointsTypeAnnotationKey: EndpointsTypeNodeExternalIP,
+			},
+			map[string]string{},
+			v1.ClusterIPNone,
+			[]string{"2001:db8::1"},
+			[]string{""},
+			map[string]string{
+				"component": "foo",
+			},
+			[]string{},
+			[]string{"foo"},
+			[]string{"", "", ""},
+			[]bool{true, true, true},
+			false,
+			[]v1.Node{
+				{
+					Status: v1.NodeStatus{
+						Addresses: []v1.NodeAddress{
+							{
+								Type:    v1.NodeInternalIP,
+								Address: "2001:db8::4",
+							},
+							{
+								Type:    v1.NodeExternalIP,
+								Address: "2001:db8::5",
+							},
+						},
+					},
+				},
+			},
+			[]*endpoint.Endpoint{
+				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::5"}},
+			},
+			false,
+		},
+		{
+			"annotated Headless services return IPv6 targets from node external IP if endpoints-type annotation is set and exposeInternalIPv6 flag set",
+			"",
+			"testing",
+			"foo",
+			v1.ServiceTypeClusterIP,
+			"",
+			"",
+			false,
+			true,
 			map[string]string{"component": "foo"},
 			map[string]string{
 				hostnameAnnotationKey:      "service.example.org",
@@ -2893,7 +2964,7 @@ func TestHeadlessServices(t *testing.T) {
 			false,
 		},
 		{
-			"annotated Headless services return dual-stack targets from node external IP if endpoints-type annotation is set",
+			"annotated Headless services return dual-stack targets from node external IP if endpoints-type annotation is set and exposeInternalIPv6 flag set",
 			"",
 			"testing",
 			"foo",
@@ -2901,6 +2972,7 @@ func TestHeadlessServices(t *testing.T) {
 			"",
 			"",
 			false,
+			true,
 			map[string]string{"component": "foo"},
 			map[string]string{
 				hostnameAnnotationKey:      "service.example.org",
@@ -2949,6 +3021,7 @@ func TestHeadlessServices(t *testing.T) {
 			"",
 			"",
 			false,
+			false,
 			map[string]string{"component": "foo"},
 			map[string]string{
 				hostnameAnnotationKey:      "service.example.org",
@@ -2981,6 +3054,7 @@ func TestHeadlessServices(t *testing.T) {
 			"",
 			"",
 			false,
+			false,
 			map[string]string{"component": "foo"},
 			map[string]string{
 				hostnameAnnotationKey:      "service.example.org",
@@ -3103,6 +3177,7 @@ func TestHeadlessServices(t *testing.T) {
 				labels.Everything(),
 				false,
 				false,
+				tc.exposeInternalIPv6,
 			)
 			require.NoError(t, err)
 
@@ -3563,6 +3638,7 @@ func TestHeadlessServicesHostIP(t *testing.T) {
 				labels.Everything(),
 				false,
 				false,
+				false,
 			)
 			require.NoError(t, err)
 
@@ -3742,6 +3818,7 @@ func TestExternalServices(t *testing.T) {
 				labels.Everything(),
 				false,
 				false,
+				false,
 			)
 			require.NoError(t, err)
 
@@ -3798,6 +3875,7 @@ func BenchmarkServiceEndpoints(b *testing.B) {
 		labels.Everything(),
 		false,
 		false,
+		false,
 	)
 	require.NoError(b, err)
 
diff --git a/source/traefik_proxy_test.go b/source/traefik_proxy_test.go
index 4db39525..4cd1cec1 100644
--- a/source/traefik_proxy_test.go
+++ b/source/traefik_proxy_test.go
@@ -19,6 +19,7 @@ package source
 import (
 	"context"
 	"encoding/json"
+	"fmt"
 	"testing"
 
 	"github.com/stretchr/testify/mock"
@@ -1806,7 +1807,7 @@ type testInformer struct {
 	times int
 }
 
-func (t *testInformer) AddEventHandler(handler cache.ResourceEventHandler) (cache.ResourceEventHandlerRegistration, error) {
+func (t *testInformer) AddEventHandler(_ cache.ResourceEventHandler) (cache.ResourceEventHandlerRegistration, error) {
 	t.times += 1
-	return nil, nil
+	return nil, fmt.Errorf("not implemented")
 }