Skip to content
Draft
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
145 changes: 78 additions & 67 deletions provider/alibabacloud/alibaba_cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -633,45 +633,45 @@ func (p *AlibabaCloudProvider) equals(record alidns.Record, endpoint *endpoint.E
return ttl1 == ttl2
}

// updateRecords updates, deletes, and creates DNS records to match the desired endpoints.
func (p *AlibabaCloudProvider) updateRecords(recordMap map[string][]alidns.Record, endpoints []*endpoint.Endpoint, hostedZoneDomains []string) {
for _, endpoint := range endpoints {
key := p.getRecordKeyByEndpoint(endpoint)
records := recordMap[key]
for _, record := range records {
value := record.Value
if record.Type == "TXT" {
value = p.unescapeTXTRecordValue(value)
}
found := false
for _, target := range endpoint.Targets {
// Find matched record to delete
if value == target {
found = true
}
}
if found {
if !p.equals(record, endpoint) {
// Update record
p.updateRecord(record, endpoint)
}
} else {
p.deleteRecord(record.RecordId)
}
p.updateOrDeleteRecords(records, endpoint)
p.createMissingRecords(records, endpoint, hostedZoneDomains)
}
}

// updateOrDeleteRecords updates records that are different and deletes records that are not in the desired endpoints.
func (p *AlibabaCloudProvider) updateOrDeleteRecords(records []alidns.Record, endpoint *endpoint.Endpoint) {
for _, record := range records {
value := record.Value
if record.Type == "TXT" {
value = p.unescapeTXTRecordValue(value)
}
for _, target := range endpoint.Targets {
if endpoint.RecordType == "TXT" {
target = p.escapeTXTRecordValue(target)
}
found := false
for _, record := range records {
// Find matched record to delete
if record.Value == target {
found = true
}
}
if !found {
p.createRecord(endpoint, target, hostedZoneDomains)
found := slices.Contains(endpoint.Targets, value)
if found {
if !p.equals(record, endpoint) {
p.updateRecord(record, endpoint)
}
} else {
p.deleteRecord(record.RecordId)
}
}
}

// createMissingRecords creates records that are missing.
func (p *AlibabaCloudProvider) createMissingRecords(records []alidns.Record, endpoint *endpoint.Endpoint, hostedZoneDomains []string) {
for _, target := range endpoint.Targets {
if endpoint.RecordType == "TXT" {
target = p.escapeTXTRecordValue(target)
}
found := slices.ContainsFunc(records, func(record alidns.Record) bool {
return record.Value == target
})
if !found {
p.createRecord(endpoint, target, hostedZoneDomains)
}
}
}
Expand Down Expand Up @@ -1022,6 +1022,7 @@ func (p *AlibabaCloudProvider) equalsPrivateZone(record pvtz.Record, endpoint *e
return ttl1 == ttl2
}

// updatePrivateZoneRecords updates, deletes, and creates records in the private zone to match the desired endpoints.
func (p *AlibabaCloudProvider) updatePrivateZoneRecords(zones map[string]*alibabaPrivateZone, endpoints []*endpoint.Endpoint) {
zoneNames := keys(zones)
for _, endpoint := range endpoints {
Expand All @@ -1031,43 +1032,53 @@ func (p *AlibabaCloudProvider) updatePrivateZoneRecords(zones map[string]*alibab
log.Errorf("Failed to update %s record named '%s' for Alibaba Cloud Private Zone: failed to find private zone '%s'", endpoint.RecordType, endpoint.DNSName, domain)
continue
}
p.updateOrDeletePrivateZoneRecords(zone, endpoint, rr)
p.createMissingPrivateZoneRecords(zones, zone, endpoint, rr)
}
}

for _, record := range zone.records {
if record.Rr != rr || record.Type != endpoint.RecordType {
continue
}
value := record.Value
if record.Type == "TXT" {
value = p.unescapeTXTRecordValue(value)
}
found := slices.Contains(endpoint.Targets, value)
if found {
if !p.equalsPrivateZone(record, endpoint) {
// Update record
p.updatePrivateZoneRecord(record, endpoint)
}
} else {
p.deletePrivateZoneRecord(record.RecordId)
}
// updateOrDeletePrivateZoneRecords updates records in the private zone that are different and
// deletes records in the private zone that are not in the desired endpoint.
func (p *AlibabaCloudProvider) updateOrDeletePrivateZoneRecords(
zone *alibabaPrivateZone,
endpoint *endpoint.Endpoint,
rr string,
) {
for _, record := range zone.records {
if record.Rr != rr || record.Type != endpoint.RecordType {
continue
}
for _, target := range endpoint.Targets {
if endpoint.RecordType == "TXT" {
target = p.escapeTXTRecordValue(target)
}
found := false
for _, record := range zone.records {
if record.Rr != rr || record.Type != endpoint.RecordType {
continue
}
// Find matched record to delete
if record.Value == target {
found = true
break
}
}
if !found {
p.createPrivateZoneRecord(zones, endpoint, target)
value := record.Value
if record.Type == "TXT" {
value = p.unescapeTXTRecordValue(value)
}
found := slices.Contains(endpoint.Targets, value)
if found {
if !p.equalsPrivateZone(record, endpoint) {
p.updatePrivateZoneRecord(record, endpoint)
}
} else {
p.deletePrivateZoneRecord(record.RecordId)
}
}
}

// createMissingPrivateZoneRecords creates records in the private zone that are missing.
func (p *AlibabaCloudProvider) createMissingPrivateZoneRecords(
zones map[string]*alibabaPrivateZone,
zone *alibabaPrivateZone,
endpoint *endpoint.Endpoint,
rr string,
) {
for _, target := range endpoint.Targets {
if endpoint.RecordType == "TXT" {
target = p.escapeTXTRecordValue(target)
}
found := slices.ContainsFunc(zone.records, func(record pvtz.Record) bool {
return record.Rr == rr && record.Type == endpoint.RecordType && record.Value == target
})
if !found {
p.createPrivateZoneRecord(zones, endpoint, target)
}
}
}
Expand Down
161 changes: 99 additions & 62 deletions provider/alibabacloud/alibaba_cloud_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,69 +439,106 @@ func TestAlibabaCloudProvider_ApplyChanges_PrivateZone(t *testing.T) {

func TestAlibabaCloudProvider_splitDNSName(t *testing.T) {
p := newTestAlibabaCloudProvider(false)
endpoint := &endpoint.Endpoint{}
hostedZoneDomains := []string{"container-service.top", "example.org"}

type testCase struct {
name string
dnsName string
zones []string
wantRR string
wantDomain string
}
var emptyZoneDomains []string

endpoint.DNSName = "www.example.org"
rr, domain := p.splitDNSName(endpoint.DNSName, hostedZoneDomains)
if rr != "www" || domain != "example.org" {
t.Errorf("Failed to splitDNSName for %s: rr=%s, domain=%s", endpoint.DNSName, rr, domain)
}
endpoint.DNSName = ".example.org"
rr, domain = p.splitDNSName(endpoint.DNSName, hostedZoneDomains)
if rr != "@" || domain != "example.org" {
t.Errorf("Failed to splitDNSName for %s: rr=%s, domain=%s", endpoint.DNSName, rr, domain)
}
endpoint.DNSName = "www"
rr, domain = p.splitDNSName(endpoint.DNSName, hostedZoneDomains)
if rr != "@" || domain != "" {
t.Errorf("Failed to splitDNSName for %s: rr=%s, domain=%s", endpoint.DNSName, rr, domain)
}
endpoint.DNSName = ""
rr, domain = p.splitDNSName(endpoint.DNSName, hostedZoneDomains)
if rr != "@" || domain != "" {
t.Errorf("Failed to splitDNSName for %s: rr=%s, domain=%s", endpoint.DNSName, rr, domain)
}
endpoint.DNSName = "_30000._tcp.container-service.top"
rr, domain = p.splitDNSName(endpoint.DNSName, hostedZoneDomains)
if rr != "_30000._tcp" || domain != "container-service.top" {
t.Errorf("Failed to splitDNSName for %s: rr=%s, domain=%s", endpoint.DNSName, rr, domain)
}
endpoint.DNSName = "container-service.top"
rr, domain = p.splitDNSName(endpoint.DNSName, hostedZoneDomains)
if rr != "@" || domain != "container-service.top" {
t.Errorf("Failed to splitDNSName for %s: rr=%s, domain=%s", endpoint.DNSName, rr, domain)
}
endpoint.DNSName = "a.b.container-service.top"
rr, domain = p.splitDNSName(endpoint.DNSName, hostedZoneDomains)
if rr != "a.b" || domain != "container-service.top" {
t.Errorf("Failed to splitDNSName for %s: rr=%s, domain=%s", endpoint.DNSName, rr, domain)
}
endpoint.DNSName = "a.b.c.container-service.top"
rr, domain = p.splitDNSName(endpoint.DNSName, hostedZoneDomains)
if rr != "a.b.c" || domain != "container-service.top" {
t.Errorf("Failed to splitDNSName for %s: rr=%s, domain=%s", endpoint.DNSName, rr, domain)
}
endpoint.DNSName = "a.b.c.container-service.top"
rr, domain = p.splitDNSName(endpoint.DNSName, []string{"c.container-service.top"})
if rr != "a.b" || domain != "c.container-service.top" {
t.Errorf("Failed to splitDNSName for %s: rr=%s, domain=%s", endpoint.DNSName, rr, domain)
}

endpoint.DNSName = "a.b.c.container-service.top"
rr, domain = p.splitDNSName(endpoint.DNSName, []string{"container-service.top", "c.container-service.top"})
if rr != "a.b" || domain != "c.container-service.top" {
t.Errorf("Failed to splitDNSName for %s: rr=%s, domain=%s", endpoint.DNSName, rr, domain)
}
rr, domain = p.splitDNSName(endpoint.DNSName, emptyZoneDomains)
if rr != "@" || domain != "" {
t.Errorf("Failed to splitDNSName with emptyZoneDomains for %s: rr=%s, domain=%s", endpoint.DNSName, rr, domain)
}
rr, domain = p.splitDNSName(endpoint.DNSName, []string{"example.com"})
if rr != "@" || domain != "" {
t.Errorf("Failed to splitDNSName for %s: rr=%s, domain=%s", endpoint.DNSName, rr, domain)
cases := []testCase{
{
name: "subdomain matches example.org zone",
dnsName: "www.example.org",
zones: []string{"container-service.top", "example.org"},
wantRR: "www",
wantDomain: "example.org",
},
{
name: "dot prefix matches example.org zone (root record)",
dnsName: ".example.org",
zones: []string{"container-service.top", "example.org"},
wantRR: "@",
wantDomain: "example.org",
},
{
name: "no domain match returns root RR",
dnsName: "www",
zones: []string{"container-service.top", "example.org"},
wantRR: "@",
wantDomain: "",
},
{
name: "empty DNS name returns root RR",
dnsName: "",
zones: []string{"container-service.top", "example.org"},
wantRR: "@",
wantDomain: "",
},
{
name: "SRV record with subdomain matches container-service.top",
dnsName: "_30000._tcp.container-service.top",
zones: []string{"container-service.top", "example.org"},
wantRR: "_30000._tcp",
wantDomain: "container-service.top",
},
{
name: "zone only matches container-service.top root",
dnsName: "container-service.top",
zones: []string{"container-service.top", "example.org"},
wantRR: "@",
wantDomain: "container-service.top",
},
{
name: "subdomain a.b matches container-service.top",
dnsName: "a.b.container-service.top",
zones: []string{"container-service.top", "example.org"},
wantRR: "a.b",
wantDomain: "container-service.top",
},
{
name: "subdomain a.b.c matches container-service.top",
dnsName: "a.b.c.container-service.top",
zones: []string{"container-service.top", "example.org"},
wantRR: "a.b.c",
wantDomain: "container-service.top",
},
{
name: "subdomain a.b matches c.container-service.top zone",
dnsName: "a.b.c.container-service.top",
zones: []string{"c.container-service.top"},
wantRR: "a.b",
wantDomain: "c.container-service.top",
},
{
name: "subdomain a.b matches most specific zone c.container-service.top",
dnsName: "a.b.c.container-service.top",
zones: []string{"container-service.top", "c.container-service.top"},
wantRR: "a.b",
wantDomain: "c.container-service.top",
},
{
name: "no zones configured returns root RR",
dnsName: "a.b.c.container-service.top",
zones: emptyZoneDomains,
wantRR: "@",
wantDomain: "",
},
{
name: "no matching zone in list returns root RR",
dnsName: "a.b.c.container-service.top",
zones: []string{"example.com"},
wantRR: "@",
wantDomain: "",
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
rr, domain := p.splitDNSName(tc.dnsName, tc.zones)
assert.Equal(t, tc.wantRR, rr, "%s: expected RR %q, got %q", tc.name, tc.wantRR, rr)
assert.Equal(t, tc.wantDomain, domain, "%s: expected domain %q, got %q", tc.name, tc.wantDomain, domain)
})
}
}

Expand Down
Loading