Skip to content
Open
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
51 changes: 38 additions & 13 deletions provider/aws/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ const (
providerSpecificGeoProximityLocationLocalZoneGroup = "aws/geoproximity-local-zone-group"
providerSpecificMultiValueAnswer = "aws/multi-value-answer"
providerSpecificHealthCheckID = "aws/health-check-id"
providerSpecificAliasDisableA = "aws/alias-disable-a"
providerSpecificAliasDisableAAAA = "aws/alias-disable-aaaa"
sameZoneAlias = "same-zone"
// Currently supported up to 10 health checks or hosted zones.
// https://docs.aws.amazon.com/Route53/latest/APIReference/API_ListTagsForResources.html#API_ListTagsForResources_RequestSyntax
Expand Down Expand Up @@ -863,19 +865,32 @@ func (p *AWSProvider) AdjustEndpoints(endpoints []*endpoint.Endpoint) ([]*endpoi
ep.SetProviderSpecificProperty(providerSpecificEvaluateTargetHealth, strconv.FormatBool(p.evaluateTargetHealth))
}

if ep.RecordType == endpoint.RecordTypeCNAME {
// This needs to match two records from Route53, one alias for 'A' (IPv4)
// and one alias for 'AAAA' (IPv6).
aliasCnameAaaaEndpoints = append(aliasCnameAaaaEndpoints, &endpoint.Endpoint{
DNSName: ep.DNSName,
Targets: ep.Targets,
RecordType: endpoint.RecordTypeAAAA,
RecordTTL: ep.RecordTTL,
Labels: ep.Labels,
ProviderSpecific: ep.ProviderSpecific,
SetIdentifier: ep.SetIdentifier,
})
ep.RecordType = endpoint.RecordTypeA
disableA := aliasDisableARecord(ep)
disableAaaa := aliasDisableAaaaRecord(ep)
disableAlias := disableA && disableAaaa
enableAandAAAA := !disableA && !disableAaaa

if ep.RecordType == endpoint.RecordTypeCNAME && !disableAlias {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something does not sound here. This most likely could and should be a method on it's own, so we could add tests just for this method

something like

log.Debugf("Modifying endpoint: %v, changing record type from %s to %s", ep, ....)
if ep.RecordType == endpoint.RecordTypeCNAME && strings.CutPrefix(k, disableAliasPrefix) {
   epModified = .....modify me...
   or 
   epC = ep.DeepCopy()
   epC.RecordType = modifyMe(......)
}

if disableA {
ep.RecordType = endpoint.RecordTypeAAAA
}
if disableAaaa {
ep.RecordType = endpoint.RecordTypeA
}
if enableAandAAAA {
// Add a new endpoint for the AAAA record
aliasCnameAaaaEndpoints = append(aliasCnameAaaaEndpoints, &endpoint.Endpoint{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure not to use &endpoint.Endpoint{}, but methods available in the package. This will create tech debt for us.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say it should be clone or similar function in Endpoint package, to make sure we do not mutate original

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aEp := ep.DeepCopy()

DNSName: ep.DNSName,
Targets: ep.Targets,
RecordType: endpoint.RecordTypeAAAA,
RecordTTL: ep.RecordTTL,
Labels: ep.Labels,
ProviderSpecific: ep.ProviderSpecific,
SetIdentifier: ep.SetIdentifier,
})
// Modify the original endpoint to be the A record
ep.RecordType = endpoint.RecordTypeA
}
}
} else {
ep.DeleteProviderSpecificProperty(providerSpecificEvaluateTargetHealth)
Expand All @@ -888,6 +903,16 @@ func (p *AWSProvider) AdjustEndpoints(endpoints []*endpoint.Endpoint) ([]*endpoi
return endpoints, nil
}

func aliasDisableARecord(ep *endpoint.Endpoint) bool {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My opinion, we will benefit to have a generic,centralised method on Endpoint object for all annotations that contains booleans like true/false

Copy link
Member

@ivankatliarchuk ivankatliarchuk Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

something like

func (ep *endpoint.Endpoint) GetProviderSpecificBool(key string bool) bool {
	val, ok := ep.GetProviderSpecificProperty(key)
	if !ok {
		return false
	}
	// Normalize whitespace and case; accept common truthy values
	v := strings.TrimSpace(strings.ToLower(val))
	switch v {
	case "1", "t", "true", "yes", "y":
		return true
	case "0", "f", "false", "no", "n":
		return false
	default:		
		return false
	}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That does sound like a good idea.
However, in this change we’re going to use values other than just true/false, as @mloiseleur mentioned, and I’m a bit concerned that introducing a generic, centralized method here would make the scope of this PR too large.
I’d prefer to handle that refactor in a separate PR that can apply the change across the whole codebase. What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

disable, ok := ep.GetProviderSpecificProperty(providerSpecificAliasDisableA)
return ok && disable == "true"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use constants instead of strings for "true".

}

func aliasDisableAaaaRecord(ep *endpoint.Endpoint) bool {
disable, ok := ep.GetProviderSpecificProperty(providerSpecificAliasDisableAAAA)
return ok && disable == "true"
}

// if the endpoint is using geoproximity, set the bias to 0 if not set
// this is needed to avoid unnecessary Upserts if the desired endpoint doesn't specify a bias
func adjustGeoProximityLocationEndpoint(ep *endpoint.Endpoint) {
Expand Down
6 changes: 6 additions & 0 deletions provider/aws/aws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,9 @@ func TestAWSAdjustEndpoints(t *testing.T) {
endpoint.NewEndpoint("cname-test-elb-no-eth.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "false"), // eth = evaluate target health
endpoint.NewEndpoint("cname-test-elb-alias.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificAlias, "true").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "true"),
endpoint.NewEndpoint("a-test-geoproximity-no-bias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "8.8.8.8").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificGeoProximityLocationAWSRegion, "us-west-2"),
endpoint.NewEndpoint("same-zone-alias-test.teapot.zalan.do", endpoint.RecordTypeCNAME, "same-zone-alias-target.teapot.zalan.do").WithProviderSpecific(providerSpecificAlias, "true").WithProviderSpecific(providerSpecificAliasDisableA, "true"),
endpoint.NewEndpoint("same-zone-alias-test2.teapot.zalan.do", endpoint.RecordTypeCNAME, "same-zone-alias-target2.teapot.zalan.do").WithProviderSpecific(providerSpecificAlias, "true").WithProviderSpecific(providerSpecificAliasDisableAAAA, "true"),
endpoint.NewEndpoint("same-zone-alias-test3.teapot.zalan.do", endpoint.RecordTypeCNAME, "same-zone-alias-target3.teapot.zalan.do").WithProviderSpecific(providerSpecificAlias, "true").WithProviderSpecific(providerSpecificAliasDisableA, "true").WithProviderSpecific(providerSpecificAliasDisableAAAA, "true"),
}

records, err := provider.AdjustEndpoints(records)
Expand All @@ -728,6 +731,9 @@ func TestAWSAdjustEndpoints(t *testing.T) {
endpoint.NewEndpoint("cname-test-elb-alias.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificAlias, "true").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "true"),
endpoint.NewEndpoint("cname-test-elb-alias.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeAAAA, "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificAlias, "true").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "true"),
endpoint.NewEndpoint("a-test-geoproximity-no-bias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "8.8.8.8").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificGeoProximityLocationAWSRegion, "us-west-2").WithProviderSpecific(providerSpecificGeoProximityLocationBias, "0"),
endpoint.NewEndpoint("same-zone-alias-test.teapot.zalan.do", endpoint.RecordTypeAAAA, "same-zone-alias-target.teapot.zalan.do").WithProviderSpecific(providerSpecificAlias, "true").WithProviderSpecific(providerSpecificAliasDisableA, "true").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "true"),
endpoint.NewEndpoint("same-zone-alias-test2.teapot.zalan.do", endpoint.RecordTypeA, "same-zone-alias-target2.teapot.zalan.do").WithProviderSpecific(providerSpecificAlias, "true").WithProviderSpecific(providerSpecificAliasDisableAAAA, "true").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "true"),
endpoint.NewEndpoint("same-zone-alias-test3.teapot.zalan.do", endpoint.RecordTypeCNAME, "same-zone-alias-target3.teapot.zalan.do").WithProviderSpecific(providerSpecificAlias, "true").WithProviderSpecific(providerSpecificAliasDisableA, "true").WithProviderSpecific(providerSpecificAliasDisableAAAA, "true").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "true"),
})
}

Expand Down
Loading