Skip to content

Commit

Permalink
Merge 540b97a into e3055f1
Browse files Browse the repository at this point in the history
  • Loading branch information
semoac committed Jun 24, 2020
2 parents e3055f1 + 540b97a commit a40745d
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 8 deletions.
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ func main() {
APIRetries: cfg.AWSAPIRetries,
PreferCNAME: cfg.AWSPreferCNAME,
DryRun: cfg.DryRun,
AwsUseBestZoneMatch: cfg.AwsUseBestZoneMatch,
},
)
case "aws-sd":
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/externaldns/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ type Config struct {
TransIPAccountName string
TransIPPrivateKeyFile string
DigitalOceanAPIPageSize int
AwsUseBestZoneMatch bool
}

var defaultConfig = &Config{
Expand Down Expand Up @@ -241,6 +242,7 @@ var defaultConfig = &Config{
TransIPAccountName: "",
TransIPPrivateKeyFile: "",
DigitalOceanAPIPageSize: 50,
AwsUseBestZoneMatch: false,
}

// NewConfig returns new Config object
Expand Down Expand Up @@ -415,6 +417,8 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("metrics-address", "Specify where to serve the metrics and health check endpoint (default: :7979)").Default(defaultConfig.MetricsAddress).StringVar(&cfg.MetricsAddress)
app.Flag("log-level", "Set the level of logging. (default: info, options: panic, debug, info, warning, error, fatal").Default(defaultConfig.LogLevel).EnumVar(&cfg.LogLevel, allLogLevelsAsStrings()...)

// Best Zone Match flag
app.Flag("aws-best-zone-match", "Search for the longest zone suffix possible when filtering zones (default: disabled)").Default(strconv.FormatBool(defaultConfig.AwsUseBestZoneMatch)).BoolVar(&cfg.AwsUseBestZoneMatch)
_, err := app.Parse(args)
if err != nil {
return err
Expand Down
46 changes: 39 additions & 7 deletions provider/aws/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ type AWSProvider struct {
// filter hosted zones by tags
zoneTagFilter provider.ZoneTagFilter
preferCNAME bool
// find best zone match
awsUseBestZoneMatch bool
}

// AWSConfig contains configuration to create a new AWS provider.
Expand All @@ -146,6 +148,7 @@ type AWSConfig struct {
APIRetries int
PreferCNAME bool
DryRun bool
AwsUseBestZoneMatch bool
}

// NewAWSProvider initializes a new AWS Route53 based Provider.
Expand Down Expand Up @@ -185,6 +188,7 @@ func NewAWSProvider(awsConfig AWSConfig) (*AWSProvider, error) {
evaluateTargetHealth: awsConfig.EvaluateTargetHealth,
preferCNAME: awsConfig.PreferCNAME,
dryRun: awsConfig.DryRun,
awsUseBestZoneMatch: awsConfig.AwsUseBestZoneMatch,
}

return provider, nil
Expand Down Expand Up @@ -408,7 +412,7 @@ func (p *AWSProvider) submitChanges(ctx context.Context, changes []*route53.Chan
}

// separate into per-zone change sets to be passed to the API.
changesByZone := changesByZone(zones, changes)
changesByZone := changesByZone(zones, changes, p.awsUseBestZoneMatch)
if len(changesByZone) == 0 {
log.Info("All records are already up to date, there are no changes for the matching hosted zones")
}
Expand Down Expand Up @@ -659,22 +663,25 @@ func sortChangesByActionNameType(cs []*route53.Change) []*route53.Change {
}

// changesByZone separates a multi-zone change into a single change per zone.
func changesByZone(zones map[string]*route53.HostedZone, changeSet []*route53.Change) map[string][]*route53.Change {
func changesByZone(zones map[string]*route53.HostedZone, changeSet []*route53.Change, awsUseBestZoneMatch bool) map[string][]*route53.Change {
changes := make(map[string][]*route53.Change)

for _, z := range zones {
changes[aws.StringValue(z.Id)] = []*route53.Change{}
}

for _, c := range changeSet {
hostname := provider.EnsureTrailingDot(aws.StringValue(c.ResourceRecordSet.Name))

zones := suitableZones(hostname, zones)
if len(zones) == 0 {
var thisZones []*route53.HostedZone
if awsUseBestZoneMatch {
thisZones = findBestZone(hostname, zones)
} else {
thisZones = suitableZones(hostname, zones)
}
if len(thisZones) == 0 {
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected", c.String())
continue
}
for _, z := range zones {
for _, z := range thisZones {
changes[aws.StringValue(z.Id)] = append(changes[aws.StringValue(z.Id)], c)
log.Debugf("Adding %s to zone %s [Id: %s]", hostname, aws.StringValue(z.Name), aws.StringValue(z.Id))
}
Expand Down Expand Up @@ -717,6 +724,31 @@ func suitableZones(hostname string, zones map[string]*route53.HostedZone) []*rou
return matchingZones
}

// Return the list of zones that cover the longest suffix.
func findBestZone(hostname string, zones map[string]*route53.HostedZone) []*route53.HostedZone {
// return only one zone with the longest match.
// This allows to only create the record in the best matching domain name.
var matchingZones []*route53.HostedZone
var maxNameLength int
for _, zone := range zones {
if aws.StringValue(zone.Name) == hostname || strings.HasSuffix(hostname, "."+aws.StringValue(zone.Name)) {
// The hosname is zone.name or ends with zone.name as suffix.
if len(aws.StringValue(zone.Name)) >= maxNameLength {
if len(aws.StringValue(zone.Name)) == maxNameLength {
// if len (zone.name) is equal to the longest zone found, append to the current response.
// This should not be a possible scenario
matchingZones = append(matchingZones, zone)
} else {
// if len (zone.name) is greater than the last zone.name found, replace the array with a new one
maxNameLength = len(aws.StringValue(zone.Name))
matchingZones = []*route53.HostedZone{zone}
}
}
}
}
return matchingZones
}

// useAlias determines if AWS ALIAS should be used.
func useAlias(ep *endpoint.Endpoint, preferCNAME bool) bool {
if preferCNAME {
Expand Down
36 changes: 35 additions & 1 deletion provider/aws/aws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ func TestAWSChangesByZones(t *testing.T) {
},
}

changesByZone := changesByZone(zones, changes)
changesByZone := changesByZone(zones, changes, false)
require.Len(t, changesByZone, 3)

validateAWSChangeRecords(t, changesByZone["foo-example-org"], []*route53.Change{
Expand Down Expand Up @@ -686,6 +686,40 @@ func TestAWSChangesByZones(t *testing.T) {
})
}

func TestAWSChangesByZonesWithBestzone(t *testing.T) {
changes := []*route53.Change{
{
Action: aws.String(route53.ChangeActionCreate),
ResourceRecordSet: &route53.ResourceRecordSet{
Name: aws.String("bar.foo.example.org"), TTL: aws.Int64(1),
},
},
}

zones := map[string]*route53.HostedZone{
"foo-example-org": {
Id: aws.String("foo-example-org"),
Name: aws.String("foo.example.org."),
},
"example-org": {
Id: aws.String("example-org"),
Name: aws.String("example.org."),
},
}

changesByZone := changesByZone(zones, changes, true)
require.Len(t, changesByZone, 1)

validateAWSChangeRecords(t, changesByZone["foo-example-org"], []*route53.Change{
{
Action: aws.String(route53.ChangeActionCreate),
ResourceRecordSet: &route53.ResourceRecordSet{
Name: aws.String("bar.foo.example.org"), TTL: aws.Int64(1),
},
},
})
}

func TestAWSsubmitChanges(t *testing.T) {
provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, []*endpoint.Endpoint{})
const subnets = 16
Expand Down

0 comments on commit a40745d

Please sign in to comment.