mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 01:26:59 +02:00
Merge pull request #5239 from conduitxyz/mrozentsvayg/cloudflare-multiple-custom-hostnames
feat(cloudflare): multiple custom hostnames support
This commit is contained in:
commit
026feab62b
1
.gitignore
vendored
1
.gitignore
vendored
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
# Vscode files
|
# Vscode files
|
||||||
.vscode
|
.vscode
|
||||||
|
__debug_*
|
||||||
|
|
||||||
# This is where the result of the go build goes
|
# This is where the result of the go build goes
|
||||||
/output*/
|
/output*/
|
||||||
|
@ -310,7 +310,9 @@ If not set the value will default to `global`.
|
|||||||
|
|
||||||
## Setting cloudflare-custom-hostname
|
## Setting cloudflare-custom-hostname
|
||||||
|
|
||||||
You can automatically configure custom hostnames for A/CNAME DNS records (as custom origins) using the `--cloudflare-custom-hostnames` flag and the `external-dns.alpha.kubernetes.io/cloudflare-custom-hostname: "<custom hostname>"` annotation.
|
Automatic configuration of Cloudflare custom hostnames (using A/CNAME DNS records as custom origin servers) is enabled by the --cloudflare-custom-hostnames flag and the `external-dns.alpha.kubernetes.io/cloudflare-custom-hostname: <custom hostname>` annotation.
|
||||||
|
|
||||||
|
Multiple hostnames are supported via a comma-separated list: `external-dns.alpha.kubernetes.io/cloudflare-custom-hostname: <custom hostname 1>,<custom hostname 2>`.
|
||||||
|
|
||||||
See [Cloudflare for Platforms](https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/domain-support/) for more information on custom hostnames.
|
See [Cloudflare for Platforms](https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/domain-support/) for more information on custom hostnames.
|
||||||
|
|
||||||
|
@ -20,8 +20,11 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"maps"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -182,11 +185,11 @@ type CloudFlareProvider struct {
|
|||||||
|
|
||||||
// cloudFlareChange differentiates between ChangActions
|
// cloudFlareChange differentiates between ChangActions
|
||||||
type cloudFlareChange struct {
|
type cloudFlareChange struct {
|
||||||
Action string
|
Action string
|
||||||
ResourceRecord cloudflare.DNSRecord
|
ResourceRecord cloudflare.DNSRecord
|
||||||
RegionalHostname cloudflare.RegionalHostname
|
RegionalHostname cloudflare.RegionalHostname
|
||||||
CustomHostname cloudflare.CustomHostname
|
CustomHostnames map[string]cloudflare.CustomHostname
|
||||||
CustomHostnamePrev string
|
CustomHostnamesPrev []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// RecordParamsTypes is a typeset of the possible Record Params that can be passed to cloudflare-go library
|
// RecordParamsTypes is a typeset of the possible Record Params that can be passed to cloudflare-go library
|
||||||
@ -324,6 +327,7 @@ func (p *CloudFlareProvider) Records(ctx context.Context) ([]*endpoint.Endpoint,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nil if custom hostnames are not enabled
|
||||||
chs, chErr := p.listCustomHostnamesWithPagination(ctx, zone.ID)
|
chs, chErr := p.listCustomHostnamesWithPagination(ctx, zone.ID)
|
||||||
if chErr != nil {
|
if chErr != nil {
|
||||||
return nil, chErr
|
return nil, chErr
|
||||||
@ -342,9 +346,18 @@ func (p *CloudFlareProvider) Records(ctx context.Context) ([]*endpoint.Endpoint,
|
|||||||
func (p *CloudFlareProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
|
func (p *CloudFlareProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
|
||||||
cloudflareChanges := []*cloudFlareChange{}
|
cloudflareChanges := []*cloudFlareChange{}
|
||||||
|
|
||||||
for _, endpoint := range changes.Create {
|
// if custom hostnames are enabled, deleting first allows to avoid conflicts with the new ones
|
||||||
for _, target := range endpoint.Targets {
|
if p.CustomHostnamesConfig.Enabled {
|
||||||
cloudflareChanges = append(cloudflareChanges, p.newCloudFlareChange(cloudFlareCreate, endpoint, target, nil))
|
for _, e := range changes.Delete {
|
||||||
|
for _, target := range e.Targets {
|
||||||
|
cloudflareChanges = append(cloudflareChanges, p.newCloudFlareChange(cloudFlareDelete, e, target, nil))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range changes.Create {
|
||||||
|
for _, target := range e.Targets {
|
||||||
|
cloudflareChanges = append(cloudflareChanges, p.newCloudFlareChange(cloudFlareCreate, e, target, nil))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,9 +379,12 @@ func (p *CloudFlareProvider) ApplyChanges(ctx context.Context, changes *plan.Cha
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, endpoint := range changes.Delete {
|
// TODO: consider deleting before creating even if custom hostnames are not in use
|
||||||
for _, target := range endpoint.Targets {
|
if !p.CustomHostnamesConfig.Enabled {
|
||||||
cloudflareChanges = append(cloudflareChanges, p.newCloudFlareChange(cloudFlareDelete, endpoint, target, nil))
|
for _, e := range changes.Delete {
|
||||||
|
for _, target := range e.Targets {
|
||||||
|
cloudflareChanges = append(cloudflareChanges, p.newCloudFlareChange(cloudFlareDelete, e, target, nil))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,57 +402,63 @@ func (p *CloudFlareProvider) submitCustomHostnameChanges(ctx context.Context, zo
|
|||||||
switch change.Action {
|
switch change.Action {
|
||||||
case cloudFlareUpdate:
|
case cloudFlareUpdate:
|
||||||
if recordTypeCustomHostnameSupported[change.ResourceRecord.Type] {
|
if recordTypeCustomHostnameSupported[change.ResourceRecord.Type] {
|
||||||
prevChName := change.CustomHostnamePrev
|
add, remove, _ := provider.Difference(change.CustomHostnamesPrev, slices.Collect(maps.Keys(change.CustomHostnames)))
|
||||||
newChName := change.CustomHostname.Hostname
|
|
||||||
if prevCh, err := getCustomHostname(chs, prevChName); err == nil {
|
for _, changeCH := range remove {
|
||||||
prevChID := prevCh.ID
|
if prevCh, err := getCustomHostname(chs, changeCH); err == nil {
|
||||||
if prevChID != "" && prevChName != newChName {
|
prevChID := prevCh.ID
|
||||||
log.WithFields(logFields).Infof("Removing previous custom hostname %q/%q", prevChID, prevChName)
|
if prevChID != "" {
|
||||||
chErr := p.Client.DeleteCustomHostname(ctx, zoneID, prevChID)
|
log.WithFields(logFields).Infof("Removing previous custom hostname %q/%q", prevChID, changeCH)
|
||||||
if chErr != nil {
|
chErr := p.Client.DeleteCustomHostname(ctx, zoneID, prevChID)
|
||||||
failedChange = true
|
if chErr != nil {
|
||||||
log.WithFields(logFields).Errorf("failed to remove previous custom hostname %q/%q: %v", prevChID, prevChName, chErr)
|
failedChange = true
|
||||||
|
log.WithFields(logFields).Errorf("failed to remove previous custom hostname %q/%q: %v", prevChID, changeCH, chErr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if newChName != "" && prevChName != newChName {
|
for _, changeCH := range add {
|
||||||
log.WithFields(logFields).Infof("Adding custom hostname %q", newChName)
|
log.WithFields(logFields).Infof("Adding custom hostname %q", changeCH)
|
||||||
_, chErr := p.Client.CreateCustomHostname(ctx, zoneID, change.CustomHostname)
|
_, chErr := p.Client.CreateCustomHostname(ctx, zoneID, change.CustomHostnames[changeCH])
|
||||||
if chErr != nil {
|
if chErr != nil {
|
||||||
failedChange = true
|
failedChange = true
|
||||||
log.WithFields(logFields).Errorf("failed to add custom hostname %q: %v", newChName, chErr)
|
log.WithFields(logFields).Errorf("failed to add custom hostname %q: %v", changeCH, chErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case cloudFlareDelete:
|
case cloudFlareDelete:
|
||||||
if recordTypeCustomHostnameSupported[change.ResourceRecord.Type] && change.CustomHostname.Hostname != "" {
|
for _, changeCH := range change.CustomHostnames {
|
||||||
log.WithFields(logFields).Infof("Deleting custom hostname %q", change.CustomHostname.Hostname)
|
if recordTypeCustomHostnameSupported[change.ResourceRecord.Type] && changeCH.Hostname != "" {
|
||||||
if ch, err := getCustomHostname(chs, change.CustomHostname.Hostname); err == nil {
|
log.WithFields(logFields).Infof("Deleting custom hostname %q", changeCH.Hostname)
|
||||||
chID := ch.ID
|
if ch, err := getCustomHostname(chs, changeCH.Hostname); err == nil {
|
||||||
chErr := p.Client.DeleteCustomHostname(ctx, zoneID, chID)
|
chID := ch.ID
|
||||||
if chErr != nil {
|
chErr := p.Client.DeleteCustomHostname(ctx, zoneID, chID)
|
||||||
failedChange = true
|
if chErr != nil {
|
||||||
log.WithFields(logFields).Errorf("failed to delete custom hostname %q/%q: %v", chID, change.CustomHostname.Hostname, chErr)
|
failedChange = true
|
||||||
|
log.WithFields(logFields).Errorf("failed to delete custom hostname %q/%q: %v", chID, changeCH.Hostname, chErr)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.WithFields(logFields).Warnf("failed to delete custom hostname %q: %v", changeCH.Hostname, err)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
log.WithFields(logFields).Warnf("failed to delete custom hostname %q: %v", change.CustomHostname.Hostname, err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case cloudFlareCreate:
|
case cloudFlareCreate:
|
||||||
if recordTypeCustomHostnameSupported[change.ResourceRecord.Type] && change.CustomHostname.Hostname != "" {
|
for _, changeCH := range change.CustomHostnames {
|
||||||
log.WithFields(logFields).Infof("Creating custom hostname %q", change.CustomHostname.Hostname)
|
if recordTypeCustomHostnameSupported[change.ResourceRecord.Type] && changeCH.Hostname != "" {
|
||||||
if ch, err := getCustomHostname(chs, change.CustomHostname.Hostname); err == nil {
|
log.WithFields(logFields).Infof("Creating custom hostname %q", changeCH.Hostname)
|
||||||
if change.CustomHostname.CustomOriginServer == ch.CustomOriginServer {
|
if ch, err := getCustomHostname(chs, changeCH.Hostname); err == nil {
|
||||||
log.WithFields(logFields).Warnf("custom hostname %q already exists with the same origin %q, continue", change.CustomHostname.Hostname, ch.CustomOriginServer)
|
if changeCH.CustomOriginServer == ch.CustomOriginServer {
|
||||||
|
log.WithFields(logFields).Warnf("custom hostname %q already exists with the same origin %q, continue", changeCH.Hostname, ch.CustomOriginServer)
|
||||||
|
} else {
|
||||||
|
failedChange = true
|
||||||
|
log.WithFields(logFields).Errorf("failed to create custom hostname, %q already exists with origin %q", changeCH.Hostname, ch.CustomOriginServer)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
failedChange = true
|
_, chErr := p.Client.CreateCustomHostname(ctx, zoneID, changeCH)
|
||||||
log.WithFields(logFields).Errorf("failed to create custom hostname, %q already exists with origin %q", change.CustomHostname.Hostname, ch.CustomOriginServer)
|
if chErr != nil {
|
||||||
}
|
failedChange = true
|
||||||
} else {
|
log.WithFields(logFields).Errorf("failed to create custom hostname %q: %v", changeCH.Hostname, chErr)
|
||||||
_, chErr := p.Client.CreateCustomHostname(ctx, zoneID, change.CustomHostname)
|
}
|
||||||
if chErr != nil {
|
|
||||||
failedChange = true
|
|
||||||
log.WithFields(logFields).Errorf("failed to create custom hostname %q: %v", change.CustomHostname.Hostname, chErr)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -460,9 +482,9 @@ func (p *CloudFlareProvider) submitChanges(ctx context.Context, changes []*cloud
|
|||||||
changesByZone := p.changesByZone(zones, changes)
|
changesByZone := p.changesByZone(zones, changes)
|
||||||
|
|
||||||
var failedZones []string
|
var failedZones []string
|
||||||
for zoneID, changes := range changesByZone {
|
for zoneID, zoneChanges := range changesByZone {
|
||||||
var failedChange bool
|
var failedChange bool
|
||||||
for _, change := range changes {
|
for _, change := range zoneChanges {
|
||||||
logFields := log.Fields{
|
logFields := log.Fields{
|
||||||
"record": change.ResourceRecord.Name,
|
"record": change.ResourceRecord.Name,
|
||||||
"type": change.ResourceRecord.Type,
|
"type": change.ResourceRecord.Type,
|
||||||
@ -557,6 +579,17 @@ func (p *CloudFlareProvider) AdjustEndpoints(endpoints []*endpoint.Endpoint) ([]
|
|||||||
}
|
}
|
||||||
e.SetProviderSpecificProperty(source.CloudflareProxiedKey, strconv.FormatBool(proxied))
|
e.SetProviderSpecificProperty(source.CloudflareProxiedKey, strconv.FormatBool(proxied))
|
||||||
|
|
||||||
|
if p.CustomHostnamesConfig.Enabled {
|
||||||
|
// sort custom hostnames in annotation to properly detect changes
|
||||||
|
if customHostnames := getEndpointCustomHostnames(e); len(customHostnames) > 1 {
|
||||||
|
sort.Strings(customHostnames)
|
||||||
|
e.SetProviderSpecificProperty(source.CloudflareCustomHostnameKey, strings.Join(customHostnames, ","))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ignore custom hostnames annotations if not enabled
|
||||||
|
e.DeleteProviderSpecificProperty(source.CloudflareCustomHostnameKey)
|
||||||
|
}
|
||||||
|
|
||||||
adjustedEndpoints = append(adjustedEndpoints, e)
|
adjustedEndpoints = append(adjustedEndpoints, e)
|
||||||
}
|
}
|
||||||
return adjustedEndpoints, nil
|
return adjustedEndpoints, nil
|
||||||
@ -601,48 +634,54 @@ func getCustomHostname(chs CustomHostnamesMap, chName string) (cloudflare.Custom
|
|||||||
return cloudflare.CustomHostname{}, fmt.Errorf("failed to get custom hostname: %q not found", chName)
|
return cloudflare.CustomHostname{}, fmt.Errorf("failed to get custom hostname: %q not found", chName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *CloudFlareProvider) newCloudFlareChange(action string, endpoint *endpoint.Endpoint, target string, current *endpoint.Endpoint) *cloudFlareChange {
|
func (p *CloudFlareProvider) newCustomHostname(customHostname string, origin string) cloudflare.CustomHostname {
|
||||||
ttl := defaultCloudFlareRecordTTL
|
return cloudflare.CustomHostname{
|
||||||
proxied := shouldBeProxied(endpoint, p.proxiedByDefault)
|
Hostname: customHostname,
|
||||||
|
CustomOriginServer: origin,
|
||||||
|
SSL: getCustomHostnamesSSLOptions(p.CustomHostnamesConfig),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if endpoint.RecordTTL.IsConfigured() {
|
func (p *CloudFlareProvider) newCloudFlareChange(action string, ep *endpoint.Endpoint, target string, current *endpoint.Endpoint) *cloudFlareChange {
|
||||||
ttl = int(endpoint.RecordTTL)
|
ttl := defaultCloudFlareRecordTTL
|
||||||
|
proxied := shouldBeProxied(ep, p.proxiedByDefault)
|
||||||
|
|
||||||
|
if ep.RecordTTL.IsConfigured() {
|
||||||
|
ttl = int(ep.RecordTTL)
|
||||||
}
|
}
|
||||||
dt := time.Now()
|
dt := time.Now()
|
||||||
|
|
||||||
customHostnamePrev := ""
|
prevCustomHostnames := []string{}
|
||||||
newCustomHostname := cloudflare.CustomHostname{}
|
newCustomHostnames := map[string]cloudflare.CustomHostname{}
|
||||||
if p.CustomHostnamesConfig.Enabled {
|
if p.CustomHostnamesConfig.Enabled {
|
||||||
if current != nil {
|
if current != nil {
|
||||||
customHostnamePrev = getEndpointCustomHostname(current)
|
prevCustomHostnames = getEndpointCustomHostnames(current)
|
||||||
}
|
}
|
||||||
newCustomHostname = cloudflare.CustomHostname{
|
for _, v := range getEndpointCustomHostnames(ep) {
|
||||||
Hostname: getEndpointCustomHostname(endpoint),
|
newCustomHostnames[v] = p.newCustomHostname(v, ep.DNSName)
|
||||||
CustomOriginServer: endpoint.DNSName,
|
|
||||||
SSL: getCustomHostnamesSSLOptions(p.CustomHostnamesConfig),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &cloudFlareChange{
|
return &cloudFlareChange{
|
||||||
Action: action,
|
Action: action,
|
||||||
ResourceRecord: cloudflare.DNSRecord{
|
ResourceRecord: cloudflare.DNSRecord{
|
||||||
Name: endpoint.DNSName,
|
Name: ep.DNSName,
|
||||||
TTL: ttl,
|
TTL: ttl,
|
||||||
// We have to use pointers to bools now, as the upstream cloudflare-go library requires them
|
// We have to use pointers to bools now, as the upstream cloudflare-go library requires them
|
||||||
// see: https://github.com/cloudflare/cloudflare-go/pull/595
|
// see: https://github.com/cloudflare/cloudflare-go/pull/595
|
||||||
Proxied: &proxied,
|
Proxied: &proxied,
|
||||||
Type: endpoint.RecordType,
|
Type: ep.RecordType,
|
||||||
Content: target,
|
Content: target,
|
||||||
Meta: map[string]interface{}{
|
Meta: map[string]interface{}{
|
||||||
"region": p.RegionKey,
|
"region": p.RegionKey,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
RegionalHostname: cloudflare.RegionalHostname{
|
RegionalHostname: cloudflare.RegionalHostname{
|
||||||
Hostname: endpoint.DNSName,
|
Hostname: ep.DNSName,
|
||||||
RegionKey: p.RegionKey,
|
RegionKey: p.RegionKey,
|
||||||
CreatedOn: &dt,
|
CreatedOn: &dt,
|
||||||
},
|
},
|
||||||
CustomHostnamePrev: customHostnamePrev,
|
CustomHostnamesPrev: prevCustomHostnames,
|
||||||
CustomHostname: newCustomHostname,
|
CustomHostnames: newCustomHostnames,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -727,10 +766,10 @@ func getCustomHostnamesSSLOptions(customHostnamesConfig CustomHostnamesConfig) *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func shouldBeProxied(endpoint *endpoint.Endpoint, proxiedByDefault bool) bool {
|
func shouldBeProxied(ep *endpoint.Endpoint, proxiedByDefault bool) bool {
|
||||||
proxied := proxiedByDefault
|
proxied := proxiedByDefault
|
||||||
|
|
||||||
for _, v := range endpoint.ProviderSpecific {
|
for _, v := range ep.ProviderSpecific {
|
||||||
if v.Name == source.CloudflareProxiedKey {
|
if v.Name == source.CloudflareProxiedKey {
|
||||||
b, err := strconv.ParseBool(v.Value)
|
b, err := strconv.ParseBool(v.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -742,19 +781,20 @@ func shouldBeProxied(endpoint *endpoint.Endpoint, proxiedByDefault bool) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if recordTypeProxyNotSupported[endpoint.RecordType] {
|
if recordTypeProxyNotSupported[ep.RecordType] {
|
||||||
proxied = false
|
proxied = false
|
||||||
}
|
}
|
||||||
return proxied
|
return proxied
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEndpointCustomHostname(endpoint *endpoint.Endpoint) string {
|
func getEndpointCustomHostnames(ep *endpoint.Endpoint) []string {
|
||||||
for _, v := range endpoint.ProviderSpecific {
|
for _, v := range ep.ProviderSpecific {
|
||||||
if v.Name == source.CloudflareCustomHostnameKey {
|
if v.Name == source.CloudflareCustomHostnameKey {
|
||||||
return v.Value
|
customHostnames := strings.Split(v.Value, ",")
|
||||||
|
return customHostnames
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ""
|
return []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func groupByNameAndTypeWithCustomHostnames(records DNSRecordsMap, chs CustomHostnamesMap) []*endpoint.Endpoint {
|
func groupByNameAndTypeWithCustomHostnames(records DNSRecordsMap, chs CustomHostnamesMap) []*endpoint.Endpoint {
|
||||||
@ -777,11 +817,10 @@ func groupByNameAndTypeWithCustomHostnames(records DNSRecordsMap, chs CustomHost
|
|||||||
}
|
}
|
||||||
|
|
||||||
// map custom origin to custom hostname, custom origin should match to a dns record
|
// map custom origin to custom hostname, custom origin should match to a dns record
|
||||||
customOriginServers := map[string]string{}
|
customHostnames := map[string][]string{}
|
||||||
|
|
||||||
// only one latest custom hostname for a dns record would work; noop (chs is empty) if custom hostnames feature is not in use
|
|
||||||
for _, c := range chs {
|
for _, c := range chs {
|
||||||
customOriginServers[c.CustomOriginServer] = c.Hostname
|
customHostnames[c.CustomOriginServer] = append(customHostnames[c.CustomOriginServer], c.Hostname)
|
||||||
}
|
}
|
||||||
|
|
||||||
// create single endpoint with all the targets for each name/type
|
// create single endpoint with all the targets for each name/type
|
||||||
@ -793,7 +832,7 @@ func groupByNameAndTypeWithCustomHostnames(records DNSRecordsMap, chs CustomHost
|
|||||||
for i, record := range records {
|
for i, record := range records {
|
||||||
targets[i] = record.Content
|
targets[i] = record.Content
|
||||||
}
|
}
|
||||||
ep := endpoint.NewEndpointWithTTL(
|
e := endpoint.NewEndpointWithTTL(
|
||||||
records[0].Name,
|
records[0].Name,
|
||||||
records[0].Type,
|
records[0].Type,
|
||||||
endpoint.TTL(records[0].TTL),
|
endpoint.TTL(records[0].TTL),
|
||||||
@ -802,16 +841,17 @@ func groupByNameAndTypeWithCustomHostnames(records DNSRecordsMap, chs CustomHost
|
|||||||
if records[0].Proxied != nil {
|
if records[0].Proxied != nil {
|
||||||
proxied = *records[0].Proxied
|
proxied = *records[0].Proxied
|
||||||
}
|
}
|
||||||
if ep == nil {
|
if e == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
ep = ep.WithProviderSpecific(source.CloudflareProxiedKey, strconv.FormatBool(proxied))
|
e = e.WithProviderSpecific(source.CloudflareProxiedKey, strconv.FormatBool(proxied))
|
||||||
// noop (customOriginServers is empty) if custom hostnames feature is not in use
|
// noop (customHostnames is empty) if custom hostnames feature is not in use
|
||||||
if customHostname, ok := customOriginServers[records[0].Name]; ok {
|
if customHostnames, ok := customHostnames[records[0].Name]; ok {
|
||||||
ep = ep.WithProviderSpecific(source.CloudflareCustomHostnameKey, customHostname)
|
sort.Strings(customHostnames)
|
||||||
|
e = e.WithProviderSpecific(source.CloudflareCustomHostnameKey, strings.Join(customHostnames, ","))
|
||||||
}
|
}
|
||||||
|
|
||||||
endpoints = append(endpoints, ep)
|
endpoints = append(endpoints, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
return endpoints
|
return endpoints
|
||||||
|
@ -405,16 +405,6 @@ func getCustomHostnameIdxByID(chs []cloudflare.CustomHostname, customHostnameID
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCustomHostnameIDbyCustomHostnameAndOrigin(chs CustomHostnamesMap, customHostname string, origin string) (string, string) {
|
|
||||||
for _, ch := range chs {
|
|
||||||
if ch.Hostname == customHostname && ch.CustomOriginServer == origin {
|
|
||||||
return ch.ID, ch.Hostname
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func AssertActions(t *testing.T, provider *CloudFlareProvider, endpoints []*endpoint.Endpoint, actions []MockAction, managedRecords []string, args ...interface{}) {
|
func AssertActions(t *testing.T, provider *CloudFlareProvider, endpoints []*endpoint.Endpoint, actions []MockAction, managedRecords []string, args ...interface{}) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
@ -1772,6 +1762,44 @@ func TestCloudflareZoneRecordsFail(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestCloudflareLongRecordsErrorLog checks if the error is logged when a record name exceeds 63 characters
|
||||||
|
// it's not likely to happen in practice, as the Cloudflare API should reject having it
|
||||||
|
func TestCloudflareLongRecordsErrorLog(t *testing.T) {
|
||||||
|
client := NewMockCloudFlareClientWithRecords(map[string][]cloudflare.DNSRecord{
|
||||||
|
"001": {
|
||||||
|
{
|
||||||
|
ID: "1234567890",
|
||||||
|
Name: "very-very-very-very-very-very-very-long-name-more-than-63-bytes-long.bar.com",
|
||||||
|
Type: endpoint.RecordTypeTXT,
|
||||||
|
TTL: 120,
|
||||||
|
Content: "some-content",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
b := testutils.LogsToBuffer(log.InfoLevel, t)
|
||||||
|
p := &CloudFlareProvider{
|
||||||
|
Client: client,
|
||||||
|
CustomHostnamesConfig: CustomHostnamesConfig{Enabled: true},
|
||||||
|
}
|
||||||
|
ctx := context.Background()
|
||||||
|
_, err := p.Records(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("should not fail - too long record, %s", err)
|
||||||
|
}
|
||||||
|
assert.Contains(t, b.String(), "is longer than 63 characters. Cannot create endpoint")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the error is expected
|
||||||
|
func checkFailed(name string, err error, shouldFail bool) error {
|
||||||
|
if errors.Is(err, nil) && shouldFail {
|
||||||
|
return fmt.Errorf("should fail - %q", name)
|
||||||
|
}
|
||||||
|
if !errors.Is(err, nil) && !shouldFail {
|
||||||
|
return fmt.Errorf("should not fail - %q, %v", name, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func TestCloudflareDNSRecordsOperationsFail(t *testing.T) {
|
func TestCloudflareDNSRecordsOperationsFail(t *testing.T) {
|
||||||
client := NewMockCloudFlareClient()
|
client := NewMockCloudFlareClient()
|
||||||
provider := &CloudFlareProvider{
|
provider := &CloudFlareProvider{
|
||||||
@ -1801,7 +1829,7 @@ func TestCloudflareDNSRecordsOperationsFail(t *testing.T) {
|
|||||||
shouldFail: true,
|
shouldFail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "failing to list DNS record",
|
Name: "adding failing to list DNS record",
|
||||||
Endpoints: []*endpoint.Endpoint{
|
Endpoints: []*endpoint.Endpoint{
|
||||||
{
|
{
|
||||||
DNSName: "newerror-list-1.foo.bar.com",
|
DNSName: "newerror-list-1.foo.bar.com",
|
||||||
@ -1811,6 +1839,11 @@ func TestCloudflareDNSRecordsOperationsFail(t *testing.T) {
|
|||||||
Labels: endpoint.Labels{},
|
Labels: endpoint.Labels{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
shouldFail: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "causing to list failing to list DNS record",
|
||||||
|
Endpoints: []*endpoint.Endpoint{},
|
||||||
shouldFail: true,
|
shouldFail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -1860,28 +1893,25 @@ func TestCloudflareDNSRecordsOperationsFail(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testFailCases {
|
for _, tc := range testFailCases {
|
||||||
records, err := provider.Records(ctx)
|
var err error
|
||||||
if err != nil {
|
var records, endpoints []*endpoint.Endpoint
|
||||||
t.Errorf("should not fail, %s", err)
|
|
||||||
|
records, err = provider.Records(ctx)
|
||||||
|
if errors.Is(err, nil) {
|
||||||
|
endpoints, err = provider.AdjustEndpoints(tc.Endpoints)
|
||||||
}
|
}
|
||||||
|
if errors.Is(err, nil) {
|
||||||
endpoints, err := provider.AdjustEndpoints(tc.Endpoints)
|
plan := &plan.Plan{
|
||||||
|
Current: records,
|
||||||
assert.NoError(t, err)
|
Desired: endpoints,
|
||||||
plan := &plan.Plan{
|
DomainFilter: endpoint.MatchAllDomainFilters{&domainFilter},
|
||||||
Current: records,
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
||||||
Desired: endpoints,
|
}
|
||||||
DomainFilter: endpoint.MatchAllDomainFilters{&domainFilter},
|
planned := plan.Calculate()
|
||||||
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
err = provider.ApplyChanges(context.Background(), planned.Changes)
|
||||||
}
|
}
|
||||||
|
if e := checkFailed(tc.Name, err, tc.shouldFail); !errors.Is(e, nil) {
|
||||||
planned := plan.Calculate()
|
t.Error(e)
|
||||||
|
|
||||||
err = provider.ApplyChanges(context.Background(), planned.Changes)
|
|
||||||
if err == nil && tc.shouldFail {
|
|
||||||
t.Errorf("should fail - %q, %v", tc.Name, err)
|
|
||||||
} else if err != nil && !tc.shouldFail {
|
|
||||||
t.Errorf("should not fail - %q, %v", tc.Name, err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1896,10 +1926,9 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
|
|||||||
domainFilter := endpoint.NewDomainFilter([]string{"bar.com"})
|
domainFilter := endpoint.NewDomainFilter([]string{"bar.com"})
|
||||||
|
|
||||||
testFailCases := []struct {
|
testFailCases := []struct {
|
||||||
Name string
|
Name string
|
||||||
Endpoints []*endpoint.Endpoint
|
Endpoints []*endpoint.Endpoint
|
||||||
ExpectedCustomHostnames map[string]string
|
shouldFail bool
|
||||||
shouldFail bool
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
Name: "failing to create custom hostname on record creation",
|
Name: "failing to create custom hostname on record creation",
|
||||||
@ -1921,7 +1950,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
|
|||||||
shouldFail: true,
|
shouldFail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "custom hostname to the same origin",
|
Name: "same custom hostname to the same origin",
|
||||||
Endpoints: []*endpoint.Endpoint{
|
Endpoints: []*endpoint.Endpoint{
|
||||||
{
|
{
|
||||||
DNSName: "origin.foo.bar.com",
|
DNSName: "origin.foo.bar.com",
|
||||||
@ -1936,12 +1965,6 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
shouldFail: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "same custom hostname to the another origin",
|
|
||||||
Endpoints: []*endpoint.Endpoint{
|
|
||||||
{
|
{
|
||||||
DNSName: "another-origin.foo.bar.com",
|
DNSName: "another-origin.foo.bar.com",
|
||||||
Targets: endpoint.Targets{"3.4.5.6"},
|
Targets: endpoint.Targets{"3.4.5.6"},
|
||||||
@ -2034,6 +2057,11 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
shouldFail: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "causing to list failing to list custom hostname",
|
||||||
|
Endpoints: []*endpoint.Endpoint{},
|
||||||
shouldFail: true,
|
shouldFail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -2148,9 +2176,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
|
|||||||
Labels: endpoint.Labels{},
|
Labels: endpoint.Labels{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ExpectedCustomHostnames: map[string]string{
|
ExpectedCustomHostnames: map[string]string{},
|
||||||
"nocustomhostname.foo.bar.com": "",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "add custom hostname",
|
Name: "add custom hostname",
|
||||||
@ -2183,8 +2209,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
ExpectedCustomHostnames: map[string]string{
|
ExpectedCustomHostnames: map[string]string{
|
||||||
"a.foo.bar.com": "a.foo.fancybar.com",
|
"a.foo.fancybar.com": "a.foo.bar.com",
|
||||||
"txt.foo.bar.com": "",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -2205,13 +2230,79 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
ExpectedCustomHostnames: map[string]string{
|
ExpectedCustomHostnames: map[string]string{
|
||||||
"a.foo.bar.com": "a2.foo.fancybar.com",
|
"a2.foo.fancybar.com": "a.foo.bar.com",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "delete custom hostname",
|
Name: "add another unsorted custom hostnames",
|
||||||
|
Endpoints: []*endpoint.Endpoint{
|
||||||
|
{
|
||||||
|
DNSName: "a.foo.bar.com",
|
||||||
|
Targets: endpoint.Targets{"1.2.3.4"},
|
||||||
|
RecordType: endpoint.RecordTypeA,
|
||||||
|
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
|
||||||
|
Labels: endpoint.Labels{},
|
||||||
|
ProviderSpecific: endpoint.ProviderSpecific{
|
||||||
|
{
|
||||||
|
Name: "external-dns.alpha.kubernetes.io/cloudflare-custom-hostname",
|
||||||
|
Value: "a3.foo.fancybar.com,a4.foo.fancybar.com,a2.foo.fancybar.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedCustomHostnames: map[string]string{
|
||||||
|
"a2.foo.fancybar.com": "a.foo.bar.com",
|
||||||
|
"a3.foo.fancybar.com": "a.foo.bar.com",
|
||||||
|
"a4.foo.fancybar.com": "a.foo.bar.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "rename custom hostnames",
|
||||||
|
Endpoints: []*endpoint.Endpoint{
|
||||||
|
{
|
||||||
|
DNSName: "a.foo.bar.com",
|
||||||
|
Targets: endpoint.Targets{"1.2.3.4"},
|
||||||
|
RecordType: endpoint.RecordTypeA,
|
||||||
|
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
|
||||||
|
Labels: endpoint.Labels{},
|
||||||
|
ProviderSpecific: endpoint.ProviderSpecific{
|
||||||
|
{
|
||||||
|
Name: "external-dns.alpha.kubernetes.io/cloudflare-custom-hostname",
|
||||||
|
Value: "a3.foo.fancybar.com,a44.foo.fancybar.com,a22.foo.fancybar.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedCustomHostnames: map[string]string{
|
||||||
|
"a22.foo.fancybar.com": "a.foo.bar.com",
|
||||||
|
"a3.foo.fancybar.com": "a.foo.bar.com",
|
||||||
|
"a44.foo.fancybar.com": "a.foo.bar.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "remove some custom hostnames",
|
||||||
|
Endpoints: []*endpoint.Endpoint{
|
||||||
|
{
|
||||||
|
DNSName: "a.foo.bar.com",
|
||||||
|
Targets: endpoint.Targets{"1.2.3.4"},
|
||||||
|
RecordType: endpoint.RecordTypeA,
|
||||||
|
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
|
||||||
|
Labels: endpoint.Labels{},
|
||||||
|
ProviderSpecific: endpoint.ProviderSpecific{
|
||||||
|
{
|
||||||
|
Name: "external-dns.alpha.kubernetes.io/cloudflare-custom-hostname",
|
||||||
|
Value: "a3.foo.fancybar.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedCustomHostnames: map[string]string{
|
||||||
|
"a3.foo.fancybar.com": "a.foo.bar.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "delete custom hostnames",
|
||||||
Endpoints: []*endpoint.Endpoint{
|
Endpoints: []*endpoint.Endpoint{
|
||||||
|
|
||||||
{
|
{
|
||||||
DNSName: "a.foo.bar.com",
|
DNSName: "a.foo.bar.com",
|
||||||
Targets: endpoint.Targets{"1.2.3.4"},
|
Targets: endpoint.Targets{"1.2.3.4"},
|
||||||
@ -2220,35 +2311,31 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
|
|||||||
Labels: endpoint.Labels{},
|
Labels: endpoint.Labels{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ExpectedCustomHostnames: map[string]string{
|
ExpectedCustomHostnames: map[string]string{},
|
||||||
"a.foo.bar.com": "",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testFailCases {
|
for _, tc := range testFailCases {
|
||||||
records, err := provider.Records(ctx)
|
var err error
|
||||||
if err != nil {
|
var records, endpoints []*endpoint.Endpoint
|
||||||
t.Errorf("should not fail, %v", err)
|
|
||||||
|
records, err = provider.Records(ctx)
|
||||||
|
if errors.Is(err, nil) {
|
||||||
|
endpoints, err = provider.AdjustEndpoints(tc.Endpoints)
|
||||||
}
|
}
|
||||||
|
if errors.Is(err, nil) {
|
||||||
|
plan := &plan.Plan{
|
||||||
|
Current: records,
|
||||||
|
Desired: endpoints,
|
||||||
|
DomainFilter: endpoint.MatchAllDomainFilters{&domainFilter},
|
||||||
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME, endpoint.RecordTypeTXT},
|
||||||
|
}
|
||||||
|
planned := plan.Calculate()
|
||||||
|
err = provider.ApplyChanges(context.Background(), planned.Changes)
|
||||||
|
|
||||||
endpoints, err := provider.AdjustEndpoints(tc.Endpoints)
|
|
||||||
|
|
||||||
assert.NoError(t, err)
|
|
||||||
plan := &plan.Plan{
|
|
||||||
Current: records,
|
|
||||||
Desired: endpoints,
|
|
||||||
DomainFilter: endpoint.MatchAllDomainFilters{&domainFilter},
|
|
||||||
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME, endpoint.RecordTypeTXT},
|
|
||||||
}
|
}
|
||||||
|
if e := checkFailed(tc.Name, err, tc.shouldFail); !errors.Is(e, nil) {
|
||||||
planned := plan.Calculate()
|
t.Error(e)
|
||||||
|
|
||||||
err = provider.ApplyChanges(context.Background(), planned.Changes)
|
|
||||||
if err == nil && tc.shouldFail {
|
|
||||||
t.Errorf("should fail - %q, %v", tc.Name, err)
|
|
||||||
} else if err != nil && !tc.shouldFail {
|
|
||||||
t.Errorf("should not fail - %q, %v", tc.Name, err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2271,18 +2358,139 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
|
|||||||
planned := plan.Calculate()
|
planned := plan.Calculate()
|
||||||
|
|
||||||
err = provider.ApplyChanges(context.Background(), planned.Changes)
|
err = provider.ApplyChanges(context.Background(), planned.Changes)
|
||||||
if err != nil {
|
if e := checkFailed(tc.Name, err, false); !errors.Is(e, nil) {
|
||||||
t.Errorf("should not fail - %q, %v", tc.Name, err)
|
t.Error(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
chs, chErr := provider.listCustomHostnamesWithPagination(ctx, "001")
|
chs, chErr := provider.listCustomHostnamesWithPagination(ctx, "001")
|
||||||
if chErr != nil {
|
if e := checkFailed(tc.Name, chErr, false); !errors.Is(e, nil) {
|
||||||
t.Errorf("should not fail - %q, %v", tc.Name, chErr)
|
t.Error(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
for expectedOrigin, expectedCustomHostname := range tc.ExpectedCustomHostnames {
|
actualCustomHostnames := map[string]string{}
|
||||||
_, ch := getCustomHostnameIDbyCustomHostnameAndOrigin(chs, expectedCustomHostname, expectedOrigin)
|
for _, ch := range chs {
|
||||||
assert.Equal(t, expectedCustomHostname, ch)
|
actualCustomHostnames[ch.Hostname] = ch.CustomOriginServer
|
||||||
|
}
|
||||||
|
assert.Equal(t, tc.ExpectedCustomHostnames, actualCustomHostnames, "custom hostnames should be the same")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloudflareDisabledCustomHostnameOperations(t *testing.T) {
|
||||||
|
client := NewMockCloudFlareClient()
|
||||||
|
provider := &CloudFlareProvider{
|
||||||
|
Client: client,
|
||||||
|
CustomHostnamesConfig: CustomHostnamesConfig{Enabled: false},
|
||||||
|
}
|
||||||
|
ctx := context.Background()
|
||||||
|
domainFilter := endpoint.NewDomainFilter([]string{"bar.com"})
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
Name string
|
||||||
|
Endpoints []*endpoint.Endpoint
|
||||||
|
testChanges bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Name: "add custom hostname",
|
||||||
|
Endpoints: []*endpoint.Endpoint{
|
||||||
|
{
|
||||||
|
DNSName: "a.foo.bar.com",
|
||||||
|
Targets: endpoint.Targets{"1.2.3.11"},
|
||||||
|
RecordType: endpoint.RecordTypeA,
|
||||||
|
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
|
||||||
|
Labels: endpoint.Labels{},
|
||||||
|
ProviderSpecific: endpoint.ProviderSpecific{
|
||||||
|
{
|
||||||
|
Name: "external-dns.alpha.kubernetes.io/cloudflare-custom-hostname",
|
||||||
|
Value: "a.foo.fancybar.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DNSName: "b.foo.bar.com",
|
||||||
|
Targets: endpoint.Targets{"1.2.3.12"},
|
||||||
|
RecordType: endpoint.RecordTypeA,
|
||||||
|
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
|
||||||
|
Labels: endpoint.Labels{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DNSName: "c.foo.bar.com",
|
||||||
|
Targets: endpoint.Targets{"1.2.3.13"},
|
||||||
|
RecordType: endpoint.RecordTypeA,
|
||||||
|
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
|
||||||
|
Labels: endpoint.Labels{},
|
||||||
|
ProviderSpecific: endpoint.ProviderSpecific{
|
||||||
|
{
|
||||||
|
Name: "external-dns.alpha.kubernetes.io/cloudflare-custom-hostname",
|
||||||
|
Value: "c1.foo.fancybar.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testChanges: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "add custom hostname",
|
||||||
|
Endpoints: []*endpoint.Endpoint{
|
||||||
|
{
|
||||||
|
DNSName: "a.foo.bar.com",
|
||||||
|
Targets: endpoint.Targets{"1.2.3.11"},
|
||||||
|
RecordType: endpoint.RecordTypeA,
|
||||||
|
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
|
||||||
|
Labels: endpoint.Labels{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DNSName: "b.foo.bar.com",
|
||||||
|
Targets: endpoint.Targets{"1.2.3.12"},
|
||||||
|
RecordType: endpoint.RecordTypeA,
|
||||||
|
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
|
||||||
|
Labels: endpoint.Labels{},
|
||||||
|
ProviderSpecific: endpoint.ProviderSpecific{
|
||||||
|
{
|
||||||
|
Name: "external-dns.alpha.kubernetes.io/cloudflare-custom-hostname",
|
||||||
|
Value: "b.foo.fancybar.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DNSName: "c.foo.bar.com",
|
||||||
|
Targets: endpoint.Targets{"1.2.3.13"},
|
||||||
|
RecordType: endpoint.RecordTypeA,
|
||||||
|
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
|
||||||
|
Labels: endpoint.Labels{},
|
||||||
|
ProviderSpecific: endpoint.ProviderSpecific{
|
||||||
|
{
|
||||||
|
Name: "external-dns.alpha.kubernetes.io/cloudflare-custom-hostname",
|
||||||
|
Value: "c2.foo.fancybar.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testChanges: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
records, err := provider.Records(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("should not fail, %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoints, err := provider.AdjustEndpoints(tc.Endpoints)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
plan := &plan.Plan{
|
||||||
|
Current: records,
|
||||||
|
Desired: endpoints,
|
||||||
|
DomainFilter: endpoint.MatchAllDomainFilters{&domainFilter},
|
||||||
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
||||||
|
}
|
||||||
|
planned := plan.Calculate()
|
||||||
|
err = provider.ApplyChanges(context.Background(), planned.Changes)
|
||||||
|
if e := checkFailed(tc.Name, err, false); !errors.Is(e, nil) {
|
||||||
|
t.Error(e)
|
||||||
|
}
|
||||||
|
if tc.testChanges {
|
||||||
|
assert.Equal(t, planned.Changes.HasChanges(), false, "no new changes should be here")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2381,11 +2589,11 @@ func TestCloudflareCustomHostnameNotFoundOnRecordDeletion(t *testing.T) {
|
|||||||
// manually corrupt custom hostname before the deletion step
|
// manually corrupt custom hostname before the deletion step
|
||||||
// the purpose is to cause getCustomHostnameOrigin() to fail on change.Action == cloudFlareDelete
|
// the purpose is to cause getCustomHostnameOrigin() to fail on change.Action == cloudFlareDelete
|
||||||
chs, chErr := provider.listCustomHostnamesWithPagination(ctx, zoneID)
|
chs, chErr := provider.listCustomHostnamesWithPagination(ctx, zoneID)
|
||||||
if chErr != nil {
|
if e := checkFailed(tc.Name, chErr, false); !errors.Is(e, nil) {
|
||||||
t.Errorf("should not fail - %q, %v", tc.Name, chErr)
|
t.Error(e)
|
||||||
}
|
}
|
||||||
if tc.preApplyHook == "corrupt" {
|
if tc.preApplyHook == "corrupt" {
|
||||||
if ch, err := getCustomHostname(chs, "newerror-getCustomHostnameOrigin.foo.fancybar.com"); err == nil {
|
if ch, err := getCustomHostname(chs, "newerror-getCustomHostnameOrigin.foo.fancybar.com"); errors.Is(err, nil) {
|
||||||
chID := ch.ID
|
chID := ch.ID
|
||||||
t.Logf("corrupting custom hostname %q", chID)
|
t.Logf("corrupting custom hostname %q", chID)
|
||||||
oldIdx := getCustomHostnameIdxByID(client.customHostnames[zoneID], chID)
|
oldIdx := getCustomHostnameIdxByID(client.customHostnames[zoneID], chID)
|
||||||
@ -2398,7 +2606,6 @@ func TestCloudflareCustomHostnameNotFoundOnRecordDeletion(t *testing.T) {
|
|||||||
client.customHostnames[zoneID][oldIdx] = ch
|
client.customHostnames[zoneID][oldIdx] = ch
|
||||||
}
|
}
|
||||||
} else if tc.preApplyHook == "duplicate" { // manually inject duplicating custom hostname with the same name and origin
|
} else if tc.preApplyHook == "duplicate" { // manually inject duplicating custom hostname with the same name and origin
|
||||||
|
|
||||||
ch := cloudflare.CustomHostname{
|
ch := cloudflare.CustomHostname{
|
||||||
ID: "ID-random-123",
|
ID: "ID-random-123",
|
||||||
Hostname: "a.foo.fancybar.com",
|
Hostname: "a.foo.fancybar.com",
|
||||||
@ -2407,8 +2614,8 @@ func TestCloudflareCustomHostnameNotFoundOnRecordDeletion(t *testing.T) {
|
|||||||
client.customHostnames[zoneID] = append(client.customHostnames[zoneID], ch)
|
client.customHostnames[zoneID] = append(client.customHostnames[zoneID], ch)
|
||||||
}
|
}
|
||||||
err = provider.ApplyChanges(context.Background(), planned.Changes)
|
err = provider.ApplyChanges(context.Background(), planned.Changes)
|
||||||
if err != nil {
|
if e := checkFailed(tc.Name, err, false); !errors.Is(e, nil) {
|
||||||
t.Errorf("should not fail - %q, %v", tc.Name, err)
|
t.Error(e)
|
||||||
}
|
}
|
||||||
assert.Contains(t, b.String(), tc.logOutput)
|
assert.Contains(t, b.String(), tc.logOutput)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user