Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ require (
github.com/cenkalti/backoff/v5 v5.0.2
github.com/civo/civogo v0.6.1
github.com/cloudflare/cloudflare-go v0.115.0
github.com/cloudflare/cloudflare-go/v4 v4.5.1
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
Expand Down Expand Up @@ -162,6 +163,10 @@ require (
github.com/sosodev/duration v1.3.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/tidwall/gjson v1.14.4 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
github.com/vektah/gqlparser/v2 v2.5.26 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
Expand Down
12 changes: 12 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/cloudflare-go v0.115.0 h1:84/dxeeXweCc0PN5Cto44iTA8AkG1fyT11yPO5ZB7sM=
github.com/cloudflare/cloudflare-go v0.115.0/go.mod h1:Ds6urDwn/TF2uIU24mu7H91xkKP8gSAHxQ44DSZgVmU=
github.com/cloudflare/cloudflare-go/v4 v4.5.1 h1:ZQgQ7QO+M9rK0KYx1CmppuG15ZTYGHn8F9/Fh7mCuQQ=
github.com/cloudflare/cloudflare-go/v4 v4.5.1/go.mod h1:XcYpLe7Mf6FN87kXzEWVnJ6z+vskW/k6eUqgqfhFE9k=
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381 h1:rdRS5BT13Iae9ssvcslol66gfOOXjaLYwqerEn/cl9s=
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381/go.mod h1:e5+USP2j8Le2M0Jo3qKPFnNhuo1wueU4nWHCXBOfQ14=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
Expand Down Expand Up @@ -990,7 +992,17 @@ 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/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/transip/gotransip/v6 v6.26.0 h1:Aejfvh8rSp8Mj2GX/RpdBjMCv+Iy/DmgfNgczPDP550=
Expand Down
32 changes: 22 additions & 10 deletions provider/cloudflare/cloudflare.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ import (
"strings"

"github.com/cloudflare/cloudflare-go"
cloudflarev4 "github.com/cloudflare/cloudflare-go/v4"
"github.com/cloudflare/cloudflare-go/v4/addressing"
"github.com/cloudflare/cloudflare-go/v4/option"
log "github.com/sirupsen/logrus"
"golang.org/x/net/publicsuffix"

Expand Down Expand Up @@ -109,17 +112,18 @@ type cloudFlareDNS interface {
CreateDNSRecord(ctx context.Context, rc *cloudflare.ResourceContainer, rp cloudflare.CreateDNSRecordParams) (cloudflare.DNSRecord, error)
DeleteDNSRecord(ctx context.Context, rc *cloudflare.ResourceContainer, recordID string) error
UpdateDNSRecord(ctx context.Context, rc *cloudflare.ResourceContainer, rp cloudflare.UpdateDNSRecordParams) error
ListDataLocalizationRegionalHostnames(ctx context.Context, rc *cloudflare.ResourceContainer, rp cloudflare.ListDataLocalizationRegionalHostnamesParams) ([]cloudflare.RegionalHostname, error)
CreateDataLocalizationRegionalHostname(ctx context.Context, rc *cloudflare.ResourceContainer, rp cloudflare.CreateDataLocalizationRegionalHostnameParams) error
UpdateDataLocalizationRegionalHostname(ctx context.Context, rc *cloudflare.ResourceContainer, rp cloudflare.UpdateDataLocalizationRegionalHostnameParams) error
DeleteDataLocalizationRegionalHostname(ctx context.Context, rc *cloudflare.ResourceContainer, hostname string) error
ListDataLocalizationRegionalHostnames(ctx context.Context, params addressing.RegionalHostnameListParams) autoPager[addressing.RegionalHostnameListResponse]
CreateDataLocalizationRegionalHostname(ctx context.Context, params addressing.RegionalHostnameNewParams) error
UpdateDataLocalizationRegionalHostname(ctx context.Context, hostname string, params addressing.RegionalHostnameEditParams) error
DeleteDataLocalizationRegionalHostname(ctx context.Context, hostname string, params addressing.RegionalHostnameDeleteParams) error
CustomHostnames(ctx context.Context, zoneID string, page int, filter cloudflare.CustomHostname) ([]cloudflare.CustomHostname, cloudflare.ResultInfo, error)
DeleteCustomHostname(ctx context.Context, zoneID string, customHostnameID string) error
CreateCustomHostname(ctx context.Context, zoneID string, ch cloudflare.CustomHostname) (*cloudflare.CustomHostnameResponse, error)
}

type zoneService struct {
service *cloudflare.API
service *cloudflare.API
serviceV4 *cloudflarev4.Client
}

func (z zoneService) ZoneIDByName(zoneName string) (string, error) {
Expand Down Expand Up @@ -287,8 +291,9 @@ func NewCloudFlareProvider(
) (*CloudFlareProvider, error) {
// initialize via chosen auth method and returns new API object
var (
config *cloudflare.API
err error
config *cloudflare.API
configV4 *cloudflarev4.Client
err error
)
if os.Getenv("CF_API_TOKEN") != "" {
token := os.Getenv("CF_API_TOKEN")
Expand All @@ -300,8 +305,15 @@ func NewCloudFlareProvider(
token = strings.TrimSpace(string(tokenBytes))
}
config, err = cloudflare.NewWithAPIToken(token)
configV4 = cloudflarev4.NewClient(
option.WithAPIToken(token),
)
} else {
config, err = cloudflare.New(os.Getenv("CF_API_KEY"), os.Getenv("CF_API_EMAIL"))
configV4 = cloudflarev4.NewClient(
option.WithAPIKey(os.Getenv("CF_API_KEY")),
option.WithAPIEmail(os.Getenv("CF_API_EMAIL")),
)
}
if err != nil {
return nil, fmt.Errorf("failed to initialize cloudflare provider: %w", err)
Expand All @@ -312,7 +324,7 @@ func NewCloudFlareProvider(
}

return &CloudFlareProvider{
Client: zoneService{config},
Client: zoneService{config, configV4},
domainFilter: domainFilter,
zoneIDFilter: zoneIDFilter,
proxiedByDefault: proxiedByDefault,
Expand Down Expand Up @@ -646,12 +658,12 @@ func (p *CloudFlareProvider) submitChanges(ctx context.Context, changes []*cloud
return fmt.Errorf("failed to build desired regional hostnames: %w", err)
}
if len(desiredRegionalHostnames) > 0 {
regionalHostnames, err := p.listDataLocalisationRegionalHostnames(ctx, resourceContainer)
regionalHostnames, err := p.listDataLocalisationRegionalHostnames(ctx, zoneID)
if err != nil {
return fmt.Errorf("could not fetch regional hostnames from zone, %w", err)
}
regionalHostnamesChanges := regionalHostnamesChanges(desiredRegionalHostnames, regionalHostnames)
if !p.submitRegionalHostnameChanges(ctx, regionalHostnamesChanges, resourceContainer) {
if !p.submitRegionalHostnameChanges(ctx, zoneID, regionalHostnamesChanges) {
failedChange = true
}
}
Expand Down
89 changes: 56 additions & 33 deletions provider/cloudflare/cloudflare_regional.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ import (
"maps"
"slices"

cloudflare "github.com/cloudflare/cloudflare-go"
cloudflarev4 "github.com/cloudflare/cloudflare-go/v4"
"github.com/cloudflare/cloudflare-go/v4/addressing"

log "github.com/sirupsen/logrus"

"sigs.k8s.io/external-dns/endpoint"
Expand Down Expand Up @@ -53,46 +55,62 @@ type regionalHostnameChange struct {
regionalHostname
}

func (z zoneService) ListDataLocalizationRegionalHostnames(ctx context.Context, rc *cloudflare.ResourceContainer, rp cloudflare.ListDataLocalizationRegionalHostnamesParams) ([]cloudflare.RegionalHostname, error) {
return z.service.ListDataLocalizationRegionalHostnames(ctx, rc, rp)
func (z zoneService) ListDataLocalizationRegionalHostnames(ctx context.Context, params addressing.RegionalHostnameListParams) autoPager[addressing.RegionalHostnameListResponse] {
return z.serviceV4.Addressing.RegionalHostnames.ListAutoPaging(ctx, params)
}

func (z zoneService) CreateDataLocalizationRegionalHostname(ctx context.Context, params addressing.RegionalHostnameNewParams) error {
_, err := z.serviceV4.Addressing.RegionalHostnames.New(ctx, params)
return err
}

func (z zoneService) CreateDataLocalizationRegionalHostname(ctx context.Context, rc *cloudflare.ResourceContainer, rp cloudflare.CreateDataLocalizationRegionalHostnameParams) error {
_, err := z.service.CreateDataLocalizationRegionalHostname(ctx, rc, rp)
func (z zoneService) UpdateDataLocalizationRegionalHostname(ctx context.Context, hostname string, params addressing.RegionalHostnameEditParams) error {
_, err := z.serviceV4.Addressing.RegionalHostnames.Edit(ctx, hostname, params)
return err
}

func (z zoneService) UpdateDataLocalizationRegionalHostname(ctx context.Context, rc *cloudflare.ResourceContainer, rp cloudflare.UpdateDataLocalizationRegionalHostnameParams) error {
_, err := z.service.UpdateDataLocalizationRegionalHostname(ctx, rc, rp)
func (z zoneService) DeleteDataLocalizationRegionalHostname(ctx context.Context, hostname string, params addressing.RegionalHostnameDeleteParams) error {
_, err := z.serviceV4.Addressing.RegionalHostnames.Delete(ctx, hostname, params)
return err
}

func (z zoneService) DeleteDataLocalizationRegionalHostname(ctx context.Context, rc *cloudflare.ResourceContainer, hostname string) error {
return z.service.DeleteDataLocalizationRegionalHostname(ctx, rc, hostname)
// listDataLocalizationRegionalHostnamesParams is a function that returns the appropriate RegionalHostname List Param based on the zoneID
func listDataLocalizationRegionalHostnamesParams(zoneID string) addressing.RegionalHostnameListParams {
return addressing.RegionalHostnameListParams{
ZoneID: cloudflarev4.F(zoneID),
}
}

// createDataLocalizationRegionalHostnameParams is a function that returns the appropriate RegionalHostname Param based on the cloudFlareChange passed in
func createDataLocalizationRegionalHostnameParams(rhc regionalHostnameChange) cloudflare.CreateDataLocalizationRegionalHostnameParams {
return cloudflare.CreateDataLocalizationRegionalHostnameParams{
Hostname: rhc.hostname,
RegionKey: rhc.regionKey,
func createDataLocalizationRegionalHostnameParams(zoneID string, rhc regionalHostnameChange) addressing.RegionalHostnameNewParams {
return addressing.RegionalHostnameNewParams{
ZoneID: cloudflarev4.F(zoneID),
Hostname: cloudflarev4.F(rhc.hostname),
RegionKey: cloudflarev4.F(rhc.regionKey),
}
}

// updateDataLocalizationRegionalHostnameParams is a function that returns the appropriate RegionalHostname Param based on the cloudFlareChange passed in
func updateDataLocalizationRegionalHostnameParams(rhc regionalHostnameChange) cloudflare.UpdateDataLocalizationRegionalHostnameParams {
return cloudflare.UpdateDataLocalizationRegionalHostnameParams{
Hostname: rhc.hostname,
RegionKey: rhc.regionKey,
func updateDataLocalizationRegionalHostnameParams(zoneID string, rhc regionalHostnameChange) addressing.RegionalHostnameEditParams {
return addressing.RegionalHostnameEditParams{
ZoneID: cloudflarev4.F(zoneID),
RegionKey: cloudflarev4.F(rhc.regionKey),
}
}

// deleteDataLocalizationRegionalHostnameParams is a function that returns the appropriate RegionalHostname Param based on the cloudFlareChange passed in
func deleteDataLocalizationRegionalHostnameParams(zoneID string, rhc regionalHostnameChange) addressing.RegionalHostnameDeleteParams {
return addressing.RegionalHostnameDeleteParams{
ZoneID: cloudflarev4.F(zoneID),
}
}

// submitRegionalHostnameChanges applies a set of regional hostname changes, returns false if at least one fails
func (p *CloudFlareProvider) submitRegionalHostnameChanges(ctx context.Context, rhChanges []regionalHostnameChange, resourceContainer *cloudflare.ResourceContainer) bool {
func (p *CloudFlareProvider) submitRegionalHostnameChanges(ctx context.Context, zoneID string, rhChanges []regionalHostnameChange) bool {
failedChange := false

for _, rhChange := range rhChanges {
if !p.submitRegionalHostnameChange(ctx, rhChange, resourceContainer) {
if !p.submitRegionalHostnameChange(ctx, zoneID, rhChange) {
failedChange = true
}
}
Expand All @@ -101,12 +119,12 @@ func (p *CloudFlareProvider) submitRegionalHostnameChanges(ctx context.Context,
}

// submitRegionalHostnameChange applies a single regional hostname change, returns false if it fails
func (p *CloudFlareProvider) submitRegionalHostnameChange(ctx context.Context, rhChange regionalHostnameChange, resourceContainer *cloudflare.ResourceContainer) bool {
func (p *CloudFlareProvider) submitRegionalHostnameChange(ctx context.Context, zoneID string, rhChange regionalHostnameChange) bool {
changeLog := log.WithFields(log.Fields{
"hostname": rhChange.hostname,
"region_key": rhChange.regionKey,
"action": rhChange.action.String(),
"zone": resourceContainer.Identifier,
"zone": zoneID,
})
if p.DryRun {
changeLog.Debug("Dry run: skipping regional hostname change", rhChange.action)
Expand All @@ -115,40 +133,45 @@ func (p *CloudFlareProvider) submitRegionalHostnameChange(ctx context.Context, r
switch rhChange.action {
case cloudFlareCreate:
changeLog.Debug("Creating regional hostname")
regionalHostnameParam := createDataLocalizationRegionalHostnameParams(rhChange)
if err := p.Client.CreateDataLocalizationRegionalHostname(ctx, resourceContainer, regionalHostnameParam); err != nil {
params := createDataLocalizationRegionalHostnameParams(zoneID, rhChange)
if err := p.Client.CreateDataLocalizationRegionalHostname(ctx, params); err != nil {
changeLog.Errorf("failed to create regional hostname: %v", err)
return false
}
case cloudFlareUpdate:
changeLog.Debug("Updating regional hostname")
regionalHostnameParam := updateDataLocalizationRegionalHostnameParams(rhChange)
if err := p.Client.UpdateDataLocalizationRegionalHostname(ctx, resourceContainer, regionalHostnameParam); err != nil {
params := updateDataLocalizationRegionalHostnameParams(zoneID, rhChange)
if err := p.Client.UpdateDataLocalizationRegionalHostname(ctx, rhChange.hostname, params); err != nil {
changeLog.Errorf("failed to update regional hostname: %v", err)
return false
}
case cloudFlareDelete:
changeLog.Debug("Deleting regional hostname")
if err := p.Client.DeleteDataLocalizationRegionalHostname(ctx, resourceContainer, rhChange.hostname); err != nil {
params := deleteDataLocalizationRegionalHostnameParams(zoneID, rhChange)
if err := p.Client.DeleteDataLocalizationRegionalHostname(ctx, rhChange.hostname, params); err != nil {
changeLog.Errorf("failed to delete regional hostname: %v", err)
return false
}
}
return true
}

func (p *CloudFlareProvider) listDataLocalisationRegionalHostnames(ctx context.Context, resourceContainer *cloudflare.ResourceContainer) (regionalHostnamesMap, error) {
rhs, err := p.Client.ListDataLocalizationRegionalHostnames(ctx, resourceContainer, cloudflare.ListDataLocalizationRegionalHostnamesParams{})
if err != nil {
return nil, convertCloudflareError(err)
}
// listDataLocalisationRegionalHostnames fetches the current regional hostnames for the given zone ID.
//
// It returns a map of hostnames to regional hostnames, or an error if the request fails.
func (p *CloudFlareProvider) listDataLocalisationRegionalHostnames(ctx context.Context, zoneID string) (regionalHostnamesMap, error) {
params := listDataLocalizationRegionalHostnamesParams(zoneID)
iter := p.Client.ListDataLocalizationRegionalHostnames(ctx, params)
rhsMap := make(regionalHostnamesMap)
for _, rh := range rhs {
for rh := range autoPagerIterator(iter) {
rhsMap[rh.Hostname] = regionalHostname{
hostname: rh.Hostname,
regionKey: rh.RegionKey,
}
}
if iter.Err() != nil {
return nil, convertCloudflareError(iter.Err())
}
return rhsMap, nil
}

Expand Down Expand Up @@ -193,7 +216,7 @@ func (p *CloudFlareProvider) addEnpointsProviderSpecificRegionKeyProperty(ctx co
return nil
}

regionalHostnames, err := p.listDataLocalisationRegionalHostnames(ctx, cloudflare.ZoneIdentifier(zoneID))
regionalHostnames, err := p.listDataLocalisationRegionalHostnames(ctx, zoneID)
if err != nil {
return err
}
Expand Down
Loading
Loading