diff --git a/.github/workflows/devstack.yml b/.github/workflows/devstack.yml index c80f28e..97a0305 100644 --- a/.github/workflows/devstack.yml +++ b/.github/workflows/devstack.yml @@ -107,3 +107,23 @@ jobs: run: | if [ $(openstack recordset list all -f value | grep -c " TXT ") -ne 10 ]; then exit 1; fi if [ $(openstack recordset list all -f value | grep -c " A ") -ne 10 ]; then exit 2; fi + + # external-dns's `fake` source only ever produces A records, so the checks above never + # exercise AAAA handling. Regression test for a bug where the webhook's Records() call + # silently dropped AAAA recordsets, so external-dns would never see an AAAA record as + # already existing and would loop forever trying (and failing) to recreate it. Designate + # itself already had the record correctly, so `openstack recordset list` alone can't catch + # this - the bug is specifically in what the webhook reports back over its own API. + - name: Seed an AAAA recordset directly in Designate + run: | + openstack recordset create example.com. aaaa-webhook-test.example.com. --type AAAA --record 2001:db8::1 + + - name: Wait for AAAA recordset to become ACTIVE + run: | + while [ "$(openstack recordset list example.com. -f csv | grep PENDING)" != "" ]; do date; openstack recordset list example.com. -f value; sleep 1; done + + - name: Verify the webhook reports the AAAA recordset via GET /records + run: | + curl -sf -H "Accept: application/external.dns.webhook+json;version=1" http://127.0.0.1:8888/records -o /tmp/records.json + cat /tmp/records.json + jq -e '[.[] | select(.recordType == "AAAA" and .dnsName == "aaaa-webhook-test.example.com" and (.targets[0] == "2001:db8::1"))] | length == 1' /tmp/records.json diff --git a/internal/designate/provider/provider.go b/internal/designate/provider/provider.go index bb7a488..7dfe226 100644 --- a/internal/designate/provider/provider.go +++ b/internal/designate/provider/provider.go @@ -139,7 +139,7 @@ func (p designateProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, e for zoneID := range managedZones { err = p.client.ForEachRecordSet(ctx, zoneID, func(recordSet *recordsets.RecordSet) error { - if recordSet.Type != endpoint.RecordTypeA && recordSet.Type != endpoint.RecordTypeTXT && recordSet.Type != endpoint.RecordTypeCNAME { + if recordSet.Type != endpoint.RecordTypeA && recordSet.Type != endpoint.RecordTypeAAAA && recordSet.Type != endpoint.RecordTypeTXT && recordSet.Type != endpoint.RecordTypeCNAME { return nil } diff --git a/internal/designate/provider/provider_test.go b/internal/designate/provider/provider_test.go index b3c65d2..a82855e 100644 --- a/internal/designate/provider/provider_test.go +++ b/internal/designate/provider/provider_test.go @@ -297,6 +297,11 @@ func TestDesignateRecords(t *testing.T) { TTL: 120, Records: []string{"10.1.1.2"}, }) + rs15ID, _ := client.CreateRecordSet(ctx, zone1ID, recordsets.CreateOpts{ + Name: "www6.example.com.", + Type: endpoint.RecordTypeAAAA, + Records: []string{"2001:db8::1"}, + }) zone2ID := client.AddZone(ctx, zones.Zone{ Name: "test.net.", @@ -345,6 +350,16 @@ func TestDesignateRecords(t *testing.T) { designateOriginalRecords: "10.1.1.2", }, }, + { + DNSName: "www6.example.com", + RecordType: endpoint.RecordTypeAAAA, + Targets: endpoint.Targets{"2001:db8::1"}, + Labels: map[string]string{ + designateRecordSetID: rs15ID, + designateZoneID: zone1ID, + designateOriginalRecords: "2001:db8::1", + }, + }, { DNSName: "srv.test.net", RecordType: endpoint.RecordTypeA,