From 41c9a79791ea4400599051f06e394f530a5ae26f Mon Sep 17 00:00:00 2001 From: Thibaut R Date: Tue, 6 May 2025 18:48:43 +0200 Subject: [PATCH 01/18] Reintroduce base config for txt owner migration # Conflicts: # controller/execute.go # registry/txt.go # Conflicts: # pkg/apis/externaldns/types.go --- controller/execute.go | 2 +- pkg/apis/externaldns/types.go | 6 ++++++ registry/txt.go | 13 ++++++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/controller/execute.go b/controller/execute.go index 5782db8daa..6310fe65c6 100644 --- a/controller/execute.go +++ b/controller/execute.go @@ -418,7 +418,7 @@ func selectRegistry(cfg *externaldns.Config, p provider.Provider) (registry.Regi case "noop": r, err = registry.NewNoopRegistry(p) case "txt": - r, err = registry.NewTXTRegistry(p, cfg.TXTPrefix, cfg.TXTSuffix, cfg.TXTOwnerID, cfg.TXTCacheInterval, cfg.TXTWildcardReplacement, cfg.ManagedDNSRecordTypes, cfg.ExcludeDNSRecordTypes, cfg.TXTEncryptEnabled, []byte(cfg.TXTEncryptAESKey)) + r, err = registry.NewTXTRegistry(p, cfg.TXTPrefix, cfg.TXTSuffix, cfg.TXTOwnerID, cfg.TXTCacheInterval, cfg.TXTWildcardReplacement, cfg.ManagedDNSRecordTypes, cfg.ExcludeDNSRecordTypes, cfg.TXTEncryptEnabled, []byte(cfg.TXTEncryptAESKey), cfg.TXTOwnerMigrate, cfg.TXTOwnerOld) case "aws-sd": r, err = registry.NewAWSSDRegistry(p, cfg.TXTOwnerID) default: diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index d9f8e6efb5..fd131e4bdb 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -142,6 +142,8 @@ type Config struct { Policy string Registry string TXTOwnerID string + TXTOwnerOld string + TXTOwnerMigrate bool TXTPrefix string TXTSuffix string TXTEncryptEnabled bool @@ -371,6 +373,8 @@ var defaultConfig = &Config{ TXTEncryptAESKey: "", TXTEncryptEnabled: false, TXTOwnerID: "default", + TXTOwnerOld: "default", + TXTOwnerMigrate: false, TXTPrefix: "", TXTSuffix: "", TXTWildcardReplacement: "", @@ -782,6 +786,8 @@ func bindFlags(b FlagBinder, cfg *Config) { b.StringVar("txt-wildcard-replacement", "When using the TXT registry, a custom string that's used instead of an asterisk for TXT records corresponding to wildcard DNS records (optional)", defaultConfig.TXTWildcardReplacement, &cfg.TXTWildcardReplacement) b.BoolVar("txt-encrypt-enabled", "When using the TXT registry, set if TXT records should be encrypted before stored (default: disabled)", defaultConfig.TXTEncryptEnabled, &cfg.TXTEncryptEnabled) b.StringVar("txt-encrypt-aes-key", "When using the TXT registry, set TXT record decryption and encryption 32 byte aes key (required when --txt-encrypt=true)", defaultConfig.TXTEncryptAESKey, &cfg.TXTEncryptAESKey) + b.BoolVar("migrate-txt-owner", "When enabled, modify the previous txt-owner to the current txt-owner (default: disabled)", defaultConfig.TXTOwnerMigrate, &cfg.TXTOwnerMigrate) + b.StringVar("from-txt-owner", "Old txt-owner-id that needs to be overwritten (default: default)", defaultConfig.TXTOwnerOld, &cfg.TXTOwnerOld) b.StringVar("dynamodb-region", "When using the DynamoDB registry, the AWS region of the DynamoDB table (optional)", cfg.AWSDynamoDBRegion, &cfg.AWSDynamoDBRegion) b.StringVar("dynamodb-table", "When using the DynamoDB registry, the name of the DynamoDB table (default: \"external-dns\")", defaultConfig.AWSDynamoDBTable, &cfg.AWSDynamoDBTable) diff --git a/registry/txt.go b/registry/txt.go index b428cfe9ad..8b6c4e4f8e 100644 --- a/registry/txt.go +++ b/registry/txt.go @@ -60,6 +60,10 @@ type TXTRegistry struct { txtEncryptEnabled bool txtEncryptAESKey []byte + //Handle Owner ID migration + isMigrationEnabled bool + oldOwnerID string + // existingTXTs is the TXT records that already exist in the zone so that // ApplyChanges() can skip re-creating them. See the struct below for details. existingTXTs *existingTXTs @@ -114,7 +118,8 @@ func (im *existingTXTs) reset() { func NewTXTRegistry(provider provider.Provider, txtPrefix, txtSuffix, ownerID string, cacheInterval time.Duration, txtWildcardReplacement string, managedRecordTypes, excludeRecordTypes []string, - txtEncryptEnabled bool, txtEncryptAESKey []byte) (*TXTRegistry, error) { + txtEncryptEnabled bool, txtEncryptAESKey []byte, + isMigrationEnabled bool, oldOwnerID string) (*TXTRegistry, error) { if ownerID == "" { return nil, errors.New("owner id cannot be empty") } @@ -148,6 +153,8 @@ func NewTXTRegistry(provider provider.Provider, txtPrefix, txtSuffix, ownerID st excludeRecordTypes: excludeRecordTypes, txtEncryptEnabled: txtEncryptEnabled, txtEncryptAESKey: txtEncryptAESKey, + isMigrationEnabled: isMigrationEnabled, + oldOwnerID: oldOwnerID, existingTXTs: newExistingTXTs(), }, nil } @@ -315,6 +322,10 @@ func (im *TXTRegistry) ApplyChanges(ctx context.Context, changes *plan.Changes) UpdateOld: endpoint.FilterEndpointsByOwnerID(im.ownerID, changes.UpdateOld), Delete: endpoint.FilterEndpointsByOwnerID(im.ownerID, changes.Delete), } + if im.isMigrationEnabled { + filteredChanges.UpdateOld = append(filteredChanges.UpdateOld, endpoint.FilterEndpointsByOwnerID(im.oldOwnerID, changes.UpdateOld)...) + filteredChanges.Delete = append(filteredChanges.Delete, endpoint.FilterEndpointsByOwnerID(im.oldOwnerID, changes.Delete)...) + } for _, r := range filteredChanges.Create { if r.Labels == nil { r.Labels = make(map[string]string) From b91622122a6250bef92cbf95e4f0c7f4b90ff03c Mon Sep 17 00:00:00 2001 From: Thibaut R Date: Tue, 6 May 2025 19:00:55 +0200 Subject: [PATCH 02/18] Added label update logic and fixed existing tests --- registry/txt.go | 8 ++++++ registry/txt_test.go | 68 ++++++++++++++++++++++---------------------- 2 files changed, 42 insertions(+), 34 deletions(-) diff --git a/registry/txt.go b/registry/txt.go index 8b6c4e4f8e..7e2e36ee0a 100644 --- a/registry/txt.go +++ b/registry/txt.go @@ -348,6 +348,10 @@ func (im *TXTRegistry) ApplyChanges(ctx context.Context, changes *plan.Changes) if im.cacheInterval > 0 { im.removeFromCache(r) } + + if im.isMigrationEnabled && r.Labels[endpoint.OwnerLabelKey] == im.oldOwnerID { + r.Labels[endpoint.OwnerLabelKey] = im.ownerID + } } // make sure TXT records are consistently updated as well @@ -359,6 +363,10 @@ func (im *TXTRegistry) ApplyChanges(ctx context.Context, changes *plan.Changes) if im.cacheInterval > 0 { im.removeFromCache(r) } + + if im.isMigrationEnabled && r.Labels[endpoint.OwnerLabelKey] == im.oldOwnerID { + r.Labels[endpoint.OwnerLabelKey] = im.ownerID + } } // make sure TXT records are consistently updated as well diff --git a/registry/txt_test.go b/registry/txt_test.go index 05cdd030d2..1c8035894c 100644 --- a/registry/txt_test.go +++ b/registry/txt_test.go @@ -49,20 +49,20 @@ func TestTXTRegistry(t *testing.T) { func testTXTRegistryNew(t *testing.T) { p := inmemory.NewInMemoryProvider() - _, err := NewTXTRegistry(p, "txt", "", "", time.Hour, "", []string{}, []string{}, false, nil) + _, err := NewTXTRegistry(p, "txt", "", "", time.Hour, "", []string{}, []string{}, false, nil, false, "") require.Error(t, err) - _, err = NewTXTRegistry(p, "", "txt", "", time.Hour, "", []string{}, []string{}, false, nil) + _, err = NewTXTRegistry(p, "", "txt", "", time.Hour, "", []string{}, []string{}, false, nil, false, "") require.Error(t, err) - r, err := NewTXTRegistry(p, "txt", "", "owner", time.Hour, "", []string{}, []string{}, false, nil) + r, err := NewTXTRegistry(p, "txt", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") require.NoError(t, err) assert.Equal(t, p, r.provider) - r, err = NewTXTRegistry(p, "", "txt", "owner", time.Hour, "", []string{}, []string{}, false, nil) + r, err = NewTXTRegistry(p, "", "txt", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") require.NoError(t, err) - _, err = NewTXTRegistry(p, "txt", "txt", "owner", time.Hour, "", []string{}, []string{}, false, nil) + _, err = NewTXTRegistry(p, "txt", "txt", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") require.Error(t, err) _, ok := r.mapper.(affixNameMapper) @@ -71,16 +71,16 @@ func testTXTRegistryNew(t *testing.T) { assert.Equal(t, p, r.provider) aesKey := []byte(";k&l)nUC/33:{?d{3)54+,AD?]SX%yh^") - _, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil) + _, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") require.NoError(t, err) - _, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, aesKey) + _, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, aesKey, false, "") require.NoError(t, err) - _, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, nil) + _, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, nil, false, "") require.Error(t, err) - r, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, aesKey) + r, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, aesKey, false, "") require.NoError(t, err) _, ok = r.mapper.(affixNameMapper) @@ -228,13 +228,13 @@ func testTXTRegistryRecordsPrefixed(t *testing.T) { }, } - r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "wc", []string{}, []string{}, false, nil) + r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "wc", []string{}, []string{}, false, nil, false, "") records, _ := r.Records(ctx) assert.True(t, testutils.SameEndpoints(records, expectedRecords)) // Ensure prefix is case-insensitive - r, _ = NewTXTRegistry(p, "TxT.", "", "owner", time.Hour, "wc", []string{}, []string{}, false, nil) + r, _ = NewTXTRegistry(p, "TxT.", "", "owner", time.Hour, "wc", []string{}, []string{}, false, nil, false, "") records, _ = r.Records(ctx) assert.True(t, testutils.SameEndpoints(records, expectedRecords)) @@ -363,13 +363,13 @@ func testTXTRegistryRecordsSuffixed(t *testing.T) { }, } - r, _ := NewTXTRegistry(p, "", "-txt", "owner", time.Hour, "", []string{}, []string{}, false, nil) + r, _ := NewTXTRegistry(p, "", "-txt", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") records, _ := r.Records(ctx) assert.True(t, testutils.SameEndpoints(records, expectedRecords)) // Ensure prefix is case-insensitive - r, _ = NewTXTRegistry(p, "", "-TxT", "owner", time.Hour, "", []string{}, []string{}, false, nil) + r, _ = NewTXTRegistry(p, "", "-TxT", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") records, _ = r.Records(ctx) assert.True(t, testutils.SameEndpointLabels(records, expectedRecords)) @@ -490,7 +490,7 @@ func testTXTRegistryRecordsNoPrefix(t *testing.T) { }, } - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil) + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") records, _ := r.Records(ctx) assert.True(t, testutils.SameEndpoints(records, expectedRecords)) @@ -527,12 +527,12 @@ func testTXTRegistryRecordsPrefixedTemplated(t *testing.T) { }, } - r, _ := NewTXTRegistry(p, "txt-%{record_type}.", "", "owner", time.Hour, "wc", []string{}, []string{}, false, nil) + r, _ := NewTXTRegistry(p, "txt-%{record_type}.", "", "owner", time.Hour, "wc", []string{}, []string{}, false, nil, false, "") records, _ := r.Records(ctx) assert.True(t, testutils.SameEndpoints(records, expectedRecords)) - r, _ = NewTXTRegistry(p, "TxT-%{record_type}.", "", "owner", time.Hour, "wc", []string{}, []string{}, false, nil) + r, _ = NewTXTRegistry(p, "TxT-%{record_type}.", "", "owner", time.Hour, "wc", []string{}, []string{}, false, nil, false, "") records, _ = r.Records(ctx) assert.True(t, testutils.SameEndpoints(records, expectedRecords)) @@ -569,12 +569,12 @@ func testTXTRegistryRecordsSuffixedTemplated(t *testing.T) { }, } - r, _ := NewTXTRegistry(p, "", "txt%{record_type}", "owner", time.Hour, "wc", []string{}, []string{}, false, nil) + r, _ := NewTXTRegistry(p, "", "txt%{record_type}", "owner", time.Hour, "wc", []string{}, []string{}, false, nil, false, "") records, _ := r.Records(ctx) assert.True(t, testutils.SameEndpoints(records, expectedRecords)) - r, _ = NewTXTRegistry(p, "", "TxT%{record_type}", "owner", time.Hour, "wc", []string{}, []string{}, false, nil) + r, _ = NewTXTRegistry(p, "", "TxT%{record_type}", "owner", time.Hour, "wc", []string{}, []string{}, false, nil, false, "") records, _ = r.Records(ctx) assert.True(t, testutils.SameEndpoints(records, expectedRecords)) @@ -617,7 +617,7 @@ func testTXTRegistryApplyChangesWithPrefix(t *testing.T) { newEndpointWithOwner("txt.cname-multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "").WithSetIdentifier("test-set-2"), }, }) - r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "", []string{}, []string{}, false, nil) + r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") changes := &plan.Changes{ Create: []*endpoint.Endpoint{ @@ -698,7 +698,7 @@ func testTXTRegistryApplyChangesWithTemplatedPrefix(t *testing.T) { p.ApplyChanges(ctx, &plan.Changes{ Create: []*endpoint.Endpoint{}, }) - r, _ := NewTXTRegistry(p, "prefix%{record_type}.", "", "owner", time.Hour, "", []string{}, []string{}, false, nil) + r, _ := NewTXTRegistry(p, "prefix%{record_type}.", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") changes := &plan.Changes{ Create: []*endpoint.Endpoint{ newEndpointWithOwnerResource("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "", "ingress/default/my-ingress"), @@ -741,7 +741,7 @@ func testTXTRegistryApplyChangesWithTemplatedSuffix(t *testing.T) { p.OnApplyChanges = func(ctx context.Context, got *plan.Changes) { assert.Equal(t, ctxEndpoints, ctx.Value(provider.RecordsContextKey)) } - r, _ := NewTXTRegistry(p, "", "-%{record_type}suffix", "owner", time.Hour, "", []string{}, []string{}, false, nil) + r, _ := NewTXTRegistry(p, "", "-%{record_type}suffix", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") changes := &plan.Changes{ Create: []*endpoint.Endpoint{ newEndpointWithOwnerResource("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "", "ingress/default/my-ingress"), @@ -806,7 +806,7 @@ func testTXTRegistryApplyChangesWithSuffix(t *testing.T) { newEndpointWithOwner("cname-multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "").WithSetIdentifier("test-set-2"), }, }) - r, _ := NewTXTRegistry(p, "", "-txt", "owner", time.Hour, "wildcard", []string{}, []string{}, false, nil) + r, _ := NewTXTRegistry(p, "", "-txt", "owner", time.Hour, "wildcard", []string{}, []string{}, false, nil, false, "") changes := &plan.Changes{ Create: []*endpoint.Endpoint{ @@ -900,7 +900,7 @@ func testTXTRegistryApplyChangesNoPrefix(t *testing.T) { newEndpointWithOwner("cname-foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), }, }) - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil) + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") changes := &plan.Changes{ Create: []*endpoint.Endpoint{ @@ -1058,7 +1058,7 @@ func testTXTRegistryMissingRecordsNoPrefix(t *testing.T) { }, } - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "wc", []string{endpoint.RecordTypeCNAME, endpoint.RecordTypeA, endpoint.RecordTypeNS}, []string{}, false, nil) + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "wc", []string{endpoint.RecordTypeCNAME, endpoint.RecordTypeA, endpoint.RecordTypeNS}, []string{}, false, nil, false, "") records, _ := r.Records(ctx) assert.True(t, testutils.SameEndpoints(records, expectedRecords)) @@ -1168,7 +1168,7 @@ func testTXTRegistryMissingRecordsWithPrefix(t *testing.T) { }, } - r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "wc", []string{endpoint.RecordTypeCNAME, endpoint.RecordTypeA, endpoint.RecordTypeNS, endpoint.RecordTypeTXT}, []string{}, false, nil) + r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "wc", []string{endpoint.RecordTypeCNAME, endpoint.RecordTypeA, endpoint.RecordTypeNS, endpoint.RecordTypeTXT}, []string{}, false, nil, false, "") records, _ := r.Records(ctx) assert.True(t, testutils.SameEndpoints(records, expectedRecords)) @@ -1463,7 +1463,7 @@ func TestNewTXTScheme(t *testing.T) { newEndpointWithOwner("cname-foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), }, }) - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil) + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") changes := &plan.Changes{ Create: []*endpoint.Endpoint{ @@ -1528,7 +1528,7 @@ func TestGenerateTXT(t *testing.T) { } p := inmemory.NewInMemoryProvider() p.CreateZone(testZone) - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil) + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") gotTXT := r.generateTXTRecord(record) assert.Equal(t, expectedTXT, gotTXT) } @@ -1547,7 +1547,7 @@ func TestGenerateTXTForAAAA(t *testing.T) { } p := inmemory.NewInMemoryProvider() p.CreateZone(testZone) - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil) + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") gotTXT := r.generateTXTRecord(record) assert.Equal(t, expectedTXT, gotTXT) } @@ -1564,7 +1564,7 @@ func TestFailGenerateTXT(t *testing.T) { expectedTXT := []*endpoint.Endpoint{} p := inmemory.NewInMemoryProvider() p.CreateZone(testZone) - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil) + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") gotTXT := r.generateTXTRecord(cnameRecord) assert.Equal(t, expectedTXT, gotTXT) } @@ -1582,7 +1582,7 @@ func TestTXTRegistryApplyChangesEncrypt(t *testing.T) { }, }) - r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "", []string{}, []string{}, true, []byte("12345678901234567890123456789012")) + r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "", []string{}, []string{}, true, []byte("12345678901234567890123456789012"), false, "") records, _ := r.Records(ctx) changes := &plan.Changes{ Delete: records, @@ -1628,7 +1628,7 @@ func TestMultiClusterDifferentRecordTypeOwnership(t *testing.T) { }, }) - r, _ := NewTXTRegistry(p, "_owner.", "", "bar", time.Hour, "", []string{}, []string{}, false, nil) + r, _ := NewTXTRegistry(p, "_owner.", "", "bar", time.Hour, "", []string{}, []string{}, false, nil, false, "") records, _ := r.Records(ctx) // new cluster has same ingress host as other cluster and uses CNAME ingress address @@ -1713,7 +1713,7 @@ func TestGenerateTXTRecordWithNewFormatOnly(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil) + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") records := r.generateTXTRecord(tc.endpoint) assert.Len(t, records, tc.expectedRecords, tc.description) @@ -1742,7 +1742,7 @@ func TestApplyChangesWithNewFormatOnly(t *testing.T) { p.CreateZone(testZone) ctx := context.Background() - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil) + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") changes := &plan.Changes{ Create: []*endpoint.Endpoint{ @@ -1790,7 +1790,7 @@ func TestTXTRegistryRecordsWithEmptyTargets(t *testing.T) { }, }) - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil) + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") hook := testutils.LogsUnderTestWithLogLevel(log.ErrorLevel, t) records, err := r.Records(ctx) require.NoError(t, err) From 104ab1f686f9294c793cd1f6901cd5774974f95c Mon Sep 17 00:00:00 2001 From: Thibaut R Date: Wed, 7 May 2025 21:18:08 +0200 Subject: [PATCH 03/18] Fixed existing declaration in tests, re introduced tests for new flag, regened flags.md from make --- docs/flags.md | 2 ++ registry/txt_encryption_test.go | 10 +++--- registry/txt_test.go | 62 +++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 5 deletions(-) diff --git a/docs/flags.md b/docs/flags.md index 61ce893406..889033f09f 100644 --- a/docs/flags.md +++ b/docs/flags.md @@ -159,6 +159,8 @@ | `--policy=sync` | Modify how DNS records are synchronized between sources and providers (default: sync, options: sync, upsert-only, create-only) | | `--registry=txt` | The registry implementation to use to keep track of DNS record ownership (default: txt, options: txt, noop, dynamodb, aws-sd) | | `--txt-owner-id="default"` | When using the TXT or DynamoDB registry, a name that identifies this instance of ExternalDNS (default: default) | +| `--from-txt-owner=FROM-TXT-OWNER` | Old txt-owner-id that needs to be overwritten (default: default) | +| `--[no-]migrate-txt-owner` | When enabled, modify the previous txt-owner to the current txt-owner (default: disabled) | | `--txt-prefix=""` | When using the TXT registry, a custom string that's prefixed to each ownership DNS record (optional). Could contain record type template like '%{record_type}-prefix-'. Mutual exclusive with txt-suffix! | | `--txt-suffix=""` | When using the TXT registry, a custom string that's suffixed to the host portion of each ownership DNS record (optional). Could contain record type template like '-%{record_type}-suffix'. Mutual exclusive with txt-prefix! | | `--txt-wildcard-replacement=""` | When using the TXT registry, a custom string that's used instead of an asterisk for TXT records corresponding to wildcard DNS records (optional) | diff --git a/registry/txt_encryption_test.go b/registry/txt_encryption_test.go index 1b7f9f5c9e..828dd65c7c 100644 --- a/registry/txt_encryption_test.go +++ b/registry/txt_encryption_test.go @@ -61,7 +61,7 @@ func TestNewTXTRegistryEncryptionConfig(t *testing.T) { }, } for _, test := range tests { - actual, err := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "", []string{}, []string{}, test.encEnabled, test.aesKeyRaw) + actual, err := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "", []string{}, []string{}, test.encEnabled, test.aesKeyRaw, false, "") if test.errorExpected { require.Error(t, err) } else { @@ -107,7 +107,7 @@ func TestGenerateTXTGenerateTextRecordEncryptionWihDecryption(t *testing.T) { for _, k := range withEncryptionKeys { t.Run(fmt.Sprintf("key '%s' with decrypted result '%s'", k, test.decrypted), func(t *testing.T) { key := []byte(k) - r, err := NewTXTRegistry(p, "", "", "owner", time.Minute, "", []string{}, []string{}, true, key) + r, err := NewTXTRegistry(p, "", "", "owner", time.Minute, "", []string{}, []string{}, true, key, false, "") assert.NoError(t, err, "Error creating TXT registry") txtRecords := r.generateTXTRecord(test.record) assert.Len(t, txtRecords, len(test.record.Targets)) @@ -144,7 +144,7 @@ func TestApplyRecordsWithEncryption(t *testing.T) { key := []byte("ZPitL0NGVQBZbTD6DwXJzD8RiStSazzYXQsdUowLURY=") - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, key) + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, key, false, "") _ = r.ApplyChanges(ctx, &plan.Changes{ Create: []*endpoint.Endpoint{ @@ -202,7 +202,7 @@ func TestApplyRecordsWithEncryptionKeyChanged(t *testing.T) { } for _, key := range withEncryptionKeys { - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, []byte(key)) + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, []byte(key), false, "") _ = r.ApplyChanges(ctx, &plan.Changes{ Create: []*endpoint.Endpoint{ newEndpointWithOwner("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner"), @@ -232,7 +232,7 @@ func TestApplyRecordsOnEncryptionKeyChangeWithKeyIdLabel(t *testing.T) { } for i, key := range withEncryptionKeys { - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, []byte(key)) + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, []byte(key), false, "") keyId := fmt.Sprintf("key-id-%d", i) changes := []*endpoint.Endpoint{ newEndpointWithOwnerAndOwnedRecordWithKeyIDLabel("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner", "", keyId), diff --git a/registry/txt_test.go b/registry/txt_test.go index 1c8035894c..dfa5673c91 100644 --- a/registry/txt_test.go +++ b/registry/txt_test.go @@ -2028,3 +2028,65 @@ func TestTXTRegistryRecreatesMissingRecords(t *testing.T) { } } } + +func TestTXTRecordMigration(t *testing.T) { + ctx := context.Background() + p := inmemory.NewInMemoryProvider() + p.CreateZone(testZone) + p.ApplyChanges(ctx, &plan.Changes{ + Create: []*endpoint.Endpoint{ + // records on cluster using A record for ingress address + newEndpointWithOwner("bar.test-zone.example.org", "1.2.3.4", endpoint.RecordTypeA, "foo"), + }, + }) + + r, _ := NewTXTRegistry(p, "%{record_type}-", "", "foo", time.Hour, "", []string{}, []string{}, false, nil, false, false, "") + createdRecords, _ := p.Records(ctx) + // Safe to use index here + txtRecords := r.generateTXTRecord(createdRecords[0]) + + expectedTXTRecords := []*endpoint.Endpoint{ + { + DNSName: "a-bar.test-zone.example.org", + Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=foo\""}, + RecordType: endpoint.RecordTypeTXT, + Labels: map[string]string{ + endpoint.OwnedRecordLabelKey: "bar.test-zone.example.org", + }, + }, + } + + assert.Equal(t, expectedTXTRecords, txtRecords) + + p.ApplyChanges(ctx, &plan.Changes{ + UpdateNew: []*endpoint.Endpoint{ + // records on cluster using A record for ingress address + newEndpointWithOwner("bar.test-zone.example.org", "1.2.3.4", endpoint.RecordTypeA, "foobar"), + }, + UpdateOld: []*endpoint.Endpoint{ + // records on cluster using A record for ingress address + newEndpointWithOwner("bar.test-zone.example.org", "1.2.3.4", endpoint.RecordTypeA, "foo"), + }, + }) + + updatedRecords, _ := p.Records(ctx) + + r, _ = NewTXTRegistry(p, "%{record_type}-", "", "foobar", time.Hour, "", []string{}, []string{}, false, nil, false, true, "foo") + + migratedTXTRecords := r.generateTXTRecord(updatedRecords[0]) + + expectedFinalTXT := []*endpoint.Endpoint{ + { + DNSName: "a-bar.test-zone.example.org", + Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=foobar\""}, + RecordType: endpoint.RecordTypeTXT, + Labels: map[string]string{ + endpoint.OwnedRecordLabelKey: "bar.test-zone.example.org", + }, + }, + } + + assert.Len(t, migratedTXTRecords, 1) + assert.Equal(t, migratedTXTRecords, expectedFinalTXT) + +} From 0c0928b51641e0b7252279b497d9be3b59e3bc57 Mon Sep 17 00:00:00 2001 From: Thibaut R Date: Fri, 9 May 2025 15:55:41 +0200 Subject: [PATCH 04/18] Fixed tests logic and target expression evaluation, fixed update of label in the TXT registry process --- registry/txt.go | 16 +++++----------- registry/txt_test.go | 42 +++++++++++++----------------------------- 2 files changed, 18 insertions(+), 40 deletions(-) diff --git a/registry/txt.go b/registry/txt.go index 7e2e36ee0a..1acaa58bba 100644 --- a/registry/txt.go +++ b/registry/txt.go @@ -259,6 +259,10 @@ func (im *TXTRegistry) Records(ctx context.Context) ([]*endpoint.Endpoint, error } } + if im.isMigrationEnabled && ep.Labels[endpoint.OwnerLabelKey] == im.oldOwnerID { + ep.Labels[endpoint.OwnerLabelKey] = im.ownerID + } + // Handle the migration of TXT records created before the new format (introduced in v0.12.0). // The migration is done for the TXT records owned by this instance only. if len(txtRecordsMap) > 0 && ep.Labels[endpoint.OwnerLabelKey] == im.ownerID { @@ -322,10 +326,7 @@ func (im *TXTRegistry) ApplyChanges(ctx context.Context, changes *plan.Changes) UpdateOld: endpoint.FilterEndpointsByOwnerID(im.ownerID, changes.UpdateOld), Delete: endpoint.FilterEndpointsByOwnerID(im.ownerID, changes.Delete), } - if im.isMigrationEnabled { - filteredChanges.UpdateOld = append(filteredChanges.UpdateOld, endpoint.FilterEndpointsByOwnerID(im.oldOwnerID, changes.UpdateOld)...) - filteredChanges.Delete = append(filteredChanges.Delete, endpoint.FilterEndpointsByOwnerID(im.oldOwnerID, changes.Delete)...) - } + for _, r := range filteredChanges.Create { if r.Labels == nil { r.Labels = make(map[string]string) @@ -348,10 +349,6 @@ func (im *TXTRegistry) ApplyChanges(ctx context.Context, changes *plan.Changes) if im.cacheInterval > 0 { im.removeFromCache(r) } - - if im.isMigrationEnabled && r.Labels[endpoint.OwnerLabelKey] == im.oldOwnerID { - r.Labels[endpoint.OwnerLabelKey] = im.ownerID - } } // make sure TXT records are consistently updated as well @@ -364,9 +361,6 @@ func (im *TXTRegistry) ApplyChanges(ctx context.Context, changes *plan.Changes) im.removeFromCache(r) } - if im.isMigrationEnabled && r.Labels[endpoint.OwnerLabelKey] == im.oldOwnerID { - r.Labels[endpoint.OwnerLabelKey] = im.ownerID - } } // make sure TXT records are consistently updated as well diff --git a/registry/txt_test.go b/registry/txt_test.go index dfa5673c91..23645a4f58 100644 --- a/registry/txt_test.go +++ b/registry/txt_test.go @@ -2033,60 +2033,44 @@ func TestTXTRecordMigration(t *testing.T) { ctx := context.Background() p := inmemory.NewInMemoryProvider() p.CreateZone(testZone) - p.ApplyChanges(ctx, &plan.Changes{ + + r, _ := NewTXTRegistry(p, "%{record_type}-", "", "foo", time.Hour, "", []string{}, []string{}, false, nil, false, false, "") + + r.ApplyChanges(ctx, &plan.Changes{ Create: []*endpoint.Endpoint{ // records on cluster using A record for ingress address - newEndpointWithOwner("bar.test-zone.example.org", "1.2.3.4", endpoint.RecordTypeA, "foo"), + newEndpointWithOwnerAndLabels("bar.test-zone.example.org", "1.2.3.4", endpoint.RecordTypeA, "foo", endpoint.Labels{endpoint.OwnerLabelKey: "owner"}), }, }) - r, _ := NewTXTRegistry(p, "%{record_type}-", "", "foo", time.Hour, "", []string{}, []string{}, false, nil, false, false, "") - createdRecords, _ := p.Records(ctx) - // Safe to use index here - txtRecords := r.generateTXTRecord(createdRecords[0]) + createdRecords, _ := r.Records(ctx) + + newTXTRecord := r.generateTXTRecord(createdRecords[0]) expectedTXTRecords := []*endpoint.Endpoint{ { DNSName: "a-bar.test-zone.example.org", Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=foo\""}, RecordType: endpoint.RecordTypeTXT, - Labels: map[string]string{ - endpoint.OwnedRecordLabelKey: "bar.test-zone.example.org", - }, }, } - assert.Equal(t, expectedTXTRecords, txtRecords) - - p.ApplyChanges(ctx, &plan.Changes{ - UpdateNew: []*endpoint.Endpoint{ - // records on cluster using A record for ingress address - newEndpointWithOwner("bar.test-zone.example.org", "1.2.3.4", endpoint.RecordTypeA, "foobar"), - }, - UpdateOld: []*endpoint.Endpoint{ - // records on cluster using A record for ingress address - newEndpointWithOwner("bar.test-zone.example.org", "1.2.3.4", endpoint.RecordTypeA, "foo"), - }, - }) - - updatedRecords, _ := p.Records(ctx) + assert.Equal(t, expectedTXTRecords[0].Targets, newTXTRecord[0].Targets) r, _ = NewTXTRegistry(p, "%{record_type}-", "", "foobar", time.Hour, "", []string{}, []string{}, false, nil, false, true, "foo") - migratedTXTRecords := r.generateTXTRecord(updatedRecords[0]) + updatedRecords, _ := r.Records(ctx) + + updatedTXTRecord := r.generateTXTRecord(updatedRecords[0]) expectedFinalTXT := []*endpoint.Endpoint{ { DNSName: "a-bar.test-zone.example.org", Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=foobar\""}, RecordType: endpoint.RecordTypeTXT, - Labels: map[string]string{ - endpoint.OwnedRecordLabelKey: "bar.test-zone.example.org", - }, }, } - assert.Len(t, migratedTXTRecords, 1) - assert.Equal(t, migratedTXTRecords, expectedFinalTXT) + assert.Equal(t, updatedTXTRecord[0].Targets, expectedFinalTXT[0].Targets) } From 6660700f3ab9d30a695cf1329d67d136f2714855 Mon Sep 17 00:00:00 2001 From: Thibaut R Date: Sun, 22 Jun 2025 09:02:52 +0200 Subject: [PATCH 05/18] Set Old owner id var down the plan to calculate changes correctly --- controller/controller.go | 3 +++ controller/execute.go | 1 + plan/plan.go | 5 ++++- registry/txt.go | 3 +++ 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/controller/controller.go b/controller/controller.go index bdbddfb64f..f0a3fbde88 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -182,6 +182,8 @@ type Controller struct { ExcludeRecordTypes []string // MinEventSyncInterval is used as a window for batching events MinEventSyncInterval time.Duration + // Old txt-owner value we need to migrate from + TXTOwnerOld string } // RunOnce runs a single iteration of a reconciliation loop. @@ -236,6 +238,7 @@ func (c *Controller) RunOnce(ctx context.Context) error { ManagedRecords: c.ManagedRecordTypes, ExcludeRecords: c.ExcludeRecordTypes, OwnerID: c.Registry.OwnerID(), + OldOwnerId: c.TXTOwnerOld, } plan = plan.Calculate() diff --git a/controller/execute.go b/controller/execute.go index 6310fe65c6..fdf1ee6970 100644 --- a/controller/execute.go +++ b/controller/execute.go @@ -382,6 +382,7 @@ func buildController( ManagedRecordTypes: cfg.ManagedDNSRecordTypes, ExcludeRecordTypes: cfg.ExcludeDNSRecordTypes, MinEventSyncInterval: cfg.MinEventSyncInterval, + TXTOwnerOld: cfg.TXTOwnerOld, EventEmitter: eventEmitter, }, nil } diff --git a/plan/plan.go b/plan/plan.go index 299623776b..fd9ed2bda9 100644 --- a/plan/plan.go +++ b/plan/plan.go @@ -52,6 +52,8 @@ type Plan struct { ExcludeRecords []string // OwnerID of records to manage OwnerID string + // Old owner ID we migrate from + OldOwnerId string } // Changes holds lists of actions to be executed by dns providers @@ -224,7 +226,8 @@ func (p *Plan) Calculate() *Plan { if records.current != nil && len(records.candidates) > 0 { update := t.resolver.ResolveUpdate(records.current, records.candidates) - if shouldUpdateTTL(update, records.current) || targetChanged(update, records.current) || p.shouldUpdateProviderSpecific(update, records.current) { + if shouldUpdateTTL(update, records.current) || targetChanged(update, records.current) || p.shouldUpdateProviderSpecific(update, records.current) || + len(p.OldOwnerId) != 0 && records.current.Labels[endpoint.OwnerLabelKey] != p.OldOwnerId { inheritOwner(records.current, update) changes.UpdateNew = append(changes.UpdateNew, update) changes.UpdateOld = append(changes.UpdateOld, records.current) diff --git a/registry/txt.go b/registry/txt.go index 1acaa58bba..6dd139cc47 100644 --- a/registry/txt.go +++ b/registry/txt.go @@ -307,6 +307,9 @@ func (im *TXTRegistry) generateTXTRecordWithFilter(r *endpoint.Endpoint, filter if txtNew != nil { txtNew.WithSetIdentifier(r.SetIdentifier) txtNew.Labels[endpoint.OwnedRecordLabelKey] = r.DNSName + if im.isMigrationEnabled && r.Labels[endpoint.OwnerLabelKey] == im.oldOwnerID { + r.Labels[endpoint.OwnerLabelKey] = im.ownerID + } txtNew.ProviderSpecific = r.ProviderSpecific if filter(txtNew) { endpoints = append(endpoints, txtNew) From 25a733e0e08909e6bb819d3bebb1dcd615b66040 Mon Sep 17 00:00:00 2001 From: Thibaut R Date: Sun, 22 Jun 2025 09:21:29 +0200 Subject: [PATCH 06/18] Lint fixes --- registry/txt.go | 1 - 1 file changed, 1 deletion(-) diff --git a/registry/txt.go b/registry/txt.go index 6dd139cc47..567707eedf 100644 --- a/registry/txt.go +++ b/registry/txt.go @@ -363,7 +363,6 @@ func (im *TXTRegistry) ApplyChanges(ctx context.Context, changes *plan.Changes) if im.cacheInterval > 0 { im.removeFromCache(r) } - } // make sure TXT records are consistently updated as well From 16ea3eeb0aa2343d7bd062ca14281b9eb1d4a7c9 Mon Sep 17 00:00:00 2001 From: Thibaut R Date: Wed, 25 Jun 2025 08:51:03 +0200 Subject: [PATCH 07/18] (wip) Code cleaning and test coverage --- plan/plan.go | 6 +++++- plan/plan_test.go | 26 ++++++++++++++++++++++++++ registry/txt.go | 4 ++++ registry/txt_test.go | 30 ++++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/plan/plan.go b/plan/plan.go index fd9ed2bda9..325a3d80fa 100644 --- a/plan/plan.go +++ b/plan/plan.go @@ -227,7 +227,7 @@ func (p *Plan) Calculate() *Plan { update := t.resolver.ResolveUpdate(records.current, records.candidates) if shouldUpdateTTL(update, records.current) || targetChanged(update, records.current) || p.shouldUpdateProviderSpecific(update, records.current) || - len(p.OldOwnerId) != 0 && records.current.Labels[endpoint.OwnerLabelKey] != p.OldOwnerId { + p.isOldOwnerIdSetAndDifferent(records.current) { inheritOwner(records.current, update) changes.UpdateNew = append(changes.UpdateNew, update) changes.UpdateOld = append(changes.UpdateOld, records.current) @@ -279,6 +279,10 @@ func (p *Plan) Calculate() *Plan { return plan } +func (p *Plan) isOldOwnerIdSetAndDifferent(current *endpoint.Endpoint) bool { + return len(p.OldOwnerId) != 0 && current.Labels[endpoint.OwnerLabelKey] != p.OldOwnerId +} + func inheritOwner(from, to *endpoint.Endpoint) { if to.Labels == nil { to.Labels = map[string]string{} diff --git a/plan/plan_test.go b/plan/plan_test.go index 10ce5a992b..acce96b27d 100644 --- a/plan/plan_test.go +++ b/plan/plan_test.go @@ -1017,6 +1017,32 @@ func (suite *PlanTestSuite) TestDualStackToSingleStack() { validateEntries(suite.T(), changes.UpdateNew, expectNoChanges) } +func (suite *PlanTestSuite) TestRecordOwnerIdMigration() { + suite.fooA5.Labels[endpoint.OwnerLabelKey] = "bar" + current := []*endpoint.Endpoint{suite.fooA5} + desired := []*endpoint.Endpoint{suite.fooA5} + expectedCreate := []*endpoint.Endpoint{} + expectedUpdateOld := []*endpoint.Endpoint{suite.fooA5} + expectedUpdateNew := []*endpoint.Endpoint{suite.fooA5} + expectedDelete := []*endpoint.Endpoint{} + + p := &Plan{ + Policies: []Policy{&SyncPolicy{}}, + Current: current, + Desired: desired, + ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}, + OwnerID: suite.fooA5.Labels[endpoint.OwnerLabelKey], + OldOwnerId: "foo", + } + + changes := p.Calculate().Changes + + validateEntries(suite.T(), changes.Create, expectedCreate) + validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) + validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) + validateEntries(suite.T(), changes.Delete, expectedDelete) +} + func TestPlan(t *testing.T) { suite.Run(t, new(PlanTestSuite)) } diff --git a/registry/txt.go b/registry/txt.go index 567707eedf..20740814cd 100644 --- a/registry/txt.go +++ b/registry/txt.go @@ -124,6 +124,10 @@ func NewTXTRegistry(provider provider.Provider, txtPrefix, txtSuffix, ownerID st return nil, errors.New("owner id cannot be empty") } + if isMigrationEnabled && oldOwnerID == "" { + return nil, errors.New("oldOwnerID cannot be empty if isMigrationEnabled is true") + } + if len(txtEncryptAESKey) == 0 { txtEncryptAESKey = nil } else if len(txtEncryptAESKey) != 32 { diff --git a/registry/txt_test.go b/registry/txt_test.go index 23645a4f58..6b7652f16c 100644 --- a/registry/txt_test.go +++ b/registry/txt_test.go @@ -83,6 +83,9 @@ func testTXTRegistryNew(t *testing.T) { r, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, aesKey, false, "") require.NoError(t, err) + _, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, nil, false, true, "") + require.Error(t, err) + _, ok = r.mapper.(affixNameMapper) assert.True(t, ok) } @@ -1533,6 +1536,33 @@ func TestGenerateTXT(t *testing.T) { assert.Equal(t, expectedTXT, gotTXT) } +func TestGenerateTXTWithMigration(t *testing.T) { + record := newEndpointWithOwner("foo.test-zone.example.org", "1.2.3.4", endpoint.RecordTypeA, "owner") + expectedTXT := []*endpoint.Endpoint{ + { + DNSName: "foo.test-zone.example.org", + Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=foobar\""}, + RecordType: endpoint.RecordTypeTXT, + Labels: map[string]string{ + endpoint.OwnedRecordLabelKey: "foo.test-zone.example.org", + }, + }, + { + DNSName: "a-foo.test-zone.example.org", + Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=foobar\""}, + RecordType: endpoint.RecordTypeTXT, + Labels: map[string]string{ + endpoint.OwnedRecordLabelKey: "foo.test-zone.example.org", + }, + }, + } + p := inmemory.NewInMemoryProvider() + p.CreateZone(testZone) + r, _ := NewTXTRegistry(p, "", "", "foobar", time.Hour, "", []string{}, []string{}, false, nil, false, true, "owner") + gotTXT := r.generateTXTRecord(record) + assert.Equal(t, expectedTXT, gotTXT) +} + func TestGenerateTXTForAAAA(t *testing.T) { record := newEndpointWithOwner("foo.test-zone.example.org", "2001:DB8::1", endpoint.RecordTypeAAAA, "owner") expectedTXT := []*endpoint.Endpoint{ From f7a450348731158813633c4c01a2e5cf27ca6797 Mon Sep 17 00:00:00 2001 From: Thibaut R Date: Fri, 27 Jun 2025 08:19:15 +0200 Subject: [PATCH 08/18] Simplified label overwriting on migration and implem tests for coverage --- registry/txt_test.go | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/registry/txt_test.go b/registry/txt_test.go index 6b7652f16c..1f35053d54 100644 --- a/registry/txt_test.go +++ b/registry/txt_test.go @@ -1538,10 +1538,10 @@ func TestGenerateTXT(t *testing.T) { func TestGenerateTXTWithMigration(t *testing.T) { record := newEndpointWithOwner("foo.test-zone.example.org", "1.2.3.4", endpoint.RecordTypeA, "owner") - expectedTXT := []*endpoint.Endpoint{ + expectedTXTBeforeMigration := []*endpoint.Endpoint{ { DNSName: "foo.test-zone.example.org", - Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=foobar\""}, + Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=owner\""}, RecordType: endpoint.RecordTypeTXT, Labels: map[string]string{ endpoint.OwnedRecordLabelKey: "foo.test-zone.example.org", @@ -1549,7 +1549,7 @@ func TestGenerateTXTWithMigration(t *testing.T) { }, { DNSName: "a-foo.test-zone.example.org", - Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=foobar\""}, + Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=owner\""}, RecordType: endpoint.RecordTypeTXT, Labels: map[string]string{ endpoint.OwnedRecordLabelKey: "foo.test-zone.example.org", @@ -1558,9 +1558,33 @@ func TestGenerateTXTWithMigration(t *testing.T) { } p := inmemory.NewInMemoryProvider() p.CreateZone(testZone) - r, _ := NewTXTRegistry(p, "", "", "foobar", time.Hour, "", []string{}, []string{}, false, nil, false, true, "owner") - gotTXT := r.generateTXTRecord(record) - assert.Equal(t, expectedTXT, gotTXT) + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, false, "") + gotTXTBeforeMigration := r.generateTXTRecord(record) + assert.Equal(t, expectedTXTBeforeMigration, gotTXTBeforeMigration) + + expectedTXTAfterMigration := []*endpoint.Endpoint{ + { + DNSName: "foo.test-zone.example.org", + Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=foobar\""}, + RecordType: endpoint.RecordTypeTXT, + Labels: map[string]string{ + endpoint.OwnedRecordLabelKey: "foo.test-zone.example.org", + }, + }, + { + DNSName: "a-foo.test-zone.example.org", + Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=foobar\""}, + RecordType: endpoint.RecordTypeTXT, + Labels: map[string]string{ + endpoint.OwnedRecordLabelKey: "foo.test-zone.example.org", + }, + }, + } + + rMigrated, _ := NewTXTRegistry(p, "", "", "foobar", time.Hour, "", []string{}, []string{}, false, nil, false, true, "owner") + gotTXTAfterMigration := rMigrated.generateTXTRecord(record) + assert.Equal(t, expectedTXTAfterMigration, gotTXTAfterMigration) + } func TestGenerateTXTForAAAA(t *testing.T) { From 1430bfaf0eae8ca41804bb76d55c0a79d46f5e20 Mon Sep 17 00:00:00 2001 From: Thibaut R Date: Fri, 29 Aug 2025 15:56:20 +0200 Subject: [PATCH 09/18] Fix tests --- registry/txt_test.go | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/registry/txt_test.go b/registry/txt_test.go index 1f35053d54..dff00a63c9 100644 --- a/registry/txt_test.go +++ b/registry/txt_test.go @@ -1539,14 +1539,6 @@ func TestGenerateTXT(t *testing.T) { func TestGenerateTXTWithMigration(t *testing.T) { record := newEndpointWithOwner("foo.test-zone.example.org", "1.2.3.4", endpoint.RecordTypeA, "owner") expectedTXTBeforeMigration := []*endpoint.Endpoint{ - { - DNSName: "foo.test-zone.example.org", - Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=owner\""}, - RecordType: endpoint.RecordTypeTXT, - Labels: map[string]string{ - endpoint.OwnedRecordLabelKey: "foo.test-zone.example.org", - }, - }, { DNSName: "a-foo.test-zone.example.org", Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=owner\""}, @@ -1563,14 +1555,6 @@ func TestGenerateTXTWithMigration(t *testing.T) { assert.Equal(t, expectedTXTBeforeMigration, gotTXTBeforeMigration) expectedTXTAfterMigration := []*endpoint.Endpoint{ - { - DNSName: "foo.test-zone.example.org", - Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=foobar\""}, - RecordType: endpoint.RecordTypeTXT, - Labels: map[string]string{ - endpoint.OwnedRecordLabelKey: "foo.test-zone.example.org", - }, - }, { DNSName: "a-foo.test-zone.example.org", Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=foobar\""}, From 4dec559c85936c3dc83cfa0d55524130815c3ea7 Mon Sep 17 00:00:00 2001 From: Thibaut R Date: Fri, 29 Aug 2025 19:54:35 +0200 Subject: [PATCH 10/18] Update txt registry doc --- docs/registry/txt.md | 88 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/docs/registry/txt.md b/docs/registry/txt.md index d538eb28ef..fbbb00bfcb 100644 --- a/docs/registry/txt.md +++ b/docs/registry/txt.md @@ -199,3 +199,91 @@ The TXT registry can optionally cache DNS records read from the provider. This c rate limits imposed by the provider. Caching is enabled by specifying a cache duration with the `--txt-cache-interval` flag. + +## OwnerID migration + +If you wish to update the owner ID of the TXT records managed by your external-dns instance, +you can set the following flags: `--migrate-txt-owner`, which will enable the migration checks +in the run loop, `--txt-owner-id=new-owner-id` and `--from-txt-owner=old-owner-id`. + +If `--migrate-txt-owner` is set, `--from-txt-owner` is mandatory. + +Example, if you had a standard deployment like so: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: external-dns +spec: + replicas: 1 + selector: + matchLabels: + app: external-dns + strategy: + type: Recreate + template: + metadata: + labels: + app: external-dns + spec: + serviceAccountName: external-dns + containers: + - name: external-dns + image: registry.k8s.io/external-dns/external-dns:v0.18.0 + imagePullPolicy: Always + args: + - "--txt-prefix=%{record_type}-" + - "--txt-cache-interval=2m" + - "--log-level=debug" + - "--log-format=text" + - "--txt-owner-id=old-owner" + - "--policy=sync" + - "--provider=some-provider" + - "--registry=txt" + - "--interval=1m" + - "--source=ingress" +``` + +You can update your deployment to migrate like so : + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: external-dns +spec: + replicas: 1 + selector: + matchLabels: + app: external-dns + strategy: + type: Recreate + template: + metadata: + labels: + app: external-dns + spec: + serviceAccountName: external-dns + containers: + - name: external-dns + imagePullPolicy: Always + image: registry.k8s.io/external-dns/external-dns:v0.18.0 + args: + - "--txt-prefix=%{record_type}-" + - "--txt-cache-interval=2m" + - "--log-level=debug" + - "--log-format=text" + - "--txt-owner-id=new-owner" + - "--from-txt-owner=old-owner" + - "--migrate-txt-owner" + - "--policy=sync" + - "--provider=some-provider" + - "--registry=txt" + - "--interval=1m" + - "--source=ingress" +``` + +If you didn't set the owner ID, the value set by external-dns is `default`. You can set the +`--from-txt-owner` flag to `default` to migrate the associated records. + From 7edd2d879dc310922f3573e6d25eff2265232415 Mon Sep 17 00:00:00 2001 From: Thibaut R Date: Wed, 3 Sep 2025 16:20:44 +0200 Subject: [PATCH 11/18] Fix rebase issues in txt test --- registry/txt_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/registry/txt_test.go b/registry/txt_test.go index dff00a63c9..f8c1865c88 100644 --- a/registry/txt_test.go +++ b/registry/txt_test.go @@ -83,7 +83,7 @@ func testTXTRegistryNew(t *testing.T) { r, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, aesKey, false, "") require.NoError(t, err) - _, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, nil, false, true, "") + _, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, nil, true, "") require.Error(t, err) _, ok = r.mapper.(affixNameMapper) @@ -1550,7 +1550,7 @@ func TestGenerateTXTWithMigration(t *testing.T) { } p := inmemory.NewInMemoryProvider() p.CreateZone(testZone) - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, false, "") + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") gotTXTBeforeMigration := r.generateTXTRecord(record) assert.Equal(t, expectedTXTBeforeMigration, gotTXTBeforeMigration) @@ -1565,7 +1565,7 @@ func TestGenerateTXTWithMigration(t *testing.T) { }, } - rMigrated, _ := NewTXTRegistry(p, "", "", "foobar", time.Hour, "", []string{}, []string{}, false, nil, false, true, "owner") + rMigrated, _ := NewTXTRegistry(p, "", "", "foobar", time.Hour, "", []string{}, []string{}, false, nil, true, "owner") gotTXTAfterMigration := rMigrated.generateTXTRecord(record) assert.Equal(t, expectedTXTAfterMigration, gotTXTAfterMigration) @@ -2032,7 +2032,7 @@ func TestTXTRegistryRecreatesMissingRecords(t *testing.T) { // When: Apply changes to recreate missing A records managedRecords := []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME, endpoint.RecordTypeAAAA, endpoint.RecordTypeTXT} - registry, err := NewTXTRegistry(p, "", "", ownerId, time.Hour, "", managedRecords, nil, false, nil) + registry, err := NewTXTRegistry(p, "", "", ownerId, time.Hour, "", managedRecords, nil, false, nil, false, "") assert.NoError(t, err) expectedRecords := append(existing, expectedCreate...) @@ -2072,7 +2072,7 @@ func TestTXTRecordMigration(t *testing.T) { p := inmemory.NewInMemoryProvider() p.CreateZone(testZone) - r, _ := NewTXTRegistry(p, "%{record_type}-", "", "foo", time.Hour, "", []string{}, []string{}, false, nil, false, false, "") + r, _ := NewTXTRegistry(p, "%{record_type}-", "", "foo", time.Hour, "", []string{}, []string{}, false, nil, false, "") r.ApplyChanges(ctx, &plan.Changes{ Create: []*endpoint.Endpoint{ @@ -2095,7 +2095,7 @@ func TestTXTRecordMigration(t *testing.T) { assert.Equal(t, expectedTXTRecords[0].Targets, newTXTRecord[0].Targets) - r, _ = NewTXTRegistry(p, "%{record_type}-", "", "foobar", time.Hour, "", []string{}, []string{}, false, nil, false, true, "foo") + r, _ = NewTXTRegistry(p, "%{record_type}-", "", "foobar", time.Hour, "", []string{}, []string{}, false, nil, true, "foo") updatedRecords, _ := r.Records(ctx) From 4a91fecf36bc0d16dc865f8c714796ec9418d68a Mon Sep 17 00:00:00 2001 From: troll-os <39793416+troll-os@users.noreply.github.com> Date: Wed, 3 Sep 2025 16:38:03 +0200 Subject: [PATCH 12/18] Update docs/registry/txt.md Co-authored-by: Michel Loiseleur <97035654+mloiseleur@users.noreply.github.com> --- docs/registry/txt.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/registry/txt.md b/docs/registry/txt.md index fbbb00bfcb..d2748d2751 100644 --- a/docs/registry/txt.md +++ b/docs/registry/txt.md @@ -230,7 +230,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.18.0 + image: registry.k8s.io/external-dns/external-dns:v0.19.0 imagePullPolicy: Always args: - "--txt-prefix=%{record_type}-" From 79b72793ccb4baa96d1363f41f0e26c5cd3eaf08 Mon Sep 17 00:00:00 2001 From: troll-os <39793416+troll-os@users.noreply.github.com> Date: Wed, 3 Sep 2025 16:38:18 +0200 Subject: [PATCH 13/18] Update docs/registry/txt.md Co-authored-by: Michel Loiseleur <97035654+mloiseleur@users.noreply.github.com> --- docs/registry/txt.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/registry/txt.md b/docs/registry/txt.md index d2748d2751..7983ebf5c0 100644 --- a/docs/registry/txt.md +++ b/docs/registry/txt.md @@ -268,7 +268,7 @@ spec: containers: - name: external-dns imagePullPolicy: Always - image: registry.k8s.io/external-dns/external-dns:v0.18.0 + image: registry.k8s.io/external-dns/external-dns:v0.19.0 args: - "--txt-prefix=%{record_type}-" - "--txt-cache-interval=2m" From cea2b7735df6b4f2f9f1ce7b0d9ce0c3b417fe17 Mon Sep 17 00:00:00 2001 From: troll-os <39793416+troll-os@users.noreply.github.com> Date: Wed, 3 Sep 2025 16:38:33 +0200 Subject: [PATCH 14/18] Update docs/registry/txt.md Co-authored-by: Michel Loiseleur <97035654+mloiseleur@users.noreply.github.com> --- docs/registry/txt.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/registry/txt.md b/docs/registry/txt.md index 7983ebf5c0..edc8c32bbf 100644 --- a/docs/registry/txt.md +++ b/docs/registry/txt.md @@ -202,9 +202,10 @@ Caching is enabled by specifying a cache duration with the `--txt-cache-interval ## OwnerID migration -If you wish to update the owner ID of the TXT records managed by your external-dns instance, -you can set the following flags: `--migrate-txt-owner`, which will enable the migration checks -in the run loop, `--txt-owner-id=new-owner-id` and `--from-txt-owner=old-owner-id`. +The owner ID of the TXT records managed by external-dns instance can be updated. + +When `--migrate-txt-owner` is set, it will enable the migration checks +in the run loop using `--txt-owner-id=new-owner-id` and `--from-txt-owner=old-owner-id`. If `--migrate-txt-owner` is set, `--from-txt-owner` is mandatory. From 8eaf4153efb0ae2328e463dd6ec7869ab2b36100 Mon Sep 17 00:00:00 2001 From: Thibaut R Date: Wed, 3 Sep 2025 17:26:53 +0200 Subject: [PATCH 15/18] Fix label overriding in TXT record generation when migration is enabled --- registry/txt.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/registry/txt.go b/registry/txt.go index 20740814cd..4e1308b293 100644 --- a/registry/txt.go +++ b/registry/txt.go @@ -307,13 +307,15 @@ func (im *TXTRegistry) generateTXTRecordWithFilter(r *endpoint.Endpoint, filter if isAlias, found := r.GetProviderSpecificProperty("alias"); found && isAlias == "true" && recordType == endpoint.RecordTypeA { recordType = endpoint.RecordTypeCNAME } + + if im.isMigrationEnabled && r.Labels[endpoint.OwnerLabelKey] == im.oldOwnerID { + r.Labels[endpoint.OwnerLabelKey] = im.ownerID + } + txtNew := endpoint.NewEndpoint(im.mapper.toTXTName(r.DNSName, recordType), endpoint.RecordTypeTXT, r.Labels.Serialize(true, im.txtEncryptEnabled, im.txtEncryptAESKey)) if txtNew != nil { txtNew.WithSetIdentifier(r.SetIdentifier) txtNew.Labels[endpoint.OwnedRecordLabelKey] = r.DNSName - if im.isMigrationEnabled && r.Labels[endpoint.OwnerLabelKey] == im.oldOwnerID { - r.Labels[endpoint.OwnerLabelKey] = im.ownerID - } txtNew.ProviderSpecific = r.ProviderSpecific if filter(txtNew) { endpoints = append(endpoints, txtNew) From acdd1a6d49b2ca9bdc774e22a5acd8013536bcb1 Mon Sep 17 00:00:00 2001 From: Thibaut R Date: Thu, 11 Sep 2025 16:45:04 +0200 Subject: [PATCH 16/18] Make linter happy --- docs/registry/txt.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/registry/txt.md b/docs/registry/txt.md index edc8c32bbf..f53c766d46 100644 --- a/docs/registry/txt.md +++ b/docs/registry/txt.md @@ -287,4 +287,3 @@ spec: If you didn't set the owner ID, the value set by external-dns is `default`. You can set the `--from-txt-owner` flag to `default` to migrate the associated records. - From ed5d0fd1d010ca1362f803b480e9b80922eb818d Mon Sep 17 00:00:00 2001 From: Thibaut R Date: Mon, 22 Sep 2025 09:48:46 +0200 Subject: [PATCH 17/18] Regen flags, fix types tests after types updates --- docs/flags.md | 4 ++-- pkg/apis/externaldns/types_test.go | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/flags.md b/docs/flags.md index 889033f09f..b1b47d212e 100644 --- a/docs/flags.md +++ b/docs/flags.md @@ -159,13 +159,13 @@ | `--policy=sync` | Modify how DNS records are synchronized between sources and providers (default: sync, options: sync, upsert-only, create-only) | | `--registry=txt` | The registry implementation to use to keep track of DNS record ownership (default: txt, options: txt, noop, dynamodb, aws-sd) | | `--txt-owner-id="default"` | When using the TXT or DynamoDB registry, a name that identifies this instance of ExternalDNS (default: default) | -| `--from-txt-owner=FROM-TXT-OWNER` | Old txt-owner-id that needs to be overwritten (default: default) | -| `--[no-]migrate-txt-owner` | When enabled, modify the previous txt-owner to the current txt-owner (default: disabled) | | `--txt-prefix=""` | When using the TXT registry, a custom string that's prefixed to each ownership DNS record (optional). Could contain record type template like '%{record_type}-prefix-'. Mutual exclusive with txt-suffix! | | `--txt-suffix=""` | When using the TXT registry, a custom string that's suffixed to the host portion of each ownership DNS record (optional). Could contain record type template like '-%{record_type}-suffix'. Mutual exclusive with txt-prefix! | | `--txt-wildcard-replacement=""` | When using the TXT registry, a custom string that's used instead of an asterisk for TXT records corresponding to wildcard DNS records (optional) | | `--[no-]txt-encrypt-enabled` | When using the TXT registry, set if TXT records should be encrypted before stored (default: disabled) | | `--txt-encrypt-aes-key=""` | When using the TXT registry, set TXT record decryption and encryption 32 byte aes key (required when --txt-encrypt=true) | +| `--[no-]migrate-txt-owner` | When enabled, modify the previous txt-owner to the current txt-owner (default: disabled) | +| `--from-txt-owner="default"` | Old txt-owner-id that needs to be overwritten (default: default) | | `--dynamodb-region=""` | When using the DynamoDB registry, the AWS region of the DynamoDB table (optional) | | `--dynamodb-table="external-dns"` | When using the DynamoDB registry, the name of the DynamoDB table (default: "external-dns") | | `--txt-cache-interval=0s` | The interval between cache synchronizations in duration format (default: disabled) | diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index 9a35cd3eb9..fad389a9dc 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -104,6 +104,7 @@ var ( Policy: "sync", Registry: "txt", TXTOwnerID: "default", + TXTOwnerOld: "default", TXTPrefix: "", TXTCacheInterval: 0, Interval: time.Minute, @@ -217,6 +218,8 @@ var ( Registry: "noop", TXTOwnerID: "owner-1", TXTPrefix: "associated-txt-record", + TXTOwnerMigrate: true, + TXTOwnerOld: "old-owner", TXTCacheInterval: 12 * time.Hour, Interval: 10 * time.Minute, MinEventSyncInterval: 50 * time.Second, @@ -360,6 +363,8 @@ func TestParseFlags(t *testing.T) { "--policy=upsert-only", "--registry=noop", "--txt-owner-id=owner-1", + "--migrate-txt-owner", + "--from-txt-owner=old-owner", "--txt-prefix=associated-txt-record", "--txt-cache-interval=12h", "--dynamodb-table=custom-table", @@ -482,6 +487,8 @@ func TestParseFlags(t *testing.T) { "EXTERNAL_DNS_REGISTRY": "noop", "EXTERNAL_DNS_TXT_OWNER_ID": "owner-1", "EXTERNAL_DNS_TXT_PREFIX": "associated-txt-record", + "EXTERNAL_DNS_MIGRATE_TXT_OWNER": "true", + "EXTERNAL_DNS_FROM_TXT_OWNER": "old-owner", "EXTERNAL_DNS_TXT_CACHE_INTERVAL": "12h", "EXTERNAL_DNS_TXT_NEW_FORMAT_ONLY": "1", "EXTERNAL_DNS_INTERVAL": "10m", From fe7435cda06994d5ccdd3c7f664d9926a3eb01b3 Mon Sep 17 00:00:00 2001 From: Thibaut R Date: Sun, 28 Sep 2025 09:07:40 +0200 Subject: [PATCH 18/18] Removed boolean flag that enabled migration, evaluate only against old owner flag instead --- controller/execute.go | 2 +- docs/flags.md | 3 +- docs/registry/txt.md | 12 ++--- pkg/apis/externaldns/types.go | 7 +-- pkg/apis/externaldns/types_test.go | 9 ++-- registry/txt.go | 14 ++---- registry/txt_encryption_test.go | 10 ++-- registry/txt_test.go | 81 ++++++++++++++---------------- 8 files changed, 61 insertions(+), 77 deletions(-) diff --git a/controller/execute.go b/controller/execute.go index fdf1ee6970..d2140c8d4d 100644 --- a/controller/execute.go +++ b/controller/execute.go @@ -419,7 +419,7 @@ func selectRegistry(cfg *externaldns.Config, p provider.Provider) (registry.Regi case "noop": r, err = registry.NewNoopRegistry(p) case "txt": - r, err = registry.NewTXTRegistry(p, cfg.TXTPrefix, cfg.TXTSuffix, cfg.TXTOwnerID, cfg.TXTCacheInterval, cfg.TXTWildcardReplacement, cfg.ManagedDNSRecordTypes, cfg.ExcludeDNSRecordTypes, cfg.TXTEncryptEnabled, []byte(cfg.TXTEncryptAESKey), cfg.TXTOwnerMigrate, cfg.TXTOwnerOld) + r, err = registry.NewTXTRegistry(p, cfg.TXTPrefix, cfg.TXTSuffix, cfg.TXTOwnerID, cfg.TXTCacheInterval, cfg.TXTWildcardReplacement, cfg.ManagedDNSRecordTypes, cfg.ExcludeDNSRecordTypes, cfg.TXTEncryptEnabled, []byte(cfg.TXTEncryptAESKey), cfg.TXTOwnerOld) case "aws-sd": r, err = registry.NewAWSSDRegistry(p, cfg.TXTOwnerID) default: diff --git a/docs/flags.md b/docs/flags.md index b1b47d212e..f03026c99f 100644 --- a/docs/flags.md +++ b/docs/flags.md @@ -164,8 +164,7 @@ | `--txt-wildcard-replacement=""` | When using the TXT registry, a custom string that's used instead of an asterisk for TXT records corresponding to wildcard DNS records (optional) | | `--[no-]txt-encrypt-enabled` | When using the TXT registry, set if TXT records should be encrypted before stored (default: disabled) | | `--txt-encrypt-aes-key=""` | When using the TXT registry, set TXT record decryption and encryption 32 byte aes key (required when --txt-encrypt=true) | -| `--[no-]migrate-txt-owner` | When enabled, modify the previous txt-owner to the current txt-owner (default: disabled) | -| `--from-txt-owner="default"` | Old txt-owner-id that needs to be overwritten (default: default) | +| `--migrate-from-txt-owner=""` | Old txt-owner-id that needs to be overwritten (default: default) | | `--dynamodb-region=""` | When using the DynamoDB registry, the AWS region of the DynamoDB table (optional) | | `--dynamodb-table="external-dns"` | When using the DynamoDB registry, the name of the DynamoDB table (default: "external-dns") | | `--txt-cache-interval=0s` | The interval between cache synchronizations in duration format (default: disabled) | diff --git a/docs/registry/txt.md b/docs/registry/txt.md index f53c766d46..3c2df4da58 100644 --- a/docs/registry/txt.md +++ b/docs/registry/txt.md @@ -204,10 +204,11 @@ Caching is enabled by specifying a cache duration with the `--txt-cache-interval The owner ID of the TXT records managed by external-dns instance can be updated. -When `--migrate-txt-owner` is set, it will enable the migration checks -in the run loop using `--txt-owner-id=new-owner-id` and `--from-txt-owner=old-owner-id`. +When `--migrate-from-txt-owner` is set, it will enable the migration checks +in the run loop using `--txt-owner-id=new-owner-id` and the value you defined for this flag. -If `--migrate-txt-owner` is set, `--from-txt-owner` is mandatory. +If you want to test the outputs of a migration beforehand, you can use the `--dry-run` flag +along with `--migrate-from-txt-owner`. Example, if you had a standard deployment like so: @@ -276,8 +277,7 @@ spec: - "--log-level=debug" - "--log-format=text" - "--txt-owner-id=new-owner" - - "--from-txt-owner=old-owner" - - "--migrate-txt-owner" + - "--migrate-from-txt-owner=old-owner" - "--policy=sync" - "--provider=some-provider" - "--registry=txt" @@ -286,4 +286,4 @@ spec: ``` If you didn't set the owner ID, the value set by external-dns is `default`. You can set the -`--from-txt-owner` flag to `default` to migrate the associated records. +`--migrate-from-txt-owner` flag to `default` to migrate the associated records. diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index fd131e4bdb..c2fff68b3f 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -143,7 +143,6 @@ type Config struct { Registry string TXTOwnerID string TXTOwnerOld string - TXTOwnerMigrate bool TXTPrefix string TXTSuffix string TXTEncryptEnabled bool @@ -373,8 +372,7 @@ var defaultConfig = &Config{ TXTEncryptAESKey: "", TXTEncryptEnabled: false, TXTOwnerID: "default", - TXTOwnerOld: "default", - TXTOwnerMigrate: false, + TXTOwnerOld: "", TXTPrefix: "", TXTSuffix: "", TXTWildcardReplacement: "", @@ -786,8 +784,7 @@ func bindFlags(b FlagBinder, cfg *Config) { b.StringVar("txt-wildcard-replacement", "When using the TXT registry, a custom string that's used instead of an asterisk for TXT records corresponding to wildcard DNS records (optional)", defaultConfig.TXTWildcardReplacement, &cfg.TXTWildcardReplacement) b.BoolVar("txt-encrypt-enabled", "When using the TXT registry, set if TXT records should be encrypted before stored (default: disabled)", defaultConfig.TXTEncryptEnabled, &cfg.TXTEncryptEnabled) b.StringVar("txt-encrypt-aes-key", "When using the TXT registry, set TXT record decryption and encryption 32 byte aes key (required when --txt-encrypt=true)", defaultConfig.TXTEncryptAESKey, &cfg.TXTEncryptAESKey) - b.BoolVar("migrate-txt-owner", "When enabled, modify the previous txt-owner to the current txt-owner (default: disabled)", defaultConfig.TXTOwnerMigrate, &cfg.TXTOwnerMigrate) - b.StringVar("from-txt-owner", "Old txt-owner-id that needs to be overwritten (default: default)", defaultConfig.TXTOwnerOld, &cfg.TXTOwnerOld) + b.StringVar("migrate-from-txt-owner", "Old txt-owner-id that needs to be overwritten (default: default)", defaultConfig.TXTOwnerOld, &cfg.TXTOwnerOld) b.StringVar("dynamodb-region", "When using the DynamoDB registry, the AWS region of the DynamoDB table (optional)", cfg.AWSDynamoDBRegion, &cfg.AWSDynamoDBRegion) b.StringVar("dynamodb-table", "When using the DynamoDB registry, the name of the DynamoDB table (default: \"external-dns\")", defaultConfig.AWSDynamoDBTable, &cfg.AWSDynamoDBTable) diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index fad389a9dc..5a6d7f96c2 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -104,7 +104,7 @@ var ( Policy: "sync", Registry: "txt", TXTOwnerID: "default", - TXTOwnerOld: "default", + TXTOwnerOld: "", TXTPrefix: "", TXTCacheInterval: 0, Interval: time.Minute, @@ -218,7 +218,6 @@ var ( Registry: "noop", TXTOwnerID: "owner-1", TXTPrefix: "associated-txt-record", - TXTOwnerMigrate: true, TXTOwnerOld: "old-owner", TXTCacheInterval: 12 * time.Hour, Interval: 10 * time.Minute, @@ -363,8 +362,7 @@ func TestParseFlags(t *testing.T) { "--policy=upsert-only", "--registry=noop", "--txt-owner-id=owner-1", - "--migrate-txt-owner", - "--from-txt-owner=old-owner", + "--migrate-from-txt-owner=old-owner", "--txt-prefix=associated-txt-record", "--txt-cache-interval=12h", "--dynamodb-table=custom-table", @@ -487,8 +485,7 @@ func TestParseFlags(t *testing.T) { "EXTERNAL_DNS_REGISTRY": "noop", "EXTERNAL_DNS_TXT_OWNER_ID": "owner-1", "EXTERNAL_DNS_TXT_PREFIX": "associated-txt-record", - "EXTERNAL_DNS_MIGRATE_TXT_OWNER": "true", - "EXTERNAL_DNS_FROM_TXT_OWNER": "old-owner", + "EXTERNAL_DNS_MIGRATE_FROM_TXT_OWNER": "old-owner", "EXTERNAL_DNS_TXT_CACHE_INTERVAL": "12h", "EXTERNAL_DNS_TXT_NEW_FORMAT_ONLY": "1", "EXTERNAL_DNS_INTERVAL": "10m", diff --git a/registry/txt.go b/registry/txt.go index 4e1308b293..d814192183 100644 --- a/registry/txt.go +++ b/registry/txt.go @@ -61,8 +61,7 @@ type TXTRegistry struct { txtEncryptAESKey []byte //Handle Owner ID migration - isMigrationEnabled bool - oldOwnerID string + oldOwnerID string // existingTXTs is the TXT records that already exist in the zone so that // ApplyChanges() can skip re-creating them. See the struct below for details. @@ -119,15 +118,11 @@ func NewTXTRegistry(provider provider.Provider, txtPrefix, txtSuffix, ownerID st cacheInterval time.Duration, txtWildcardReplacement string, managedRecordTypes, excludeRecordTypes []string, txtEncryptEnabled bool, txtEncryptAESKey []byte, - isMigrationEnabled bool, oldOwnerID string) (*TXTRegistry, error) { + oldOwnerID string) (*TXTRegistry, error) { if ownerID == "" { return nil, errors.New("owner id cannot be empty") } - if isMigrationEnabled && oldOwnerID == "" { - return nil, errors.New("oldOwnerID cannot be empty if isMigrationEnabled is true") - } - if len(txtEncryptAESKey) == 0 { txtEncryptAESKey = nil } else if len(txtEncryptAESKey) != 32 { @@ -157,7 +152,6 @@ func NewTXTRegistry(provider provider.Provider, txtPrefix, txtSuffix, ownerID st excludeRecordTypes: excludeRecordTypes, txtEncryptEnabled: txtEncryptEnabled, txtEncryptAESKey: txtEncryptAESKey, - isMigrationEnabled: isMigrationEnabled, oldOwnerID: oldOwnerID, existingTXTs: newExistingTXTs(), }, nil @@ -263,7 +257,7 @@ func (im *TXTRegistry) Records(ctx context.Context) ([]*endpoint.Endpoint, error } } - if im.isMigrationEnabled && ep.Labels[endpoint.OwnerLabelKey] == im.oldOwnerID { + if im.oldOwnerID != "" && ep.Labels[endpoint.OwnerLabelKey] == im.oldOwnerID { ep.Labels[endpoint.OwnerLabelKey] = im.ownerID } @@ -308,7 +302,7 @@ func (im *TXTRegistry) generateTXTRecordWithFilter(r *endpoint.Endpoint, filter recordType = endpoint.RecordTypeCNAME } - if im.isMigrationEnabled && r.Labels[endpoint.OwnerLabelKey] == im.oldOwnerID { + if im.oldOwnerID != "" && r.Labels[endpoint.OwnerLabelKey] == im.oldOwnerID { r.Labels[endpoint.OwnerLabelKey] = im.ownerID } diff --git a/registry/txt_encryption_test.go b/registry/txt_encryption_test.go index 828dd65c7c..65cee6248d 100644 --- a/registry/txt_encryption_test.go +++ b/registry/txt_encryption_test.go @@ -61,7 +61,7 @@ func TestNewTXTRegistryEncryptionConfig(t *testing.T) { }, } for _, test := range tests { - actual, err := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "", []string{}, []string{}, test.encEnabled, test.aesKeyRaw, false, "") + actual, err := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "", []string{}, []string{}, test.encEnabled, test.aesKeyRaw, "") if test.errorExpected { require.Error(t, err) } else { @@ -107,7 +107,7 @@ func TestGenerateTXTGenerateTextRecordEncryptionWihDecryption(t *testing.T) { for _, k := range withEncryptionKeys { t.Run(fmt.Sprintf("key '%s' with decrypted result '%s'", k, test.decrypted), func(t *testing.T) { key := []byte(k) - r, err := NewTXTRegistry(p, "", "", "owner", time.Minute, "", []string{}, []string{}, true, key, false, "") + r, err := NewTXTRegistry(p, "", "", "owner", time.Minute, "", []string{}, []string{}, true, key, "") assert.NoError(t, err, "Error creating TXT registry") txtRecords := r.generateTXTRecord(test.record) assert.Len(t, txtRecords, len(test.record.Targets)) @@ -144,7 +144,7 @@ func TestApplyRecordsWithEncryption(t *testing.T) { key := []byte("ZPitL0NGVQBZbTD6DwXJzD8RiStSazzYXQsdUowLURY=") - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, key, false, "") + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, key, "") _ = r.ApplyChanges(ctx, &plan.Changes{ Create: []*endpoint.Endpoint{ @@ -202,7 +202,7 @@ func TestApplyRecordsWithEncryptionKeyChanged(t *testing.T) { } for _, key := range withEncryptionKeys { - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, []byte(key), false, "") + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, []byte(key), "") _ = r.ApplyChanges(ctx, &plan.Changes{ Create: []*endpoint.Endpoint{ newEndpointWithOwner("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner"), @@ -232,7 +232,7 @@ func TestApplyRecordsOnEncryptionKeyChangeWithKeyIdLabel(t *testing.T) { } for i, key := range withEncryptionKeys { - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, []byte(key), false, "") + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, []byte(key), "") keyId := fmt.Sprintf("key-id-%d", i) changes := []*endpoint.Endpoint{ newEndpointWithOwnerAndOwnedRecordWithKeyIDLabel("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner", "", keyId), diff --git a/registry/txt_test.go b/registry/txt_test.go index f8c1865c88..b5146aa49b 100644 --- a/registry/txt_test.go +++ b/registry/txt_test.go @@ -49,20 +49,20 @@ func TestTXTRegistry(t *testing.T) { func testTXTRegistryNew(t *testing.T) { p := inmemory.NewInMemoryProvider() - _, err := NewTXTRegistry(p, "txt", "", "", time.Hour, "", []string{}, []string{}, false, nil, false, "") + _, err := NewTXTRegistry(p, "txt", "", "", time.Hour, "", []string{}, []string{}, false, nil, "") require.Error(t, err) - _, err = NewTXTRegistry(p, "", "txt", "", time.Hour, "", []string{}, []string{}, false, nil, false, "") + _, err = NewTXTRegistry(p, "", "txt", "", time.Hour, "", []string{}, []string{}, false, nil, "") require.Error(t, err) - r, err := NewTXTRegistry(p, "txt", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") + r, err := NewTXTRegistry(p, "txt", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, "") require.NoError(t, err) assert.Equal(t, p, r.provider) - r, err = NewTXTRegistry(p, "", "txt", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") + r, err = NewTXTRegistry(p, "", "txt", "owner", time.Hour, "", []string{}, []string{}, false, nil, "") require.NoError(t, err) - _, err = NewTXTRegistry(p, "txt", "txt", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") + _, err = NewTXTRegistry(p, "txt", "txt", "owner", time.Hour, "", []string{}, []string{}, false, nil, "") require.Error(t, err) _, ok := r.mapper.(affixNameMapper) @@ -71,21 +71,18 @@ func testTXTRegistryNew(t *testing.T) { assert.Equal(t, p, r.provider) aesKey := []byte(";k&l)nUC/33:{?d{3)54+,AD?]SX%yh^") - _, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") + _, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, "") require.NoError(t, err) - _, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, aesKey, false, "") + _, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, aesKey, "") require.NoError(t, err) - _, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, nil, false, "") + _, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, nil, "") require.Error(t, err) - r, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, aesKey, false, "") + r, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, aesKey, "") require.NoError(t, err) - _, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, nil, true, "") - require.Error(t, err) - _, ok = r.mapper.(affixNameMapper) assert.True(t, ok) } @@ -231,13 +228,13 @@ func testTXTRegistryRecordsPrefixed(t *testing.T) { }, } - r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "wc", []string{}, []string{}, false, nil, false, "") + r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "wc", []string{}, []string{}, false, nil, "") records, _ := r.Records(ctx) assert.True(t, testutils.SameEndpoints(records, expectedRecords)) // Ensure prefix is case-insensitive - r, _ = NewTXTRegistry(p, "TxT.", "", "owner", time.Hour, "wc", []string{}, []string{}, false, nil, false, "") + r, _ = NewTXTRegistry(p, "TxT.", "", "owner", time.Hour, "wc", []string{}, []string{}, false, nil, "") records, _ = r.Records(ctx) assert.True(t, testutils.SameEndpoints(records, expectedRecords)) @@ -366,13 +363,13 @@ func testTXTRegistryRecordsSuffixed(t *testing.T) { }, } - r, _ := NewTXTRegistry(p, "", "-txt", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") + r, _ := NewTXTRegistry(p, "", "-txt", "owner", time.Hour, "", []string{}, []string{}, false, nil, "") records, _ := r.Records(ctx) assert.True(t, testutils.SameEndpoints(records, expectedRecords)) // Ensure prefix is case-insensitive - r, _ = NewTXTRegistry(p, "", "-TxT", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") + r, _ = NewTXTRegistry(p, "", "-TxT", "owner", time.Hour, "", []string{}, []string{}, false, nil, "") records, _ = r.Records(ctx) assert.True(t, testutils.SameEndpointLabels(records, expectedRecords)) @@ -493,7 +490,7 @@ func testTXTRegistryRecordsNoPrefix(t *testing.T) { }, } - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, "") records, _ := r.Records(ctx) assert.True(t, testutils.SameEndpoints(records, expectedRecords)) @@ -530,12 +527,12 @@ func testTXTRegistryRecordsPrefixedTemplated(t *testing.T) { }, } - r, _ := NewTXTRegistry(p, "txt-%{record_type}.", "", "owner", time.Hour, "wc", []string{}, []string{}, false, nil, false, "") + r, _ := NewTXTRegistry(p, "txt-%{record_type}.", "", "owner", time.Hour, "wc", []string{}, []string{}, false, nil, "") records, _ := r.Records(ctx) assert.True(t, testutils.SameEndpoints(records, expectedRecords)) - r, _ = NewTXTRegistry(p, "TxT-%{record_type}.", "", "owner", time.Hour, "wc", []string{}, []string{}, false, nil, false, "") + r, _ = NewTXTRegistry(p, "TxT-%{record_type}.", "", "owner", time.Hour, "wc", []string{}, []string{}, false, nil, "") records, _ = r.Records(ctx) assert.True(t, testutils.SameEndpoints(records, expectedRecords)) @@ -572,12 +569,12 @@ func testTXTRegistryRecordsSuffixedTemplated(t *testing.T) { }, } - r, _ := NewTXTRegistry(p, "", "txt%{record_type}", "owner", time.Hour, "wc", []string{}, []string{}, false, nil, false, "") + r, _ := NewTXTRegistry(p, "", "txt%{record_type}", "owner", time.Hour, "wc", []string{}, []string{}, false, nil, "") records, _ := r.Records(ctx) assert.True(t, testutils.SameEndpoints(records, expectedRecords)) - r, _ = NewTXTRegistry(p, "", "TxT%{record_type}", "owner", time.Hour, "wc", []string{}, []string{}, false, nil, false, "") + r, _ = NewTXTRegistry(p, "", "TxT%{record_type}", "owner", time.Hour, "wc", []string{}, []string{}, false, nil, "") records, _ = r.Records(ctx) assert.True(t, testutils.SameEndpoints(records, expectedRecords)) @@ -620,7 +617,7 @@ func testTXTRegistryApplyChangesWithPrefix(t *testing.T) { newEndpointWithOwner("txt.cname-multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "").WithSetIdentifier("test-set-2"), }, }) - r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") + r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, "") changes := &plan.Changes{ Create: []*endpoint.Endpoint{ @@ -701,7 +698,7 @@ func testTXTRegistryApplyChangesWithTemplatedPrefix(t *testing.T) { p.ApplyChanges(ctx, &plan.Changes{ Create: []*endpoint.Endpoint{}, }) - r, _ := NewTXTRegistry(p, "prefix%{record_type}.", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") + r, _ := NewTXTRegistry(p, "prefix%{record_type}.", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, "") changes := &plan.Changes{ Create: []*endpoint.Endpoint{ newEndpointWithOwnerResource("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "", "ingress/default/my-ingress"), @@ -744,7 +741,7 @@ func testTXTRegistryApplyChangesWithTemplatedSuffix(t *testing.T) { p.OnApplyChanges = func(ctx context.Context, got *plan.Changes) { assert.Equal(t, ctxEndpoints, ctx.Value(provider.RecordsContextKey)) } - r, _ := NewTXTRegistry(p, "", "-%{record_type}suffix", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") + r, _ := NewTXTRegistry(p, "", "-%{record_type}suffix", "owner", time.Hour, "", []string{}, []string{}, false, nil, "") changes := &plan.Changes{ Create: []*endpoint.Endpoint{ newEndpointWithOwnerResource("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "", "ingress/default/my-ingress"), @@ -809,7 +806,7 @@ func testTXTRegistryApplyChangesWithSuffix(t *testing.T) { newEndpointWithOwner("cname-multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "").WithSetIdentifier("test-set-2"), }, }) - r, _ := NewTXTRegistry(p, "", "-txt", "owner", time.Hour, "wildcard", []string{}, []string{}, false, nil, false, "") + r, _ := NewTXTRegistry(p, "", "-txt", "owner", time.Hour, "wildcard", []string{}, []string{}, false, nil, "") changes := &plan.Changes{ Create: []*endpoint.Endpoint{ @@ -903,7 +900,7 @@ func testTXTRegistryApplyChangesNoPrefix(t *testing.T) { newEndpointWithOwner("cname-foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), }, }) - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, "") changes := &plan.Changes{ Create: []*endpoint.Endpoint{ @@ -1061,7 +1058,7 @@ func testTXTRegistryMissingRecordsNoPrefix(t *testing.T) { }, } - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "wc", []string{endpoint.RecordTypeCNAME, endpoint.RecordTypeA, endpoint.RecordTypeNS}, []string{}, false, nil, false, "") + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "wc", []string{endpoint.RecordTypeCNAME, endpoint.RecordTypeA, endpoint.RecordTypeNS}, []string{}, false, nil, "") records, _ := r.Records(ctx) assert.True(t, testutils.SameEndpoints(records, expectedRecords)) @@ -1171,7 +1168,7 @@ func testTXTRegistryMissingRecordsWithPrefix(t *testing.T) { }, } - r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "wc", []string{endpoint.RecordTypeCNAME, endpoint.RecordTypeA, endpoint.RecordTypeNS, endpoint.RecordTypeTXT}, []string{}, false, nil, false, "") + r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "wc", []string{endpoint.RecordTypeCNAME, endpoint.RecordTypeA, endpoint.RecordTypeNS, endpoint.RecordTypeTXT}, []string{}, false, nil, "") records, _ := r.Records(ctx) assert.True(t, testutils.SameEndpoints(records, expectedRecords)) @@ -1466,7 +1463,7 @@ func TestNewTXTScheme(t *testing.T) { newEndpointWithOwner("cname-foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), }, }) - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, "") changes := &plan.Changes{ Create: []*endpoint.Endpoint{ @@ -1531,7 +1528,7 @@ func TestGenerateTXT(t *testing.T) { } p := inmemory.NewInMemoryProvider() p.CreateZone(testZone) - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, "") gotTXT := r.generateTXTRecord(record) assert.Equal(t, expectedTXT, gotTXT) } @@ -1550,7 +1547,7 @@ func TestGenerateTXTWithMigration(t *testing.T) { } p := inmemory.NewInMemoryProvider() p.CreateZone(testZone) - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, "") gotTXTBeforeMigration := r.generateTXTRecord(record) assert.Equal(t, expectedTXTBeforeMigration, gotTXTBeforeMigration) @@ -1565,7 +1562,7 @@ func TestGenerateTXTWithMigration(t *testing.T) { }, } - rMigrated, _ := NewTXTRegistry(p, "", "", "foobar", time.Hour, "", []string{}, []string{}, false, nil, true, "owner") + rMigrated, _ := NewTXTRegistry(p, "", "", "foobar", time.Hour, "", []string{}, []string{}, false, nil, "owner") gotTXTAfterMigration := rMigrated.generateTXTRecord(record) assert.Equal(t, expectedTXTAfterMigration, gotTXTAfterMigration) @@ -1585,7 +1582,7 @@ func TestGenerateTXTForAAAA(t *testing.T) { } p := inmemory.NewInMemoryProvider() p.CreateZone(testZone) - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, "") gotTXT := r.generateTXTRecord(record) assert.Equal(t, expectedTXT, gotTXT) } @@ -1602,7 +1599,7 @@ func TestFailGenerateTXT(t *testing.T) { expectedTXT := []*endpoint.Endpoint{} p := inmemory.NewInMemoryProvider() p.CreateZone(testZone) - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, "") gotTXT := r.generateTXTRecord(cnameRecord) assert.Equal(t, expectedTXT, gotTXT) } @@ -1620,7 +1617,7 @@ func TestTXTRegistryApplyChangesEncrypt(t *testing.T) { }, }) - r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "", []string{}, []string{}, true, []byte("12345678901234567890123456789012"), false, "") + r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "", []string{}, []string{}, true, []byte("12345678901234567890123456789012"), "") records, _ := r.Records(ctx) changes := &plan.Changes{ Delete: records, @@ -1666,7 +1663,7 @@ func TestMultiClusterDifferentRecordTypeOwnership(t *testing.T) { }, }) - r, _ := NewTXTRegistry(p, "_owner.", "", "bar", time.Hour, "", []string{}, []string{}, false, nil, false, "") + r, _ := NewTXTRegistry(p, "_owner.", "", "bar", time.Hour, "", []string{}, []string{}, false, nil, "") records, _ := r.Records(ctx) // new cluster has same ingress host as other cluster and uses CNAME ingress address @@ -1751,7 +1748,7 @@ func TestGenerateTXTRecordWithNewFormatOnly(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, "") records := r.generateTXTRecord(tc.endpoint) assert.Len(t, records, tc.expectedRecords, tc.description) @@ -1780,7 +1777,7 @@ func TestApplyChangesWithNewFormatOnly(t *testing.T) { p.CreateZone(testZone) ctx := context.Background() - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, "") changes := &plan.Changes{ Create: []*endpoint.Endpoint{ @@ -1828,7 +1825,7 @@ func TestTXTRegistryRecordsWithEmptyTargets(t *testing.T) { }, }) - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false, "") + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, "") hook := testutils.LogsUnderTestWithLogLevel(log.ErrorLevel, t) records, err := r.Records(ctx) require.NoError(t, err) @@ -2032,7 +2029,7 @@ func TestTXTRegistryRecreatesMissingRecords(t *testing.T) { // When: Apply changes to recreate missing A records managedRecords := []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME, endpoint.RecordTypeAAAA, endpoint.RecordTypeTXT} - registry, err := NewTXTRegistry(p, "", "", ownerId, time.Hour, "", managedRecords, nil, false, nil, false, "") + registry, err := NewTXTRegistry(p, "", "", ownerId, time.Hour, "", managedRecords, nil, false, nil, "") assert.NoError(t, err) expectedRecords := append(existing, expectedCreate...) @@ -2072,7 +2069,7 @@ func TestTXTRecordMigration(t *testing.T) { p := inmemory.NewInMemoryProvider() p.CreateZone(testZone) - r, _ := NewTXTRegistry(p, "%{record_type}-", "", "foo", time.Hour, "", []string{}, []string{}, false, nil, false, "") + r, _ := NewTXTRegistry(p, "%{record_type}-", "", "foo", time.Hour, "", []string{}, []string{}, false, nil, "") r.ApplyChanges(ctx, &plan.Changes{ Create: []*endpoint.Endpoint{ @@ -2095,7 +2092,7 @@ func TestTXTRecordMigration(t *testing.T) { assert.Equal(t, expectedTXTRecords[0].Targets, newTXTRecord[0].Targets) - r, _ = NewTXTRegistry(p, "%{record_type}-", "", "foobar", time.Hour, "", []string{}, []string{}, false, nil, true, "foo") + r, _ = NewTXTRegistry(p, "%{record_type}-", "", "foobar", time.Hour, "", []string{}, []string{}, false, nil, "foo") updatedRecords, _ := r.Records(ctx)