From 84a997bdf9923708ac20021c5de9178decd2cf2f Mon Sep 17 00:00:00 2001 From: Tom M G Date: Fri, 23 May 2025 07:34:29 +0200 Subject: [PATCH 1/7] feat(cloudflare): Support DNS records tagging --- controller/execute.go | 1 + pkg/apis/externaldns/types.go | 5 +++-- provider/cloudflare/cloudflare.go | 30 ++++++++++++++++++++++++++++++ source/annotations/annotations.go | 1 + 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/controller/execute.go b/controller/execute.go index 6ae1cf5b3..0c51b0cb9 100644 --- a/controller/execute.go +++ b/controller/execute.go @@ -208,6 +208,7 @@ func Execute() { cloudflare.DNSRecordsConfig{ PerPage: cfg.CloudflareDNSRecordsPerPage, Comment: cfg.CloudflareDNSRecordsComment, + Tags: cfg.CloudflareDNSRecordsTags, }) case "google": p, err = google.NewGoogleProvider(ctx, cfg.GoogleProject, domainFilter, zoneIDFilter, cfg.GoogleBatchChangeSize, cfg.GoogleBatchChangeInterval, cfg.GoogleZoneVisibility, cfg.DryRun) diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index df96f71bd..35d562225 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -111,10 +111,10 @@ type Config struct { CloudflareCustomHostnames bool CloudflareDNSRecordsPerPage int CloudflareDNSRecordsComment string + CloudflareDNSRecordsTags string CloudflareCustomHostnamesMinTLSVersion string CloudflareCustomHostnamesCertificateAuthority string CloudflareRegionKey string - CloudflareRecordComment string CoreDNSPrefix string AkamaiServiceConsumerDomain string AkamaiClientToken string @@ -541,7 +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("cloudflare-record-comment", "When using the Cloudflare provider, specify the comment for the DNS records (default: '')").Default("").StringVar(&cfg.CloudflareDNSRecordsComment) + app.Flag("cloudflare-record-tags", "When using the Cloudflare provider for a paid zone, specify the tags for the DNS records as a comma-separated string (default: '')").Default("").StringVar(&cfg.CloudflareDNSRecordsTags) 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) diff --git a/provider/cloudflare/cloudflare.go b/provider/cloudflare/cloudflare.go index 29dd80009..0bed6377c 100644 --- a/provider/cloudflare/cloudflare.go +++ b/provider/cloudflare/cloudflare.go @@ -198,6 +198,7 @@ func (z zoneService) CreateCustomHostname(ctx context.Context, zoneID string, ch type DNSRecordsConfig struct { PerPage int Comment string + Tags string } func (c *DNSRecordsConfig) trimAndValidateComment(dnsName, comment string, paidZone func(string) bool) string { @@ -213,6 +214,20 @@ func (c *DNSRecordsConfig) trimAndValidateComment(dnsName, comment string, paidZ return comment } +func (c *DNSRecordsConfig) validateTags(dnsName string, tags []string, paidZone func(string) bool) []string { + if tags == nil { + return nil + } + + if !paidZone(dnsName) { + log.Warnf("DNS record tags is not supported for free zones. Skipping for %s", dnsName) + c.Tags = "" + return nil + } + + return tags +} + func (p *CloudFlareProvider) ZoneHasPaidPlan(hostname string) bool { zone, err := publicsuffix.EffectiveTLDPlusOne(hostname) if err != nil { @@ -324,6 +339,9 @@ func NewCloudFlareProvider(domainFilter endpoint.DomainFilter, zoneIDFilter prov return nil, fmt.Errorf("failed to initialize cloudflare provider: %w", err) } + tags := strings.Split(dnsRecordsConfig.Tags, ",") + sort.Strings(tags) + dnsRecordsConfig.Tags = strings.Join(tags, ",") return &CloudFlareProvider{ Client: zoneService{config}, domainFilter: domainFilter, @@ -881,6 +899,17 @@ func (p *CloudFlareProvider) newCloudFlareChange(action string, ep *endpoint.End comment = p.DNSRecordsConfig.trimAndValidateComment(ep.DNSName, comment, p.ZoneHasPaidPlan) } + // Load tags from program flag + tags := p.DNSRecordsConfig.Tags + if val, ok := ep.GetProviderSpecificProperty(annotations.CloudflareRecordTagsKey); ok { + // Replace comment with Ingress annotation + t := strings.Split(val, ",") + sort.Strings(t) + tags = strings.Join(t, ",") + } + validTags := strings.Split(tags, ",") + validTags = p.DNSRecordsConfig.validateTags(ep.DNSName, validTags, p.ZoneHasPaidPlan) + return &cloudFlareChange{ Action: action, ResourceRecord: cloudflare.DNSRecord{ @@ -892,6 +921,7 @@ func (p *CloudFlareProvider) newCloudFlareChange(action string, ep *endpoint.End Type: ep.RecordType, Content: target, Comment: comment, + Tags: validTags, }, RegionalHostname: regionalHostname, CustomHostnamesPrev: prevCustomHostnames, diff --git a/source/annotations/annotations.go b/source/annotations/annotations.go index 5c98516e3..826a949d7 100644 --- a/source/annotations/annotations.go +++ b/source/annotations/annotations.go @@ -23,6 +23,7 @@ const ( 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" + CloudflareRecordTagsKey = "external-dns.alpha.kubernetes.io/cloudflare-record-tags" AWSPrefix = "external-dns.alpha.kubernetes.io/aws-" SCWPrefix = "external-dns.alpha.kubernetes.io/scw-" From da59638679bdcea137570591a5308b3dba64bfc6 Mon Sep 17 00:00:00 2001 From: Tom M G Date: Mon, 2 Jun 2025 10:23:35 +0200 Subject: [PATCH 2/7] Add missing import --- provider/cloudflare/cloudflare_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/provider/cloudflare/cloudflare_test.go b/provider/cloudflare/cloudflare_test.go index e84295f97..61e053947 100644 --- a/provider/cloudflare/cloudflare_test.go +++ b/provider/cloudflare/cloudflare_test.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" "os" + "reflect" "slices" "sort" "strings" From b509aec5151272a476cba5c565563c73d0bb61b3 Mon Sep 17 00:00:00 2001 From: Tom M G Date: Mon, 2 Jun 2025 14:28:43 +0200 Subject: [PATCH 3/7] Set tags via providerSpecific annotations --- source/annotations/provider_specific.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/annotations/provider_specific.go b/source/annotations/provider_specific.go index 93612ff09..a928473f8 100644 --- a/source/annotations/provider_specific.go +++ b/source/annotations/provider_specific.go @@ -73,6 +73,12 @@ func ProviderSpecificAnnotations(annotations map[string]string) (endpoint.Provid Name: CloudflareRecordCommentKey, Value: v, }) + + } else if strings.Contains(k, CloudflareRecordTagsKey) { + providerSpecificAnnotations = append(providerSpecificAnnotations, endpoint.ProviderSpecificProperty{ + Name: CloudflareRecordTagsKey, + Value: v, + }) } } } From f48e5c7c66c8f3058a1a8a1597b45164d4ca518a Mon Sep 17 00:00:00 2001 From: Tom M G Date: Tue, 3 Jun 2025 17:54:23 +0200 Subject: [PATCH 4/7] Improve code readability by using early returns --- provider/cloudflare/cloudflare.go | 35 ++++++++++++++----------- source/annotations/provider_specific.go | 1 - 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/provider/cloudflare/cloudflare.go b/provider/cloudflare/cloudflare.go index 3d3e8de18..664d9e937 100644 --- a/provider/cloudflare/cloudflare.go +++ b/provider/cloudflare/cloudflare.go @@ -189,15 +189,20 @@ type DNSRecordsConfig struct { } 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] - } + if len(comment) <= freeZoneMaxCommentLength { + return comment } + + maxLength := freeZoneMaxCommentLength + if paidZone(dnsName) { + maxLength = paidZoneMaxCommentLength + } + + if len(comment) > maxLength { + log.Warnf("DNS record comment is invalid. Trimming comment of %s. To avoid endless syncs, please set it to less than %d chars.", dnsName, maxLength) + return comment[:maxLength] + } + return comment } @@ -206,15 +211,15 @@ func (c *DNSRecordsConfig) validTags(dnsName string, paidZone func(string) bool) return nil } - if !paidZone(dnsName) { - log.Warnf("DNS record tags is not supported for free zones. Skipping for %s", dnsName) - c.Tags = "" - return nil + if paidZone(dnsName) { + tags := strings.Split(c.Tags, ",") + sort.Strings(tags) + return tags } - tags := strings.Split(c.Tags, ",") - sort.Strings(tags) - return tags + log.Warnf("DNS record tags are not supported for free zones. Skipping for %s", dnsName) + c.Tags = "" + return nil } func (p *CloudFlareProvider) ZoneHasPaidPlan(hostname string) bool { diff --git a/source/annotations/provider_specific.go b/source/annotations/provider_specific.go index a928473f8..9562c773b 100644 --- a/source/annotations/provider_specific.go +++ b/source/annotations/provider_specific.go @@ -73,7 +73,6 @@ func ProviderSpecificAnnotations(annotations map[string]string) (endpoint.Provid Name: CloudflareRecordCommentKey, Value: v, }) - } else if strings.Contains(k, CloudflareRecordTagsKey) { providerSpecificAnnotations = append(providerSpecificAnnotations, endpoint.ProviderSpecificProperty{ Name: CloudflareRecordTagsKey, From 17e1d5979feeb99b8f75c5925c94410dce40d68d Mon Sep 17 00:00:00 2001 From: Tom M G Date: Fri, 27 Jun 2025 15:27:10 +0200 Subject: [PATCH 5/7] Add comment and tags in getCreateDNSRecordParam + updateDNSRecordParam + AdjustEndpoints --- pkg/apis/externaldns/types.go | 2 +- provider/cloudflare/cloudflare.go | 67 ++++++++++++++++++++----------- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index daf6e8afd..bcb502e55 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -535,7 +535,7 @@ func App(cfg *Config) *kingpin.Application { 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-regional-services", "When using the Cloudflare provider, specify if Regional Services feature will be used (default: disabled)").Default(strconv.FormatBool(defaultConfig.CloudflareRegionalServices)).BoolVar(&cfg.CloudflareRegionalServices) app.Flag("cloudflare-region-key", "When using the Cloudflare provider, specify the default region for Regional Services. Any value other than an empty string will enable the Regional Services feature (optional)").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("cloudflare-record-comment", "When using the Cloudflare provider, specify the comment for the DNS records (default: '')").Default("").StringVar(&cfg.CloudflareDNSRecordsComment) app.Flag("cloudflare-record-tags", "When using the Cloudflare provider for a paid zone, specify the tags for the DNS records as a comma-separated string (default: '')").Default("").StringVar(&cfg.CloudflareDNSRecordsTags) app.Flag("coredns-prefix", "When using the CoreDNS provider, specify the prefix name").Default(defaultConfig.CoreDNSPrefix).StringVar(&cfg.CoreDNSPrefix) diff --git a/provider/cloudflare/cloudflare.go b/provider/cloudflare/cloudflare.go index d4c916297..e936e51d4 100644 --- a/provider/cloudflare/cloudflare.go +++ b/provider/cloudflare/cloudflare.go @@ -187,19 +187,27 @@ func (c *DNSRecordsConfig) trimAndValidateComment(dnsName, comment string, paidZ return comment } -func (c *DNSRecordsConfig) validTags(dnsName string, paidZone func(string) bool) []string { - if c.Tags == "" { - return nil +func (c *DNSRecordsConfig) validTags(dnsName string, paidZone func(string) bool, tagsFromAnnotation []string) []string { + if len(tagsFromAnnotation) > 0 || c.Tags != "" { + paidZone := paidZone(dnsName) + if !paidZone { + log.Warnf("DNS record tags are not supported for free zones. Skipping for %s", dnsName) + c.Tags = "" + return nil + } } - if paidZone(dnsName) { + if len(tagsFromAnnotation) > 0 { + sort.Strings(tagsFromAnnotation) + return tagsFromAnnotation + } + + if c.Tags != "" { tags := strings.Split(c.Tags, ",") sort.Strings(tags) return tags } - log.Warnf("DNS record tags are not supported for free zones. Skipping for %s", dnsName) - c.Tags = "" return nil } @@ -261,6 +269,8 @@ func updateDNSRecordParam(cfc cloudFlareChange) cloudflare.UpdateDNSRecordParams Type: cfc.ResourceRecord.Type, Content: cfc.ResourceRecord.Content, Priority: cfc.ResourceRecord.Priority, + Comment: &cfc.ResourceRecord.Comment, + Tags: cfc.ResourceRecord.Tags, } return params @@ -275,6 +285,8 @@ func getCreateDNSRecordParam(cfc cloudFlareChange) cloudflare.CreateDNSRecordPar Type: cfc.ResourceRecord.Type, Content: cfc.ResourceRecord.Content, Priority: cfc.ResourceRecord.Priority, + Comment: cfc.ResourceRecord.Comment, + Tags: cfc.ResourceRecord.Tags, } return params @@ -329,10 +341,6 @@ func NewCloudFlareProvider( return nil, fmt.Errorf("failed to initialize cloudflare provider: %w", err) } - tags := strings.Split(dnsRecordsConfig.Tags, ",") - sort.Strings(tags) - dnsRecordsConfig.Tags = strings.Join(tags, ",") - if regionalServicesConfig.RegionKey != "" { regionalServicesConfig.Enabled = true } @@ -602,11 +610,13 @@ func (p *CloudFlareProvider) submitChanges(ctx context.Context, changes []*cloud for _, change := range zoneChanges { logFields := log.Fields{ - "record": change.ResourceRecord.Name, - "type": change.ResourceRecord.Type, - "ttl": change.ResourceRecord.TTL, - "action": change.Action, - "zone": zoneID, + "record": change.ResourceRecord.Name, + "type": change.ResourceRecord.Type, + "ttl": change.ResourceRecord.TTL, + "action": change.Action, + "zone": zoneID, + "comment": change.ResourceRecord.Comment, + "tags": change.ResourceRecord.Tags, } log.WithFields(logFields).Info("Changing record.") @@ -723,6 +733,14 @@ func (p *CloudFlareProvider) AdjustEndpoints(endpoints []*endpoint.Endpoint) ([] } } + // if _, ok := e.GetProviderSpecificProperty(annotations.CloudflareRecordCommentKey); !ok { + // e.SetProviderSpecificProperty(annotations.CloudflareRecordCommentKey, p.DNSRecordsConfig.Comment) + // } + + // if _, ok := e.GetProviderSpecificProperty(annotations.CloudflareRecordTagsKey); !ok { + // e.SetProviderSpecificProperty(annotations.CloudflareRecordTagsKey, p.DNSRecordsConfig.Tags) + // } + adjustedEndpoints = append(adjustedEndpoints, e) } return adjustedEndpoints, nil @@ -813,15 +831,14 @@ func (p *CloudFlareProvider) newCloudFlareChange(action changeAction, ep *endpoi } else { priority = mxRecord.GetPriority() target = *mxRecord.GetHost() - } - } - - // Load tags from program flag + } + } + + var tagsFromAnnotation []string + // Load tags from program flag if val, ok := ep.GetProviderSpecificProperty(annotations.CloudflareRecordTagsKey); ok { // Replace comment with Ingress annotation - t := strings.Split(val, ",") - sort.Strings(t) - p.DNSRecordsConfig.Tags = strings.Join(t, ",") + tagsFromAnnotation = strings.Split(val, ",") } return &cloudFlareChange{ @@ -836,7 +853,7 @@ func (p *CloudFlareProvider) newCloudFlareChange(action changeAction, ep *endpoi Content: target, Comment: comment, Priority: priority, - Tags: p.DNSRecordsConfig.validTags(ep.DNSName, p.ZoneHasPaidPlan), + Tags: p.DNSRecordsConfig.validTags(ep.DNSName, p.ZoneHasPaidPlan, tagsFromAnnotation), }, RegionalHostname: p.regionalHostname(ep), CustomHostnamesPrev: prevCustomHostnames, @@ -1012,6 +1029,10 @@ func (p *CloudFlareProvider) groupByNameAndTypeWithCustomHostnames(records DNSRe e = e.WithProviderSpecific(annotations.CloudflareRecordCommentKey, records[0].Comment) } + if len(records[0].Tags) > 0 { + e = e.WithProviderSpecific(annotations.CloudflareRecordTagsKey, strings.Join(records[0].Tags, ",")) + } + endpoints = append(endpoints, e) } return endpoints From bd289e5a172c3469ce2515889b7b29d0c2595661 Mon Sep 17 00:00:00 2001 From: Tom M G Date: Fri, 27 Jun 2025 21:01:09 +0200 Subject: [PATCH 6/7] Avoid PaidZone repetitive call --- provider/cloudflare/cloudflare.go | 9 ++++++++- provider/cloudflare/cloudflare_test.go | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/provider/cloudflare/cloudflare.go b/provider/cloudflare/cloudflare.go index e936e51d4..0db5b4d7f 100644 --- a/provider/cloudflare/cloudflare.go +++ b/provider/cloudflare/cloudflare.go @@ -217,6 +217,11 @@ func (p *CloudFlareProvider) ZoneHasPaidPlan(hostname string) bool { log.Errorf("Failed to get effective TLD+1 for hostname %s %v", hostname, err) return false } + + if paidZone, ok := p.PaidZones[zone]; ok { + return paidZone + } + zoneID, err := p.Client.ZoneIDByName(zone) if err != nil { log.Errorf("Failed to get zone %s by name %v", zone, err) @@ -229,7 +234,8 @@ func (p *CloudFlareProvider) ZoneHasPaidPlan(hostname string) bool { return false } - return zoneDetails.Plan.IsSubscribed + p.PaidZones[zone] = zoneDetails.Plan.IsSubscribed + return p.PaidZones[zone] } // CloudFlareProvider is an implementation of Provider for CloudFlare DNS. @@ -244,6 +250,7 @@ type CloudFlareProvider struct { CustomHostnamesConfig CustomHostnamesConfig DNSRecordsConfig DNSRecordsConfig RegionalServicesConfig RegionalServicesConfig + PaidZones map[string]bool } // cloudFlareChange differentiates between ChangeActions diff --git a/provider/cloudflare/cloudflare_test.go b/provider/cloudflare/cloudflare_test.go index 3b324ee20..786698e5c 100644 --- a/provider/cloudflare/cloudflare_test.go +++ b/provider/cloudflare/cloudflare_test.go @@ -1973,7 +1973,7 @@ func TestCloudFlareProvider_newCloudFlareChange(t *testing.T) { for _, test := range tagsTestCases { t.Run(test.name, func(t *testing.T) { - change := test.provider.newCloudFlareChange(cloudFlareCreate, test.endpoint, test.endpoint.Targets[0], nil) + change, _ := test.provider.newCloudFlareChange(cloudFlareCreate, test.endpoint, test.endpoint.Targets[0], nil) if test.expected == nil && len(change.ResourceRecord.Tags) != 0 { t.Errorf("expected tags to be %v, but got %v", test.expected, change.ResourceRecord.Tags) } else if !reflect.DeepEqual(change.ResourceRecord.Tags, test.expected) { From a19a8e6a8a8cf0e9afe072b380d1cfeeef61de7d Mon Sep 17 00:00:00 2001 From: Tom M G Date: Mon, 21 Jul 2025 20:55:14 +0200 Subject: [PATCH 7/7] Fix broken test lacking PaidZones init in Cloudflare provider --- provider/cloudflare/cloudflare.go | 1 + provider/cloudflare/cloudflare_test.go | 2 ++ 2 files changed, 3 insertions(+) diff --git a/provider/cloudflare/cloudflare.go b/provider/cloudflare/cloudflare.go index bed822033..1b6e9be56 100644 --- a/provider/cloudflare/cloudflare.go +++ b/provider/cloudflare/cloudflare.go @@ -373,6 +373,7 @@ func NewCloudFlareProvider( DryRun: dryRun, RegionalServicesConfig: regionalServicesConfig, DNSRecordsConfig: dnsRecordsConfig, + PaidZones: make(map[string]bool), }, nil } diff --git a/provider/cloudflare/cloudflare_test.go b/provider/cloudflare/cloudflare_test.go index 4ddf68702..0602eb695 100644 --- a/provider/cloudflare/cloudflare_test.go +++ b/provider/cloudflare/cloudflare_test.go @@ -2646,6 +2646,7 @@ func TestZoneHasPaidPlan(t *testing.T) { Client: client, domainFilter: endpoint.NewDomainFilter([]string{"foo.com", "bar.com"}), zoneIDFilter: provider.NewZoneIDFilter([]string{""}), + PaidZones: make(map[string]bool), } assert.False(t, cfprovider.ZoneHasPaidPlan("subdomain.foo.com")) @@ -2657,6 +2658,7 @@ func TestZoneHasPaidPlan(t *testing.T) { Client: client, domainFilter: endpoint.NewDomainFilter([]string{"foo.com", "bar.com"}), zoneIDFilter: provider.NewZoneIDFilter([]string{""}), + PaidZones: make(map[string]bool), } assert.False(t, cfproviderWithZoneError.ZoneHasPaidPlan("subdomain.foo.com")) }