diff --git a/pkg/dns/aws/dns.go b/pkg/dns/aws/dns.go index bb35a84f71..5a696b736f 100644 --- a/pkg/dns/aws/dns.go +++ b/pkg/dns/aws/dns.go @@ -115,22 +115,19 @@ func (m *Manager) discoverZones() error { // Find the public zone, which is the non-private zone whose domain matches // the cluster base domain. if len(m.publicZoneID) == 0 { + var lastPublicZone *route53.HostedZone f := func(resp *route53.ListHostedZonesOutput, lastPage bool) (shouldContinue bool) { - for _, zone := range resp.HostedZones { - if aws.StringValue(zone.Name) == m.config.BaseDomain && !aws.BoolValue(zone.Config.PrivateZone) { - m.publicZoneID = aws.StringValue(zone.Id) - return false - } - } - return true + lastPublicZone = findNearestPublicParentZone(m.config.BaseDomain, resp.HostedZones, lastPublicZone) + return lastPage } err := m.route53.ListHostedZonesPages(&route53.ListHostedZonesInput{}, f) if err != nil { return fmt.Errorf("failed to list hosted zones: %v", err) } - if len(m.publicZoneID) == 0 { + if lastPublicZone == nil || aws.StringValue(lastPublicZone.Name) == "" { return fmt.Errorf("couldn't find public hosted zone for %q", m.config.BaseDomain) } + m.publicZoneID = aws.StringValue(lastPublicZone.Name) logrus.Infof("using public zone %s", m.publicZoneID) } @@ -271,3 +268,35 @@ func (m *Manager) updateAlias(domain, zoneID, target, targetHostedZoneID string) logrus.Infof("updated DNS record in zone %s, %s -> %s: %v", zoneID, domain, target, resp) return nil } + +func findNearestPublicParentZone(domain string, candidates []*route53.HostedZone, lastNearest *route53.HostedZone) *route53.HostedZone { + last := lastNearest + if last == nil { + last = &route53.HostedZone{} + } + parents := []string{domain} + for { + idx := strings.Index(domain, ".") + if idx == -1 { + break + } + parents = append(parents, domain[idx+1:]) + domain = domain[idx+1:] + } + + for i, zone := range candidates { + // If the Config is empty or If the zone is private, we skip it. + if zone.Config == nil || aws.BoolValue(zone.Config.PrivateZone) { + continue + } + name := aws.StringValue(zone.Name) + for _, p := range parents { + if p == name && len(name) >= len(aws.StringValue(last.Name)) { + last = candidates[i] + // there are no parents that can be longer after this. + break + } + } + } + return last +} diff --git a/pkg/dns/aws/dns_test.go b/pkg/dns/aws/dns_test.go new file mode 100644 index 0000000000..ce57557c1c --- /dev/null +++ b/pkg/dns/aws/dns_test.go @@ -0,0 +1,153 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/route53" +) + +type candidateZone struct { + public bool + name string +} + +func (cz candidateZone) ToHostedZone() *route53.HostedZone { + return &route53.HostedZone{Name: aws.String(cz.name), Config: &route53.HostedZoneConfig{PrivateZone: aws.Bool(!cz.public)}} +} + +func Test_findNearestPublicParentZone(t *testing.T) { + tests := []struct { + domain string + last candidateZone + zones []candidateZone + + exp string + }{{ + domain: "team-1.staging.20190205.dev.openshift.com.", + + exp: "", + }, { + domain: "team-1.staging.20190205.dev.openshift.com.", + zones: []candidateZone{{ + name: "team-1.staging.20190205.dev.openshift.com.", + }}, + + exp: "", + }, { + domain: "team-1.staging.20190205.dev.openshift.com.", + zones: []candidateZone{{ + public: true, + name: "team-1.staging.20190205.dev.openshift.com.", + }}, + + exp: "team-1.staging.20190205.dev.openshift.com.", + }, { + domain: "team-1.staging.20190205.dev.openshift.com.", + zones: []candidateZone{{ + name: "team-1.staging.20190205.dev.openshift.com.", + }, { + public: true, + name: "dev.openshift.com.", + }}, + + exp: "dev.openshift.com.", + }, { + domain: "team-1.staging.20190205.dev.openshift.com.", + zones: []candidateZone{{ + name: "team-1.staging.20190205.dev.openshift.com.", + }, { + public: true, + name: "ging.20190205.dev.openshift.com.", + }}, + + exp: "", + }, { + domain: "team-1.staging.20190205.dev.openshift.com.", + zones: []candidateZone{{ + name: "team-1.staging.20190205.dev.openshift.com.", + }, { + public: true, + name: "staging.20190205.dev.openshift.com.", + }}, + + exp: "staging.20190205.dev.openshift.com.", + }, { + domain: "team-1.staging.20190205.dev.openshift.com.", + zones: []candidateZone{{ + name: "team-1.staging.20190205.dev.openshift.com.", + }, { + public: true, + name: "dev.openshift.com.", + }, { + public: true, + name: "staging.20190205.dev.openshift.com.", + }}, + + exp: "staging.20190205.dev.openshift.com.", + }, { + domain: "team-1.staging.20190205.dev.openshift.com.", + zones: []candidateZone{{ + name: "team-1.staging.20190205.dev.openshift.com.", + }, { + public: true, + name: "dev.openshift.com.", + }, { + name: "staging.20190205.dev.openshift.com.", + }}, + + exp: "dev.openshift.com.", + }, { + domain: "team-1.staging.20190205.dev.openshift.com.", + last: candidateZone{ + public: true, + name: "staging.20190205.dev.openshift.com.", + }, + zones: []candidateZone{{ + name: "team-1.staging.20190205.dev.openshift.com.", + }, { + public: true, + name: "dev.openshift.com.", + }, { + public: true, + name: "20190205.dev.openshift.com.", + }}, + + exp: "staging.20190205.dev.openshift.com.", + }, { + domain: "team-1.staging.20190205.dev.openshift.com.", + last: candidateZone{ + public: true, + name: "20190205.dev.openshift.com.", + }, + zones: []candidateZone{{ + name: "team-1.staging.20190205.dev.openshift.com.", + }, { + public: true, + name: "dev.openshift.com.", + }, { + public: true, + name: "staging.20190205.dev.openshift.com.", + }}, + + exp: "staging.20190205.dev.openshift.com.", + }} + + for idx, test := range tests { + t.Run(fmt.Sprintf("#%d", idx), func(t *testing.T) { + var zones []*route53.HostedZone + for _, z := range test.zones { + zones = append(zones, z.ToHostedZone()) + } + var last *route53.HostedZone + if test.last.name != "" { + last = test.last.ToHostedZone() + } + got := findNearestPublicParentZone(test.domain, zones, last) + if test.exp != aws.StringValue(got.Name) { + t.Fatalf("exp: %s got: %s", test.exp, aws.StringValue(got.Name)) + } + }) + } +}