Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>
This commit is contained in:
ivan katliarchuk 2024-09-14 10:03:49 +01:00
parent 2b14345083
commit 3830fb29c6
No known key found for this signature in database
GPG Key ID: 601CDBBBB76E47BE
2 changed files with 587 additions and 704 deletions

View File

@ -20,16 +20,14 @@ import (
"context"
"errors"
"fmt"
"regexp"
"sort"
"strconv"
"strings"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/service/route53"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/route53"
route53types "github.com/aws/aws-sdk-go-v2/service/route53/types"
log "github.com/sirupsen/logrus"
"sigs.k8s.io/external-dns/endpoint"
@ -42,11 +40,11 @@ const (
recordTTL = 300
// From the experiments, it seems that the default MaxItems applied is 100,
// and that, on the server side, there is a hard limit of 300 elements per page.
// After a discussion with AWS representants, clients should accept
// when less items are returned, and still paginate accordingly.
// After a discussion with AWS representatives, clients should accept
// when fewer items are returned, and still paginate accordingly.
// As we are using the standard AWS client, this should already be compliant.
// Hence, ifever AWS decides to raise this limit, we will automatically reduce the pressure on rate limits
route53PageSize = "300"
// Hence, if AWS ever decides to raise this limit, we will automatically reduce the pressure on rate limits
route53PageSize int32 = 300
// providerSpecificAlias specifies whether a CNAME endpoint maps to an AWS ALIAS record.
providerSpecificAlias = "alias"
providerSpecificTargetHostedZone = "aws/target-hosted-zone"
@ -200,16 +198,16 @@ var canonicalHostedZones = map[string]string{
// Route53API is the subset of the AWS Route53 API that we actually use. Add methods as required. Signatures must match exactly.
// mostly taken from: https://github.com/kubernetes/kubernetes/blob/853167624edb6bc0cfdcdfb88e746e178f5db36c/federation/pkg/dnsprovider/providers/aws/route53/stubs/route53api.go
type Route53API interface {
ListResourceRecordSetsPagesWithContext(ctx context.Context, input *route53.ListResourceRecordSetsInput, fn func(resp *route53.ListResourceRecordSetsOutput, lastPage bool) (shouldContinue bool), opts ...request.Option) error
ChangeResourceRecordSetsWithContext(ctx context.Context, input *route53.ChangeResourceRecordSetsInput, opts ...request.Option) (*route53.ChangeResourceRecordSetsOutput, error)
CreateHostedZoneWithContext(ctx context.Context, input *route53.CreateHostedZoneInput, opts ...request.Option) (*route53.CreateHostedZoneOutput, error)
ListHostedZonesPagesWithContext(ctx context.Context, input *route53.ListHostedZonesInput, fn func(resp *route53.ListHostedZonesOutput, lastPage bool) (shouldContinue bool), opts ...request.Option) error
ListTagsForResourceWithContext(ctx context.Context, input *route53.ListTagsForResourceInput, opts ...request.Option) (*route53.ListTagsForResourceOutput, error)
ListResourceRecordSets(ctx context.Context, input *route53.ListResourceRecordSetsInput, optFns ...func(options *route53.Options)) (*route53.ListResourceRecordSetsOutput, error)
ChangeResourceRecordSets(ctx context.Context, input *route53.ChangeResourceRecordSetsInput, optFns ...func(options *route53.Options)) (*route53.ChangeResourceRecordSetsOutput, error)
CreateHostedZone(ctx context.Context, input *route53.CreateHostedZoneInput, optFns ...func(*route53.Options)) (*route53.CreateHostedZoneOutput, error)
ListHostedZones(ctx context.Context, input *route53.ListHostedZonesInput, optFns ...func(options *route53.Options)) (*route53.ListHostedZonesOutput, error)
ListTagsForResource(ctx context.Context, input *route53.ListTagsForResourceInput, optFns ...func(options *route53.Options)) (*route53.ListTagsForResourceOutput, error)
}
// wrapper to handle ownership relation throughout the provider implementation
type Route53Change struct {
route53.Change
route53types.Change
OwnedRecord string
sizeBytes int
sizeValues int
@ -219,13 +217,13 @@ type Route53Changes []*Route53Change
type profiledZone struct {
profile string
zone *route53.HostedZone
zone *route53types.HostedZone
}
func (cs Route53Changes) Route53Changes() []*route53.Change {
ret := []*route53.Change{}
func (cs Route53Changes) Route53Changes() []route53types.Change {
ret := []route53types.Change{}
for _, c := range cs {
ret = append(ret, &c.Change)
ret = append(ret, c.Change)
}
return ret
}
@ -254,7 +252,7 @@ type AWSProvider struct {
zoneTypeFilter provider.ZoneTypeFilter
// filter hosted zones by tags
zoneTagFilter provider.ZoneTagFilter
// extend filter for sub-domains in the zone (e.g. first.us-east-1.example.com)
// extend filter for subdomains in the zone (e.g. first.us-east-1.example.com)
zoneMatchParent bool
preferCNAME bool
zonesCache *zonesListCache
@ -303,13 +301,13 @@ func NewAWSProvider(awsConfig AWSConfig, clients map[string]Route53API) (*AWSPro
}
// Zones returns the list of hosted zones.
func (p *AWSProvider) Zones(ctx context.Context) (map[string]*route53.HostedZone, error) {
func (p *AWSProvider) Zones(ctx context.Context) (map[string]*route53types.HostedZone, error) {
zones, err := p.zones(ctx)
if err != nil {
return nil, err
}
result := make(map[string]*route53.HostedZone, len(zones))
result := make(map[string]*route53types.HostedZone, len(zones))
for id, zone := range zones {
result[id] = zone.zone
}
@ -325,61 +323,57 @@ func (p *AWSProvider) zones(ctx context.Context) (map[string]*profiledZone, erro
log.Debug("Refreshing zones list cache")
zones := make(map[string]*profiledZone)
var profile string
var tagErr error
f := func(resp *route53.ListHostedZonesOutput, lastPage bool) (shouldContinue bool) {
for _, zone := range resp.HostedZones {
if !p.zoneIDFilter.Match(aws.StringValue(zone.Id)) {
continue
}
if !p.zoneTypeFilter.Match(zone) {
continue
}
for profile, client := range p.clients {
var tagErr error
paginator := route53.NewListHostedZonesPaginator(client, &route53.ListHostedZonesInput{})
if !p.domainFilter.Match(aws.StringValue(zone.Name)) {
if !p.zoneMatchParent {
continue
}
if !p.domainFilter.MatchParent(aws.StringValue(zone.Name)) {
continue
}
}
// Only fetch tags if a tag filter was specified
if !p.zoneTagFilter.IsEmpty() {
tags, err := p.tagsForZone(ctx, *zone.Id, profile)
if err != nil {
tagErr = err
return false
}
if !p.zoneTagFilter.Match(tags) {
continue
}
}
zones[aws.StringValue(zone.Id)] = &profiledZone{
profile: profile,
zone: zone,
}
}
return true
}
for p, client := range p.clients {
profile = p
err := client.ListHostedZonesPagesWithContext(ctx, &route53.ListHostedZonesInput{}, f)
if err != nil {
var awsErr awserr.Error
if errors.As(err, &awsErr) {
if awsErr.Code() == route53.ErrCodeThrottlingException {
log.Warnf("Skipping AWS profile %q due to provider side throttling: %v", profile, awsErr.Message())
for paginator.HasMorePages() {
resp, err := paginator.NextPage(ctx)
if err != nil {
var te *route53types.ThrottlingException
if errors.As(err, &te) {
log.Infof("Skipping AWS profile %q due to provider side throttling: %v", profile, te.ErrorMessage())
continue
}
// nothing to do here. Falling through to general error handling
return nil, provider.NewSoftError(fmt.Errorf("failed to list hosted zones: %w", err))
}
for _, zone := range resp.HostedZones {
if !p.zoneIDFilter.Match(*zone.Id) {
continue
}
if !p.zoneTypeFilter.Match(zone) {
continue
}
if !p.domainFilter.Match(*zone.Name) {
if !p.zoneMatchParent {
continue
}
if !p.domainFilter.MatchParent(*zone.Name) {
continue
}
}
// Only fetch tags if a tag filter was specified
if !p.zoneTagFilter.IsEmpty() {
tags, err := p.tagsForZone(ctx, *zone.Id, profile)
if err != nil {
tagErr = err
break
}
if !p.zoneTagFilter.Match(tags) {
continue
}
}
zones[*zone.Id] = &profiledZone{
profile: profile,
zone: &zone,
}
}
return nil, provider.NewSoftError(fmt.Errorf("failed to list hosted zones: %w", err))
}
if tagErr != nil {
return nil, provider.NewSoftError(fmt.Errorf("failed to list zones tags: %w", tagErr))
@ -387,7 +381,7 @@ func (p *AWSProvider) zones(ctx context.Context) (map[string]*profiledZone, erro
}
for _, zone := range zones {
log.Debugf("Considering zone: %s (domain: %s)", aws.StringValue(zone.zone.Id), aws.StringValue(zone.zone.Name))
log.Debugf("Considering zone: %s (domain: %s)", *zone.zone.Id, *zone.zone.Name)
}
if p.zonesCache.duration > time.Duration(0) {
@ -404,28 +398,6 @@ func wildcardUnescape(s string) string {
return strings.Replace(s, "\\052", "*", 1)
}
// See https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DomainNameFormat.html
// convertOctalToAscii decodes inputs that contain octal escape sequences into their original ASCII characters.
// The function returns converted string where any octal escape sequences have been replaced with their corresponding ASCII characters.
func convertOctalToAscii(input string) string {
if !containsOctalSequence(input) {
return input
}
result, err := strconv.Unquote("\"" + input + "\"")
if err != nil {
return input
}
return result
}
// validateDomainName checks if the domain name contains valid octal escape sequences.
func containsOctalSequence(domain string) bool {
// Pattern to match valid octal escape sequences
octalEscapePattern := `\\[0-3][0-7]{2}`
octalEscapeRegex := regexp.MustCompile(octalEscapePattern)
return octalEscapeRegex.MatchString(domain)
}
// Records returns the list of records in a given hosted zone.
func (p *AWSProvider) Records(ctx context.Context) (endpoints []*endpoint.Endpoint, _ error) {
zones, err := p.zones(ctx)
@ -438,93 +410,93 @@ func (p *AWSProvider) Records(ctx context.Context) (endpoints []*endpoint.Endpoi
func (p *AWSProvider) records(ctx context.Context, zones map[string]*profiledZone) ([]*endpoint.Endpoint, error) {
endpoints := make([]*endpoint.Endpoint, 0)
f := func(resp *route53.ListResourceRecordSetsOutput, lastPage bool) (shouldContinue bool) {
for _, r := range resp.ResourceRecordSets {
newEndpoints := make([]*endpoint.Endpoint, 0)
if !p.SupportedRecordType(aws.StringValue(r.Type)) {
continue
}
name := convertOctalToAscii(wildcardUnescape(aws.StringValue(r.Name)))
var ttl endpoint.TTL
if r.TTL != nil {
ttl = endpoint.TTL(*r.TTL)
}
if len(r.ResourceRecords) > 0 {
targets := make([]string, len(r.ResourceRecords))
for idx, rr := range r.ResourceRecords {
targets[idx] = aws.StringValue(rr.Value)
}
ep := endpoint.NewEndpointWithTTL(name, aws.StringValue(r.Type), ttl, targets...)
if aws.StringValue(r.Type) == endpoint.RecordTypeCNAME {
ep = ep.WithProviderSpecific(providerSpecificAlias, "false")
}
newEndpoints = append(newEndpoints, ep)
}
if r.AliasTarget != nil {
// Alias records don't have TTLs so provide the default to match the TXT generation
if ttl == 0 {
ttl = recordTTL
}
ep := endpoint.
NewEndpointWithTTL(name, endpoint.RecordTypeA, ttl, aws.StringValue(r.AliasTarget.DNSName)).
WithProviderSpecific(providerSpecificEvaluateTargetHealth, fmt.Sprintf("%t", aws.BoolValue(r.AliasTarget.EvaluateTargetHealth))).
WithProviderSpecific(providerSpecificAlias, "true")
newEndpoints = append(newEndpoints, ep)
}
for _, ep := range newEndpoints {
if r.SetIdentifier != nil {
ep.SetIdentifier = aws.StringValue(r.SetIdentifier)
switch {
case r.Weight != nil:
ep.WithProviderSpecific(providerSpecificWeight, fmt.Sprintf("%d", aws.Int64Value(r.Weight)))
case r.Region != nil:
ep.WithProviderSpecific(providerSpecificRegion, aws.StringValue(r.Region))
case r.Failover != nil:
ep.WithProviderSpecific(providerSpecificFailover, aws.StringValue(r.Failover))
case r.MultiValueAnswer != nil && aws.BoolValue(r.MultiValueAnswer):
ep.WithProviderSpecific(providerSpecificMultiValueAnswer, "")
case r.GeoLocation != nil:
if r.GeoLocation.ContinentCode != nil {
ep.WithProviderSpecific(providerSpecificGeolocationContinentCode, aws.StringValue(r.GeoLocation.ContinentCode))
} else {
if r.GeoLocation.CountryCode != nil {
ep.WithProviderSpecific(providerSpecificGeolocationCountryCode, aws.StringValue(r.GeoLocation.CountryCode))
}
if r.GeoLocation.SubdivisionCode != nil {
ep.WithProviderSpecific(providerSpecificGeolocationSubdivisionCode, aws.StringValue(r.GeoLocation.SubdivisionCode))
}
}
default:
// one of the above needs to be set, otherwise SetIdentifier doesn't make sense
}
}
if r.HealthCheckId != nil {
ep.WithProviderSpecific(providerSpecificHealthCheckID, aws.StringValue(r.HealthCheckId))
}
endpoints = append(endpoints, ep)
}
}
return true
}
for _, z := range zones {
params := &route53.ListResourceRecordSetsInput{
HostedZoneId: z.zone.Id,
MaxItems: aws.String(route53PageSize),
}
client := p.clients[z.profile]
if err := client.ListResourceRecordSetsPagesWithContext(ctx, params, f); err != nil {
return nil, fmt.Errorf("failed to list resource records sets for zone %s using aws profile %q: %w", *z.zone.Id, z.profile, err)
paginator := route53.NewListResourceRecordSetsPaginator(client, &route53.ListResourceRecordSetsInput{
HostedZoneId: z.zone.Id,
MaxItems: aws.Int32(route53PageSize),
})
for paginator.HasMorePages() {
resp, err := paginator.NextPage(ctx)
if err != nil {
return nil, fmt.Errorf("failed to list resource records sets for zone %s using aws profile %q: %w", *z.zone.Id, z.profile, err)
}
for _, r := range resp.ResourceRecordSets {
newEndpoints := make([]*endpoint.Endpoint, 0)
if !p.SupportedRecordType(r.Type) {
continue
}
var ttl endpoint.TTL
if r.TTL != nil {
ttl = endpoint.TTL(*r.TTL)
}
if len(r.ResourceRecords) > 0 {
targets := make([]string, len(r.ResourceRecords))
for idx, rr := range r.ResourceRecords {
targets[idx] = *rr.Value
}
ep := endpoint.NewEndpointWithTTL(wildcardUnescape(*r.Name), string(r.Type), ttl, targets...)
if r.Type == endpoint.RecordTypeCNAME {
ep = ep.WithProviderSpecific(providerSpecificAlias, "false")
}
newEndpoints = append(newEndpoints, ep)
}
if r.AliasTarget != nil {
// Alias records don't have TTLs so provide the default to match the TXT generation
if ttl == 0 {
ttl = recordTTL
}
ep := endpoint.
NewEndpointWithTTL(wildcardUnescape(*r.Name), endpoint.RecordTypeA, ttl, *r.AliasTarget.DNSName).
WithProviderSpecific(providerSpecificEvaluateTargetHealth, fmt.Sprintf("%t", r.AliasTarget.EvaluateTargetHealth)).
WithProviderSpecific(providerSpecificAlias, "true")
newEndpoints = append(newEndpoints, ep)
}
for _, ep := range newEndpoints {
if r.SetIdentifier != nil {
ep.SetIdentifier = *r.SetIdentifier
switch {
case r.Weight != nil:
ep.WithProviderSpecific(providerSpecificWeight, fmt.Sprintf("%d", *r.Weight))
case r.Region != "":
ep.WithProviderSpecific(providerSpecificRegion, string(r.Region))
case r.Failover != "":
ep.WithProviderSpecific(providerSpecificFailover, string(r.Failover))
case r.MultiValueAnswer != nil && *r.MultiValueAnswer:
ep.WithProviderSpecific(providerSpecificMultiValueAnswer, "")
case r.GeoLocation != nil:
if r.GeoLocation.ContinentCode != nil {
ep.WithProviderSpecific(providerSpecificGeolocationContinentCode, *r.GeoLocation.ContinentCode)
} else {
if r.GeoLocation.CountryCode != nil {
ep.WithProviderSpecific(providerSpecificGeolocationCountryCode, *r.GeoLocation.CountryCode)
}
if r.GeoLocation.SubdivisionCode != nil {
ep.WithProviderSpecific(providerSpecificGeolocationSubdivisionCode, *r.GeoLocation.SubdivisionCode)
}
}
default:
// one of the above needs to be set, otherwise SetIdentifier doesn't make sense
}
}
if r.HealthCheckId != nil {
ep.WithProviderSpecific(providerSpecificHealthCheckID, *r.HealthCheckId)
}
endpoints = append(endpoints, ep)
}
}
}
}
@ -584,14 +556,14 @@ func (p *AWSProvider) createUpdateChanges(newEndpoints, oldEndpoints []*endpoint
}
combined := make(Route53Changes, 0, len(deletes)+len(creates)+len(updates))
combined = append(combined, p.newChanges(route53.ChangeActionCreate, creates)...)
combined = append(combined, p.newChanges(route53.ChangeActionUpsert, updates)...)
combined = append(combined, p.newChanges(route53.ChangeActionDelete, deletes)...)
combined = append(combined, p.newChanges(route53types.ChangeActionCreate, creates)...)
combined = append(combined, p.newChanges(route53types.ChangeActionUpsert, updates)...)
combined = append(combined, p.newChanges(route53types.ChangeActionDelete, deletes)...)
return combined
}
// GetDomainFilter generates a filter to exclude any domain that is not controlled by the provider
func (p *AWSProvider) GetDomainFilter() endpoint.DomainFilter {
func (p *AWSProvider) GetDomainFilter() endpoint.DomainFilterInterface {
zones, err := p.Zones(context.Background())
if err != nil {
log.Errorf("failed to list zones: %v", err)
@ -599,7 +571,7 @@ func (p *AWSProvider) GetDomainFilter() endpoint.DomainFilter {
}
zoneNames := []string(nil)
for _, z := range zones {
zoneNames = append(zoneNames, aws.StringValue(z.Name), "."+aws.StringValue(z.Name))
zoneNames = append(zoneNames, *z.Name, "."+*z.Name)
}
log.Infof("Applying provider record filter for domains: %v", zoneNames)
return endpoint.NewDomainFilter(zoneNames)
@ -615,8 +587,8 @@ func (p *AWSProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) e
updateChanges := p.createUpdateChanges(changes.UpdateNew, changes.UpdateOld)
combinedChanges := make(Route53Changes, 0, len(changes.Delete)+len(changes.Create)+len(updateChanges))
combinedChanges = append(combinedChanges, p.newChanges(route53.ChangeActionCreate, changes.Create)...)
combinedChanges = append(combinedChanges, p.newChanges(route53.ChangeActionDelete, changes.Delete)...)
combinedChanges = append(combinedChanges, p.newChanges(route53types.ChangeActionCreate, changes.Create)...)
combinedChanges = append(combinedChanges, p.newChanges(route53types.ChangeActionDelete, changes.Delete)...)
combinedChanges = append(combinedChanges, updateChanges...)
return p.submitChanges(ctx, combinedChanges, zones)
@ -639,7 +611,7 @@ func (p *AWSProvider) submitChanges(ctx context.Context, changes Route53Changes,
var failedZones []string
for z, cs := range changesByZone {
log := log.WithFields(log.Fields{
"zoneName": aws.StringValue(zones[z].zone.Name),
"zoneName": *zones[z].zone.Name,
"zoneID": z,
"profile": zones[z].profile,
})
@ -658,13 +630,13 @@ func (p *AWSProvider) submitChanges(ctx context.Context, changes Route53Changes,
}
for _, c := range b {
log.Infof("Desired change: %s %s %s", *c.Action, *c.ResourceRecordSet.Name, *c.ResourceRecordSet.Type)
log.Infof("Desired change: %s %s %s", c.Action, *c.ResourceRecordSet.Name, c.ResourceRecordSet.Type)
}
if !p.dryRun {
params := &route53.ChangeResourceRecordSetsInput{
HostedZoneId: aws.String(z),
ChangeBatch: &route53.ChangeBatch{
ChangeBatch: &route53types.ChangeBatch{
Changes: b.Route53Changes(),
},
}
@ -672,8 +644,8 @@ func (p *AWSProvider) submitChanges(ctx context.Context, changes Route53Changes,
successfulChanges := 0
client := p.clients[zones[z].profile]
if _, err := client.ChangeResourceRecordSetsWithContext(ctx, params); err != nil {
log.Errorf("Failure in zone %s when submitting change batch: %v", aws.StringValue(zones[z].zone.Name), err)
if _, err := client.ChangeResourceRecordSets(ctx, params); err != nil {
log.Errorf("Failure in zone %s when submitting change batch: %v", *zones[z].zone.Name, err)
changesByOwnership := groupChangesByNameAndOwnershipRelation(b)
@ -682,12 +654,12 @@ func (p *AWSProvider) submitChanges(ctx context.Context, changes Route53Changes,
for _, changes := range changesByOwnership {
for _, c := range changes {
log.Debugf("Desired change: %s %s %s", *c.Action, *c.ResourceRecordSet.Name, *c.ResourceRecordSet.Type)
log.Debugf("Desired change: %s %s %s", c.Action, *c.ResourceRecordSet.Name, c.ResourceRecordSet.Type)
}
params.ChangeBatch = &route53.ChangeBatch{
params.ChangeBatch = &route53types.ChangeBatch{
Changes: changes.Route53Changes(),
}
if _, err := client.ChangeResourceRecordSetsWithContext(ctx, params); err != nil {
if _, err := client.ChangeResourceRecordSets(ctx, params); err != nil {
failedUpdate = true
log.Errorf("Failed submitting change (error: %v), it will be retried in a separate change batch in the next iteration", err)
p.failedChangesQueue[z] = append(p.failedChangesQueue[z], changes...)
@ -726,7 +698,7 @@ func (p *AWSProvider) submitChanges(ctx context.Context, changes Route53Changes,
}
// newChanges returns a collection of Changes based on the given records and action.
func (p *AWSProvider) newChanges(action string, endpoints []*endpoint.Endpoint) Route53Changes {
func (p *AWSProvider) newChanges(action route53types.ChangeAction, endpoints []*endpoint.Endpoint) Route53Changes {
changes := make(Route53Changes, 0, len(endpoints))
for _, endpoint := range endpoints {
@ -735,8 +707,10 @@ func (p *AWSProvider) newChanges(action string, endpoints []*endpoint.Endpoint)
if dualstack {
// make a copy of change, modify RRS type to AAAA, then add new change
rrs := *change.ResourceRecordSet
change2 := &Route53Change{Change: route53.Change{Action: change.Action, ResourceRecordSet: &rrs}}
change2.ResourceRecordSet.Type = aws.String(route53.RRTypeAaaa)
change2 := &Route53Change{
Change: route53types.Change{Action: change.Action, ResourceRecordSet: &rrs},
}
change2.ResourceRecordSet.Type = route53types.RRTypeAaaa
changes = append(changes, change2)
}
}
@ -798,11 +772,11 @@ func (p *AWSProvider) AdjustEndpoints(endpoints []*endpoint.Endpoint) ([]*endpoi
// returned Change is based on the given record by the given action, e.g.
// action=ChangeActionCreate returns a change for creation of the record and
// action=ChangeActionDelete returns a change for deletion of the record.
func (p *AWSProvider) newChange(action string, ep *endpoint.Endpoint) (*Route53Change, bool) {
func (p *AWSProvider) newChange(action route53types.ChangeAction, ep *endpoint.Endpoint) (*Route53Change, bool) {
change := &Route53Change{
Change: route53.Change{
Action: aws.String(action),
ResourceRecordSet: &route53.ResourceRecordSet{
Change: route53types.Change{
Action: action,
ResourceRecordSet: &route53types.ResourceRecordSet{
Name: aws.String(ep.DNSName),
},
},
@ -817,24 +791,24 @@ func (p *AWSProvider) newChange(action string, ep *endpoint.Endpoint) (*Route53C
if val, ok := ep.Labels[endpoint.DualstackLabelKey]; ok {
dualstack = val == "true"
}
change.ResourceRecordSet.Type = aws.String(route53.RRTypeA)
change.ResourceRecordSet.AliasTarget = &route53.AliasTarget{
change.ResourceRecordSet.Type = route53types.RRTypeA
change.ResourceRecordSet.AliasTarget = &route53types.AliasTarget{
DNSName: aws.String(ep.Targets[0]),
HostedZoneId: aws.String(cleanZoneID(targetHostedZone)),
EvaluateTargetHealth: aws.Bool(evalTargetHealth),
EvaluateTargetHealth: evalTargetHealth,
}
change.sizeBytes += len([]byte(ep.Targets[0]))
change.sizeValues += 1
} else {
change.ResourceRecordSet.Type = aws.String(ep.RecordType)
change.ResourceRecordSet.Type = route53types.RRType(ep.RecordType)
if !ep.RecordTTL.IsConfigured() {
change.ResourceRecordSet.TTL = aws.Int64(recordTTL)
} else {
change.ResourceRecordSet.TTL = aws.Int64(int64(ep.RecordTTL))
}
change.ResourceRecordSet.ResourceRecords = make([]*route53.ResourceRecord, len(ep.Targets))
change.ResourceRecordSet.ResourceRecords = make([]route53types.ResourceRecord, len(ep.Targets))
for idx, val := range ep.Targets {
change.ResourceRecordSet.ResourceRecords[idx] = &route53.ResourceRecord{
change.ResourceRecordSet.ResourceRecords[idx] = route53types.ResourceRecord{
Value: aws.String(val),
}
change.sizeBytes += len([]byte(val))
@ -842,7 +816,7 @@ func (p *AWSProvider) newChange(action string, ep *endpoint.Endpoint) (*Route53C
}
}
if action == route53.ChangeActionUpsert {
if action == route53types.ChangeActionUpsert {
// If the value of the Action element is UPSERT, each ResourceRecord element and each character in a Value
// element is counted twice
change.sizeBytes *= 2
@ -861,16 +835,16 @@ func (p *AWSProvider) newChange(action string, ep *endpoint.Endpoint) (*Route53C
change.ResourceRecordSet.Weight = aws.Int64(weight)
}
if prop, ok := ep.GetProviderSpecificProperty(providerSpecificRegion); ok {
change.ResourceRecordSet.Region = aws.String(prop)
change.ResourceRecordSet.Region = route53types.ResourceRecordSetRegion(prop)
}
if prop, ok := ep.GetProviderSpecificProperty(providerSpecificFailover); ok {
change.ResourceRecordSet.Failover = aws.String(prop)
change.ResourceRecordSet.Failover = route53types.ResourceRecordSetFailover(prop)
}
if _, ok := ep.GetProviderSpecificProperty(providerSpecificMultiValueAnswer); ok {
change.ResourceRecordSet.MultiValueAnswer = aws.Bool(true)
}
geolocation := &route53.GeoLocation{}
geolocation := &route53types.GeoLocation{}
useGeolocation := false
if prop, ok := ep.GetProviderSpecificProperty(providerSpecificGeolocationContinentCode); ok {
geolocation.ContinentCode = aws.String(prop)
@ -932,7 +906,7 @@ func groupChangesByNameAndOwnershipRelation(cs Route53Changes) map[string]Route5
for _, v := range cs {
key := v.OwnedRecord
if key == "" {
key = aws.StringValue(v.ResourceRecordSet.Name)
key = *v.ResourceRecordSet.Name
}
changesByOwnership[key] = append(changesByOwnership[key], v)
}
@ -942,8 +916,8 @@ func groupChangesByNameAndOwnershipRelation(cs Route53Changes) map[string]Route5
func (p *AWSProvider) tagsForZone(ctx context.Context, zoneID string, profile string) (map[string]string, error) {
client := p.clients[profile]
response, err := client.ListTagsForResourceWithContext(ctx, &route53.ListTagsForResourceInput{
ResourceType: aws.String("hostedzone"),
response, err := client.ListTagsForResource(ctx, &route53.ListTagsForResourceInput{
ResourceType: route53types.TagResourceTypeHostedzone,
ResourceId: aws.String(zoneID),
})
if err != nil {
@ -1030,10 +1004,10 @@ func batchChangeSet(cs Route53Changes, batchSize int, batchSizeBytes int, batchS
func sortChangesByActionNameType(cs Route53Changes) Route53Changes {
sort.SliceStable(cs, func(i, j int) bool {
if *cs[i].Action > *cs[j].Action {
if cs[i].Action > cs[j].Action {
return true
}
if *cs[i].Action < *cs[j].Action {
if cs[i].Action < cs[j].Action {
return false
}
if *cs[i].ResourceRecordSet.Name < *cs[j].ResourceRecordSet.Name {
@ -1042,7 +1016,7 @@ func sortChangesByActionNameType(cs Route53Changes) Route53Changes {
if *cs[i].ResourceRecordSet.Name > *cs[j].ResourceRecordSet.Name {
return false
}
return *cs[i].ResourceRecordSet.Type < *cs[j].ResourceRecordSet.Type
return cs[i].ResourceRecordSet.Type < cs[j].ResourceRecordSet.Type
})
return cs
@ -1053,34 +1027,34 @@ func changesByZone(zones map[string]*profiledZone, changeSet Route53Changes) map
changes := make(map[string]Route53Changes)
for _, z := range zones {
changes[aws.StringValue(z.zone.Id)] = Route53Changes{}
changes[*z.zone.Id] = Route53Changes{}
}
for _, c := range changeSet {
hostname := provider.EnsureTrailingDot(aws.StringValue(c.ResourceRecordSet.Name))
hostname := provider.EnsureTrailingDot(*c.ResourceRecordSet.Name)
zones := suitableZones(hostname, zones)
if len(zones) == 0 {
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected", c.String())
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected", *c.ResourceRecordSet.Name)
continue
}
for _, z := range zones {
if c.ResourceRecordSet.AliasTarget != nil && aws.StringValue(c.ResourceRecordSet.AliasTarget.HostedZoneId) == sameZoneAlias {
if c.ResourceRecordSet.AliasTarget != nil && *c.ResourceRecordSet.AliasTarget.HostedZoneId == sameZoneAlias {
// alias record is to be created; target needs to be in the same zone as endpoint
// if it's not, this will fail
rrset := *c.ResourceRecordSet
aliasTarget := *rrset.AliasTarget
aliasTarget.HostedZoneId = aws.String(cleanZoneID(aws.StringValue(z.zone.Id)))
aliasTarget.HostedZoneId = aws.String(cleanZoneID(*z.zone.Id))
rrset.AliasTarget = &aliasTarget
c = &Route53Change{
Change: route53.Change{
Change: route53types.Change{
Action: c.Action,
ResourceRecordSet: &rrset,
},
}
}
changes[aws.StringValue(z.zone.Id)] = append(changes[aws.StringValue(z.zone.Id)], c)
log.Debugf("Adding %s to zone %s [Id: %s]", hostname, aws.StringValue(z.zone.Name), aws.StringValue(z.zone.Id))
changes[*z.zone.Id] = append(changes[*z.zone.Id], c)
log.Debugf("Adding %s to zone %s [Id: %s]", hostname, *z.zone.Name, *z.zone.Id)
}
}
@ -1102,10 +1076,10 @@ func suitableZones(hostname string, zones map[string]*profiledZone) []*profiledZ
var publicZone *profiledZone
for _, z := range zones {
if aws.StringValue(z.zone.Name) == hostname || strings.HasSuffix(hostname, "."+aws.StringValue(z.zone.Name)) {
if z.zone.Config == nil || !aws.BoolValue(z.zone.Config.PrivateZone) {
if *z.zone.Name == hostname || strings.HasSuffix(hostname, "."+*z.zone.Name) {
if z.zone.Config == nil || !z.zone.Config.PrivateZone {
// Only select the best matching public zone
if publicZone == nil || len(aws.StringValue(z.zone.Name)) > len(aws.StringValue(publicZone.zone.Name)) {
if publicZone == nil || len(*z.zone.Name) > len(*publicZone.zone.Name) {
publicZone = z
}
} else {
@ -1180,11 +1154,11 @@ func cleanZoneID(id string) string {
return strings.TrimPrefix(id, "/hostedzone/")
}
func (p *AWSProvider) SupportedRecordType(recordType string) bool {
func (p *AWSProvider) SupportedRecordType(recordType route53types.RRType) bool {
switch recordType {
case "MX":
case route53types.RRTypeMx:
return true
default:
return provider.SupportedRecordType(recordType)
return provider.SupportedRecordType(string(recordType))
}
}

File diff suppressed because it is too large Load Diff