mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-05 17:16:59 +02:00
feat(txt-registry): deprecate legacy txt-format (#5172)
* feat(txt-registry): only support single format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): only support single format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): only support single format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): only support single format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): only support single format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): deprecate legacy txt-format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): deprecate legacy txt-format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): deprecate legacy txt-format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): deprecate legacy txt-format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): deprecate legacy txt-format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): deprecate legacy txt-format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): deprecate legacy txt-format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): deprecate legacy txt-format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): deprecate legacy txt-format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): deprecate legacy txt-format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): deprecate legacy txt-format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): deprecate legacy txt-format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): deprecate legacy txt-format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): deprecate legacy txt-format Co-authored-by: Michel Loiseleur <97035654+mloiseleur@users.noreply.github.com> * feat(txt-registry): deprecate legacy txt-format Co-authored-by: Michel Loiseleur <97035654+mloiseleur@users.noreply.github.com> * feat(txt-registry): deprecate legacy txt-format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): deprecate legacy txt-format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): deprecate legacy txt-format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): address review comments Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): deprecate legacy txt-format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): deprecate legacy txt-format * feat(txt-registry): deprecate legacy txt-format Co-authored-by: Michel Loiseleur <97035654+mloiseleur@users.noreply.github.com> * feat(txt-registry): deprecate legacy txt-format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * feat(txt-registry): deprecate legacy txt-format Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> --------- Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> Co-authored-by: Michel Loiseleur <97035654+mloiseleur@users.noreply.github.com>
This commit is contained in:
parent
3c8f774192
commit
9f16d835f1
@ -20,3 +20,7 @@ insert_final_newline = true
|
||||
[{Makefile,go.mod,go.sum,*.go}]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[*.py]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
@ -23,5 +23,6 @@ repos:
|
||||
rev: v0.44.0
|
||||
hooks:
|
||||
- id: markdownlint
|
||||
args: ["--fix"]
|
||||
|
||||
minimum_pre_commit_version: !!str 3.2
|
||||
|
@ -395,7 +395,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.TXTNewFormatOnly)
|
||||
r, err = registry.NewTXTRegistry(p, cfg.TXTPrefix, cfg.TXTSuffix, cfg.TXTOwnerID, cfg.TXTCacheInterval, cfg.TXTWildcardReplacement, cfg.ManagedDNSRecordTypes, cfg.ExcludeDNSRecordTypes, cfg.TXTEncryptEnabled, []byte(cfg.TXTEncryptAESKey))
|
||||
case "aws-sd":
|
||||
r, err = registry.NewAWSSDRegistry(p, cfg.TXTOwnerID)
|
||||
default:
|
||||
|
@ -83,7 +83,6 @@ func TestSelectRegistry(t *testing.T) {
|
||||
TXTWildcardReplacement: "wildcard",
|
||||
ManagedDNSRecordTypes: []string{"A", "CNAME"},
|
||||
ExcludeDNSRecordTypes: []string{"TXT"},
|
||||
TXTNewFormatOnly: true,
|
||||
},
|
||||
provider: &MockProvider{},
|
||||
wantErr: false,
|
||||
|
@ -165,7 +165,6 @@
|
||||
| `--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-]txt-new-format-only` | When using the TXT registry, only use new format records which include record type information (e.g., prefix: 'a-'). Reduces number of TXT records (default: disabled) |
|
||||
| `--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) |
|
||||
|
@ -3,8 +3,51 @@
|
||||
The TXT registry is the default registry.
|
||||
It stores DNS record metadata in TXT records, using the same provider.
|
||||
|
||||
If you plan to manage apex domains with external-dns whilst using a txt registry, you should ensure when using --txt-prefix that you specify the record type substitution and that it ends in a period (**.**). The record should be created under the same domain as the apex record being managed, i.e. --txt-prefix=someprefix-%{record_type}.
|
||||
|
||||
> Note: `--txt-prefix` and `--txt-suffix` contribute to the 63-byte maximum record length. To avoid errors, use them only if absolutely required and keep them as short as possible.
|
||||
|
||||
## Record Format Options
|
||||
|
||||
### For version `v0.18+`
|
||||
|
||||
The TXT registry supports single format for storing DNS record metadata:
|
||||
|
||||
- Creates a TXT record with record type information (e.g., 'a-' prefix for A records)
|
||||
|
||||
The TXT registry would try to guarantee a consistency in between providers and sources, if provider supports the behaviour.
|
||||
|
||||
If you are dealing with APEX domains, like `example.com` and TXT records are failing to be created for managed record types specified by `--managed-record-types`, consider following options:
|
||||
|
||||
1. TXT record with prefix based on requirements. Example `--txt-prefix="%{record_type}-abc-"` or `--txt-prefix="%{record_type}.abc-"`
|
||||
2. TXT record with suffix based on requirements. Example `--txt-suffix="-abc-%{record_type}"` or `--txt-suffix="-abc.%{record_type}."`
|
||||
|
||||
If configured `--txt-prefix="%{record_type}-abc-"` for apex domain `ex.com` the expected result is
|
||||
|
||||
| Name | TYPE |
|
||||
|:------------------------------:|:-------:|
|
||||
| `cname-a-abc-nginx-v2.ex.com.` | `TXT` |
|
||||
| `nginx-v2.ex.com.` | `CNAME` |
|
||||
|
||||
If configured `--txt-suffix="-abc.%{record_type}"` for apex domain `ex.com` the expected result is
|
||||
|
||||
| Name | TYPE |
|
||||
|:------------------------------:|:-------:|
|
||||
| `cname-nginx-v2-abc.a.ex.com.` | `TXT` |
|
||||
| `nginx-v3.ex.com.` | `CNAME` |
|
||||
|
||||
### Manually Cleanup Legacy TXT Records
|
||||
|
||||
> While deleting registry TXT records won't cause downtime, a well-thought-out migration and cleanup plan is crucial.
|
||||
|
||||
Occasionally, it may be necessary to remove outdated TXT records from your registry.
|
||||
|
||||
An example script for AWS can be found in [scripts/aws-cleanup-legacy-txt-records.py](../../scripts/aws-cleanup-legacy-txt-records.py) with instructions on how to run it.
|
||||
The script performs targeted deletion of TXT records that include `ResourceRecords` matching the `heritage=external-dns,external-dns/owner=default` or similar pattern.
|
||||
In the event of unintended deletion of all TXT records managed by `external-dns`, `external-dns` will initiate a full DNS record regeneration, along with`TXT` and `non-TXT` records. Just be aware, this operation's duration is directly proportional to the DNS estate size."
|
||||
|
||||
### For version `v0.16.0 & v0.16.1`
|
||||
|
||||
The TXT registry supports two formats for storing DNS record metadata:
|
||||
|
||||
- Legacy format: Creates a TXT record without record type information
|
||||
@ -31,14 +74,14 @@ The `--txt-new-format-only` flag should be used in addition to your existing ext
|
||||
|
||||
### Migration to New Format Only
|
||||
|
||||
> Note: `external-dns` will not automatically remove legacy format records when switching to new-format-only mode. You'll need to clean up the old records manually if desired.
|
||||
|
||||
When transitioning from dual-format to new-format-only records:
|
||||
|
||||
- Ensure all your `external-dns` instances support the new format
|
||||
- Enable the `--txt-new-format-only` flag on your external-dns instances
|
||||
Manually clean up any existing legacy format TXT records from your DNS provider
|
||||
|
||||
Note: `external-dns` will not automatically remove legacy format records when switching to new-format-only mode. You'll need to clean up the old records manually if desired.
|
||||
|
||||
## Prefixes and Suffixes
|
||||
|
||||
In order to avoid having the registry TXT records collide with
|
||||
|
@ -146,7 +146,6 @@ type Config struct {
|
||||
TXTSuffix string
|
||||
TXTEncryptEnabled bool
|
||||
TXTEncryptAESKey string `secure:"yes"`
|
||||
TXTNewFormatOnly bool
|
||||
Interval time.Duration
|
||||
MinEventSyncInterval time.Duration
|
||||
Once bool
|
||||
@ -367,7 +366,6 @@ var defaultConfig = &Config{
|
||||
TXTCacheInterval: 0,
|
||||
TXTEncryptAESKey: "",
|
||||
TXTEncryptEnabled: false,
|
||||
TXTNewFormatOnly: false,
|
||||
TXTOwnerID: "default",
|
||||
TXTPrefix: "",
|
||||
TXTSuffix: "",
|
||||
@ -625,7 +623,6 @@ func App(cfg *Config) *kingpin.Application {
|
||||
app.Flag("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)").Default(defaultConfig.TXTWildcardReplacement).StringVar(&cfg.TXTWildcardReplacement)
|
||||
app.Flag("txt-encrypt-enabled", "When using the TXT registry, set if TXT records should be encrypted before stored (default: disabled)").BoolVar(&cfg.TXTEncryptEnabled)
|
||||
app.Flag("txt-encrypt-aes-key", "When using the TXT registry, set TXT record decryption and encryption 32 byte aes key (required when --txt-encrypt=true)").Default(defaultConfig.TXTEncryptAESKey).StringVar(&cfg.TXTEncryptAESKey)
|
||||
app.Flag("txt-new-format-only", "When using the TXT registry, only use new format records which include record type information (e.g., prefix: 'a-'). Reduces number of TXT records (default: disabled)").BoolVar(&cfg.TXTNewFormatOnly)
|
||||
app.Flag("dynamodb-region", "When using the DynamoDB registry, the AWS region of the DynamoDB table (optional)").Default(cfg.AWSDynamoDBRegion).StringVar(&cfg.AWSDynamoDBRegion)
|
||||
app.Flag("dynamodb-table", "When using the DynamoDB registry, the name of the DynamoDB table (default: \"external-dns\")").Default(defaultConfig.AWSDynamoDBTable).StringVar(&cfg.AWSDynamoDBTable)
|
||||
|
||||
|
@ -101,7 +101,6 @@ var (
|
||||
TXTOwnerID: "default",
|
||||
TXTPrefix: "",
|
||||
TXTCacheInterval: 0,
|
||||
TXTNewFormatOnly: false,
|
||||
Interval: time.Minute,
|
||||
MinEventSyncInterval: 5 * time.Second,
|
||||
Once: false,
|
||||
@ -214,7 +213,6 @@ var (
|
||||
TXTOwnerID: "owner-1",
|
||||
TXTPrefix: "associated-txt-record",
|
||||
TXTCacheInterval: 12 * time.Hour,
|
||||
TXTNewFormatOnly: true,
|
||||
Interval: 10 * time.Minute,
|
||||
MinEventSyncInterval: 50 * time.Second,
|
||||
Once: true,
|
||||
@ -359,7 +357,6 @@ func TestParseFlags(t *testing.T) {
|
||||
"--txt-owner-id=owner-1",
|
||||
"--txt-prefix=associated-txt-record",
|
||||
"--txt-cache-interval=12h",
|
||||
"--txt-new-format-only",
|
||||
"--dynamodb-table=custom-table",
|
||||
"--interval=10m",
|
||||
"--min-event-sync-interval=50s",
|
||||
|
@ -58,8 +58,6 @@ type TXTRegistry struct {
|
||||
// encrypt text records
|
||||
txtEncryptEnabled bool
|
||||
txtEncryptAESKey []byte
|
||||
|
||||
newFormatOnly bool
|
||||
}
|
||||
|
||||
// NewTXTRegistry returns a new TXTRegistry object. When newFormatOnly is true, it will only
|
||||
@ -68,8 +66,7 @@ type TXTRegistry struct {
|
||||
func NewTXTRegistry(provider provider.Provider, txtPrefix, txtSuffix, ownerID string,
|
||||
cacheInterval time.Duration, txtWildcardReplacement string,
|
||||
managedRecordTypes, excludeRecordTypes []string,
|
||||
txtEncryptEnabled bool, txtEncryptAESKey []byte,
|
||||
newFormatOnly bool) (*TXTRegistry, error) {
|
||||
txtEncryptEnabled bool, txtEncryptAESKey []byte) (*TXTRegistry, error) {
|
||||
if ownerID == "" {
|
||||
return nil, errors.New("owner id cannot be empty")
|
||||
}
|
||||
@ -103,7 +100,6 @@ func NewTXTRegistry(provider provider.Provider, txtPrefix, txtSuffix, ownerID st
|
||||
excludeRecordTypes: excludeRecordTypes,
|
||||
txtEncryptEnabled: txtEncryptEnabled,
|
||||
txtEncryptAESKey: txtEncryptAESKey,
|
||||
newFormatOnly: newFormatOnly,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -236,25 +232,13 @@ func (im *TXTRegistry) Records(ctx context.Context) ([]*endpoint.Endpoint, error
|
||||
func (im *TXTRegistry) generateTXTRecord(r *endpoint.Endpoint) []*endpoint.Endpoint {
|
||||
endpoints := make([]*endpoint.Endpoint, 0)
|
||||
|
||||
// Create legacy format record by default unless newFormatOnly is true
|
||||
if !im.newFormatOnly && !im.txtEncryptEnabled && !im.mapper.recordTypeInAffix() && r.RecordType != endpoint.RecordTypeAAAA {
|
||||
// old TXT record format
|
||||
txt := endpoint.NewEndpoint(im.mapper.toTXTName(r.DNSName), endpoint.RecordTypeTXT, r.Labels.Serialize(true, im.txtEncryptEnabled, im.txtEncryptAESKey))
|
||||
if txt != nil {
|
||||
txt.WithSetIdentifier(r.SetIdentifier)
|
||||
txt.Labels[endpoint.OwnedRecordLabelKey] = r.DNSName
|
||||
txt.ProviderSpecific = r.ProviderSpecific
|
||||
endpoints = append(endpoints, txt)
|
||||
}
|
||||
}
|
||||
|
||||
// Always create new format record
|
||||
recordType := r.RecordType
|
||||
// AWS Alias records are encoded as type "cname"
|
||||
if isAlias, found := r.GetProviderSpecificProperty("alias"); found && isAlias == "true" && recordType == endpoint.RecordTypeA {
|
||||
recordType = endpoint.RecordTypeCNAME
|
||||
}
|
||||
txtNew := endpoint.NewEndpoint(im.mapper.toNewTXTName(r.DNSName, recordType), endpoint.RecordTypeTXT, r.Labels.Serialize(true, im.txtEncryptEnabled, im.txtEncryptAESKey))
|
||||
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
|
||||
@ -336,8 +320,7 @@ func (im *TXTRegistry) AdjustEndpoints(endpoints []*endpoint.Endpoint) ([]*endpo
|
||||
|
||||
type nameMapper interface {
|
||||
toEndpointName(string) (endpointName string, recordType string)
|
||||
toTXTName(string) string
|
||||
toNewTXTName(string, string) string
|
||||
toTXTName(string, string) string
|
||||
recordTypeInAffix() bool
|
||||
}
|
||||
|
||||
@ -437,22 +420,6 @@ func (pr affixNameMapper) toEndpointName(txtDNSName string) (endpointName string
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func (pr affixNameMapper) toTXTName(endpointDNSName string) string {
|
||||
DNSName := strings.SplitN(endpointDNSName, ".", 2)
|
||||
|
||||
prefix := pr.dropAffixTemplate(pr.prefix)
|
||||
suffix := pr.dropAffixTemplate(pr.suffix)
|
||||
// If specified, replace a leading asterisk in the generated txt record name with some other string
|
||||
if pr.wildcardReplacement != "" && DNSName[0] == "*" {
|
||||
DNSName[0] = pr.wildcardReplacement
|
||||
}
|
||||
|
||||
if len(DNSName) < 2 {
|
||||
return prefix + DNSName[0] + suffix
|
||||
}
|
||||
return prefix + DNSName[0] + suffix + "." + DNSName[1]
|
||||
}
|
||||
|
||||
func (pr affixNameMapper) recordTypeInAffix() bool {
|
||||
if strings.Contains(pr.prefix, recordTemplate) {
|
||||
return true
|
||||
@ -470,7 +437,7 @@ func (pr affixNameMapper) normalizeAffixTemplate(afix, recordType string) string
|
||||
return afix
|
||||
}
|
||||
|
||||
func (pr affixNameMapper) toNewTXTName(endpointDNSName, recordType string) string {
|
||||
func (pr affixNameMapper) toTXTName(endpointDNSName, recordType string) string {
|
||||
DNSName := strings.SplitN(endpointDNSName, ".", 2)
|
||||
recordType = strings.ToLower(recordType)
|
||||
recordT := recordType + "-"
|
||||
|
@ -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),
|
||||
|
@ -18,6 +18,7 @@ package registry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
@ -48,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)
|
||||
@ -70,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, 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)
|
||||
|
||||
_, ok = r.mapper.(affixNameMapper)
|
||||
@ -227,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))
|
||||
@ -362,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))
|
||||
@ -489,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))
|
||||
@ -526,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))
|
||||
@ -568,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))
|
||||
@ -616,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{
|
||||
@ -640,37 +641,29 @@ func testTXTRegistryApplyChangesWithPrefix(t *testing.T) {
|
||||
expected := &plan.Changes{
|
||||
Create: []*endpoint.Endpoint{
|
||||
newEndpointWithOwnerResource("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress"),
|
||||
newEndpointWithOwnerAndOwnedRecord("txt.new-record-1.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "new-record-1.test-zone.example.org"),
|
||||
|
||||
newEndpointWithOwnerAndOwnedRecord("txt.cname-new-record-1.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "new-record-1.test-zone.example.org"),
|
||||
newEndpointWithOwnerResource("multiple.test-zone.example.org", "lb3.loadbalancer.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress").WithSetIdentifier("test-set-3"),
|
||||
newEndpointWithOwnerAndOwnedRecord("txt.multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-3"),
|
||||
newEndpointWithOwnerAndOwnedRecord("txt.cname-multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-3"),
|
||||
newEndpointWithOwnerResource("example", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress"),
|
||||
newEndpointWithOwnerAndOwnedRecord("txt.example", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "example"),
|
||||
newEndpointWithOwnerAndOwnedRecord("txt.cname-example", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "example"),
|
||||
},
|
||||
Delete: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"),
|
||||
newEndpointWithOwnerAndOwnedRecord("txt.foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "foobar.test-zone.example.org"),
|
||||
newEndpointWithOwnerAndOwnedRecord("txt.cname-foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "foobar.test-zone.example.org"),
|
||||
newEndpointWithOwner("multiple.test-zone.example.org", "lb1.loadbalancer.com", endpoint.RecordTypeCNAME, "owner").WithSetIdentifier("test-set-1"),
|
||||
newEndpointWithOwnerAndOwnedRecord("txt.multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-1"),
|
||||
newEndpointWithOwnerAndOwnedRecord("txt.cname-multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-1"),
|
||||
},
|
||||
UpdateNew: []*endpoint.Endpoint{
|
||||
newEndpointWithOwnerResource("tar.test-zone.example.org", "new-tar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress-2"),
|
||||
newEndpointWithOwnerAndOwnedRecord("txt.tar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress-2\"", endpoint.RecordTypeTXT, "", "tar.test-zone.example.org"),
|
||||
newEndpointWithOwnerAndOwnedRecord("txt.cname-tar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress-2\"", endpoint.RecordTypeTXT, "", "tar.test-zone.example.org"),
|
||||
newEndpointWithOwnerResource("multiple.test-zone.example.org", "new.loadbalancer.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress-2").WithSetIdentifier("test-set-2"),
|
||||
newEndpointWithOwnerAndOwnedRecord("txt.multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress-2\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-2"),
|
||||
newEndpointWithOwnerAndOwnedRecord("txt.cname-multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress-2\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-2"),
|
||||
},
|
||||
UpdateOld: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("tar.test-zone.example.org", "tar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"),
|
||||
newEndpointWithOwnerAndOwnedRecord("txt.tar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "tar.test-zone.example.org"),
|
||||
newEndpointWithOwnerAndOwnedRecord("txt.cname-tar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "tar.test-zone.example.org"),
|
||||
newEndpointWithOwner("multiple.test-zone.example.org", "lb2.loadbalancer.com", endpoint.RecordTypeCNAME, "owner").WithSetIdentifier("test-set-2"),
|
||||
newEndpointWithOwnerAndOwnedRecord("txt.multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-2"),
|
||||
newEndpointWithOwnerAndOwnedRecord("txt.cname-multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-2"),
|
||||
},
|
||||
}
|
||||
@ -705,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"),
|
||||
@ -748,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"),
|
||||
@ -813,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{
|
||||
@ -838,40 +831,30 @@ func testTXTRegistryApplyChangesWithSuffix(t *testing.T) {
|
||||
expected := &plan.Changes{
|
||||
Create: []*endpoint.Endpoint{
|
||||
newEndpointWithOwnerResource("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress"),
|
||||
newEndpointWithOwnerAndOwnedRecord("new-record-1-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "new-record-1.test-zone.example.org"),
|
||||
newEndpointWithOwnerAndOwnedRecord("cname-new-record-1-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "new-record-1.test-zone.example.org"),
|
||||
newEndpointWithOwnerResource("multiple.test-zone.example.org", "lb3.loadbalancer.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress").WithSetIdentifier("test-set-3"),
|
||||
newEndpointWithOwnerAndOwnedRecord("multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-3"),
|
||||
newEndpointWithOwnerAndOwnedRecord("cname-multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-3"),
|
||||
newEndpointWithOwnerResource("example", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress"),
|
||||
newEndpointWithOwnerAndOwnedRecord("example-txt", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "example"),
|
||||
newEndpointWithOwnerAndOwnedRecord("cname-example-txt", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "example"),
|
||||
newEndpointWithOwnerResource("*.wildcard.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress"),
|
||||
newEndpointWithOwnerAndOwnedRecord("wildcard-txt.wildcard.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "*.wildcard.test-zone.example.org"),
|
||||
newEndpointWithOwnerAndOwnedRecord("cname-wildcard-txt.wildcard.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "*.wildcard.test-zone.example.org"),
|
||||
},
|
||||
Delete: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"),
|
||||
newEndpointWithOwnerAndOwnedRecord("foobar-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "foobar.test-zone.example.org"),
|
||||
newEndpointWithOwnerAndOwnedRecord("cname-foobar-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "foobar.test-zone.example.org"),
|
||||
newEndpointWithOwner("multiple.test-zone.example.org", "lb1.loadbalancer.com", endpoint.RecordTypeCNAME, "owner").WithSetIdentifier("test-set-1"),
|
||||
newEndpointWithOwnerAndOwnedRecord("multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-1"),
|
||||
newEndpointWithOwnerAndOwnedRecord("cname-multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-1"),
|
||||
},
|
||||
UpdateNew: []*endpoint.Endpoint{
|
||||
newEndpointWithOwnerResource("tar.test-zone.example.org", "new-tar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress-2"),
|
||||
newEndpointWithOwnerAndOwnedRecord("tar-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress-2\"", endpoint.RecordTypeTXT, "", "tar.test-zone.example.org"),
|
||||
newEndpointWithOwnerAndOwnedRecord("cname-tar-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress-2\"", endpoint.RecordTypeTXT, "", "tar.test-zone.example.org"),
|
||||
newEndpointWithOwnerResource("multiple.test-zone.example.org", "new.loadbalancer.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress-2").WithSetIdentifier("test-set-2"),
|
||||
newEndpointWithOwnerAndOwnedRecord("multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress-2\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-2"),
|
||||
newEndpointWithOwnerAndOwnedRecord("cname-multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress-2\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-2"),
|
||||
},
|
||||
UpdateOld: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("tar.test-zone.example.org", "tar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"),
|
||||
newEndpointWithOwnerAndOwnedRecord("tar-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "tar.test-zone.example.org"),
|
||||
newEndpointWithOwnerAndOwnedRecord("cname-tar-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "tar.test-zone.example.org"),
|
||||
newEndpointWithOwner("multiple.test-zone.example.org", "lb2.loadbalancer.com", endpoint.RecordTypeCNAME, "owner").WithSetIdentifier("test-set-2"),
|
||||
newEndpointWithOwnerAndOwnedRecord("multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-2"),
|
||||
newEndpointWithOwnerAndOwnedRecord("cname-multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-2"),
|
||||
},
|
||||
}
|
||||
@ -917,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{
|
||||
@ -938,19 +921,14 @@ func testTXTRegistryApplyChangesNoPrefix(t *testing.T) {
|
||||
expected := &plan.Changes{
|
||||
Create: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner"),
|
||||
newEndpointWithOwnerAndOwnedRecord("new-record-1.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "new-record-1.test-zone.example.org"),
|
||||
newEndpointWithOwnerAndOwnedRecord("cname-new-record-1.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "new-record-1.test-zone.example.org"),
|
||||
newEndpointWithOwner("example", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner"),
|
||||
newEndpointWithOwnerAndOwnedRecord("example", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "example"),
|
||||
newEndpointWithOwnerAndOwnedRecord("cname-example", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "example"),
|
||||
newEndpointWithOwner("new-alias.test-zone.example.org", "my-domain.com", endpoint.RecordTypeA, "owner").WithProviderSpecific("alias", "true"),
|
||||
// TODO: It's not clear why the TXT registry copies ProviderSpecificProperties to ownership records; that doesn't seem correct.
|
||||
newEndpointWithOwnerAndOwnedRecord("new-alias.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "new-alias.test-zone.example.org").WithProviderSpecific("alias", "true"),
|
||||
newEndpointWithOwnerAndOwnedRecord("cname-new-alias.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "new-alias.test-zone.example.org").WithProviderSpecific("alias", "true"),
|
||||
},
|
||||
Delete: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"),
|
||||
newEndpointWithOwnerAndOwnedRecord("foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "foobar.test-zone.example.org"),
|
||||
newEndpointWithOwnerAndOwnedRecord("cname-foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "foobar.test-zone.example.org"),
|
||||
},
|
||||
UpdateNew: []*endpoint.Endpoint{},
|
||||
@ -1080,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))
|
||||
@ -1190,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))
|
||||
@ -1454,7 +1432,7 @@ func TestToEndpointNameNewTXT(t *testing.T) {
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
txtDomain := tc.mapper.toNewTXTName(tc.domain, tc.recordType)
|
||||
txtDomain := tc.mapper.toTXTName(tc.domain, tc.recordType)
|
||||
assert.Equal(t, tc.txtDomain, txtDomain)
|
||||
|
||||
domain, _ := tc.mapper.toEndpointName(txtDomain)
|
||||
@ -1485,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{
|
||||
@ -1505,15 +1483,12 @@ func TestNewTXTScheme(t *testing.T) {
|
||||
expected := &plan.Changes{
|
||||
Create: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner"),
|
||||
newEndpointWithOwnerAndOwnedRecord("new-record-1.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "new-record-1.test-zone.example.org"),
|
||||
newEndpointWithOwnerAndOwnedRecord("cname-new-record-1.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "new-record-1.test-zone.example.org"),
|
||||
newEndpointWithOwner("example", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner"),
|
||||
newEndpointWithOwnerAndOwnedRecord("example", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "example"),
|
||||
newEndpointWithOwnerAndOwnedRecord("cname-example", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "example"),
|
||||
},
|
||||
Delete: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"),
|
||||
newEndpointWithOwnerAndOwnedRecord("foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "foobar.test-zone.example.org"),
|
||||
newEndpointWithOwnerAndOwnedRecord("cname-foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "foobar.test-zone.example.org"),
|
||||
},
|
||||
UpdateNew: []*endpoint.Endpoint{},
|
||||
@ -1536,20 +1511,13 @@ func TestNewTXTScheme(t *testing.T) {
|
||||
assert.Nil(t, ctx.Value(provider.RecordsContextKey))
|
||||
}
|
||||
err := r.ApplyChanges(ctx, changes)
|
||||
fmt.Println(err)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestGenerateTXT(t *testing.T) {
|
||||
record := newEndpointWithOwner("foo.test-zone.example.org", "new-foo.loadbalancer.com", endpoint.RecordTypeCNAME, "owner")
|
||||
expectedTXT := []*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: "cname-foo.test-zone.example.org",
|
||||
Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=owner\""},
|
||||
@ -1561,7 +1529,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)
|
||||
}
|
||||
@ -1580,7 +1548,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)
|
||||
}
|
||||
@ -1597,7 +1565,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)
|
||||
}
|
||||
@ -1615,7 +1583,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,
|
||||
@ -1661,7 +1629,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
|
||||
@ -1705,53 +1673,10 @@ func TestMultiClusterDifferentRecordTypeOwnership(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
helper methods
|
||||
|
||||
*/
|
||||
|
||||
func newEndpointWithOwner(dnsName, target, recordType, ownerID string) *endpoint.Endpoint {
|
||||
return newEndpointWithOwnerAndLabels(dnsName, target, recordType, ownerID, nil)
|
||||
}
|
||||
|
||||
func newEndpointWithOwnerAndOwnedRecord(dnsName, target, recordType, ownerID, ownedRecord string) *endpoint.Endpoint {
|
||||
return newEndpointWithOwnerAndLabels(dnsName, target, recordType, ownerID, endpoint.Labels{endpoint.OwnedRecordLabelKey: ownedRecord})
|
||||
}
|
||||
|
||||
func newEndpointWithOwnerAndLabels(dnsName, target, recordType, ownerID string, labels endpoint.Labels) *endpoint.Endpoint {
|
||||
e := endpoint.NewEndpoint(dnsName, recordType, target)
|
||||
e.Labels[endpoint.OwnerLabelKey] = ownerID
|
||||
for k, v := range labels {
|
||||
e.Labels[k] = v
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func newEndpointWithOwnerResource(dnsName, target, recordType, ownerID, resource string) *endpoint.Endpoint {
|
||||
e := endpoint.NewEndpoint(dnsName, recordType, target)
|
||||
e.Labels[endpoint.OwnerLabelKey] = ownerID
|
||||
e.Labels[endpoint.ResourceLabelKey] = resource
|
||||
return e
|
||||
}
|
||||
|
||||
func TestNewTXTRegistryWithNewFormatOnly(t *testing.T) {
|
||||
p := inmemory.NewInMemoryProvider()
|
||||
|
||||
r, err := NewTXTRegistry(p, "txt", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, r.newFormatOnly)
|
||||
|
||||
r, err = NewTXTRegistry(p, "txt", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, true)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, r.newFormatOnly)
|
||||
}
|
||||
|
||||
func TestGenerateTXTRecordWithNewFormatOnly(t *testing.T) {
|
||||
p := inmemory.NewInMemoryProvider()
|
||||
testCases := []struct {
|
||||
name string
|
||||
newFormatOnly bool
|
||||
endpoint *endpoint.Endpoint
|
||||
expectedRecords int
|
||||
expectedPrefix string
|
||||
@ -1759,15 +1684,13 @@ func TestGenerateTXTRecordWithNewFormatOnly(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "legacy format enabled - standard record",
|
||||
newFormatOnly: false,
|
||||
endpoint: newEndpointWithOwner("foo.test-zone.example.org", "1.2.3.4", endpoint.RecordTypeA, "owner"),
|
||||
expectedRecords: 2,
|
||||
expectedRecords: 1,
|
||||
expectedPrefix: "a-",
|
||||
description: "Should generate both old and new format TXT records",
|
||||
description: "Should generate only new format TXT records",
|
||||
},
|
||||
{
|
||||
name: "new format only - standard record",
|
||||
newFormatOnly: true,
|
||||
endpoint: newEndpointWithOwner("foo.test-zone.example.org", "1.2.3.4", endpoint.RecordTypeA, "owner"),
|
||||
expectedRecords: 1,
|
||||
expectedPrefix: "a-",
|
||||
@ -1775,7 +1698,6 @@ func TestGenerateTXTRecordWithNewFormatOnly(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "legacy format enabled - AAAA record",
|
||||
newFormatOnly: false,
|
||||
endpoint: newEndpointWithOwner("foo.test-zone.example.org", "2001:db8::1", endpoint.RecordTypeAAAA, "owner"),
|
||||
expectedRecords: 1,
|
||||
expectedPrefix: "aaaa-",
|
||||
@ -1783,7 +1705,6 @@ func TestGenerateTXTRecordWithNewFormatOnly(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "new format only - AAAA record",
|
||||
newFormatOnly: true,
|
||||
endpoint: newEndpointWithOwner("foo.test-zone.example.org", "2001:db8::1", endpoint.RecordTypeAAAA, "owner"),
|
||||
expectedRecords: 1,
|
||||
expectedPrefix: "aaaa-",
|
||||
@ -1793,7 +1714,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, tc.newFormatOnly)
|
||||
r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil)
|
||||
records := r.generateTXTRecord(tc.endpoint)
|
||||
|
||||
assert.Len(t, records, tc.expectedRecords, tc.description)
|
||||
@ -1802,7 +1723,7 @@ func TestGenerateTXTRecordWithNewFormatOnly(t *testing.T) {
|
||||
assert.Equal(t, endpoint.RecordTypeTXT, record.RecordType)
|
||||
}
|
||||
|
||||
if tc.newFormatOnly || tc.endpoint.RecordType == endpoint.RecordTypeAAAA {
|
||||
if tc.endpoint.RecordType == endpoint.RecordTypeAAAA {
|
||||
hasNewFormat := false
|
||||
for _, record := range records {
|
||||
if strings.HasPrefix(record.DNSName, tc.expectedPrefix) {
|
||||
@ -1822,7 +1743,7 @@ func TestApplyChangesWithNewFormatOnly(t *testing.T) {
|
||||
p.CreateZone(testZone)
|
||||
ctx := context.Background()
|
||||
|
||||
r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, true)
|
||||
r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil)
|
||||
|
||||
changes := &plan.Changes{
|
||||
Create: []*endpoint.Endpoint{
|
||||
@ -1870,7 +1791,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)
|
||||
|
45
registry/txt_utils_test.go
Normal file
45
registry/txt_utils_test.go
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright 2025 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package registry
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
)
|
||||
|
||||
func newEndpointWithOwner(dnsName, target, recordType, ownerID string) *endpoint.Endpoint {
|
||||
return newEndpointWithOwnerAndLabels(dnsName, target, recordType, ownerID, nil)
|
||||
}
|
||||
|
||||
func newEndpointWithOwnerAndOwnedRecord(dnsName, target, recordType, ownerID, ownedRecord string) *endpoint.Endpoint {
|
||||
return newEndpointWithOwnerAndLabels(dnsName, target, recordType, ownerID, endpoint.Labels{endpoint.OwnedRecordLabelKey: ownedRecord})
|
||||
}
|
||||
|
||||
func newEndpointWithOwnerAndLabels(dnsName, target, recordType, ownerID string, labels endpoint.Labels) *endpoint.Endpoint {
|
||||
e := endpoint.NewEndpoint(dnsName, recordType, target)
|
||||
e.Labels[endpoint.OwnerLabelKey] = ownerID
|
||||
for k, v := range labels {
|
||||
e.Labels[k] = v
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func newEndpointWithOwnerResource(dnsName, target, recordType, ownerID, resource string) *endpoint.Endpoint {
|
||||
e := endpoint.NewEndpoint(dnsName, recordType, target)
|
||||
e.Labels[endpoint.OwnerLabelKey] = ownerID
|
||||
e.Labels[endpoint.ResourceLabelKey] = resource
|
||||
return e
|
||||
}
|
215
scripts/aws-cleanup-legacy-txt-records.py
Executable file
215
scripts/aws-cleanup-legacy-txt-records.py
Executable file
@ -0,0 +1,215 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2025 The Kubernetes Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Warning: The script deletes all records that match certain values. It could delete both legacy and new records if there is no way to differentiate them.
|
||||
|
||||
# This Python script is designed to help migrate DNS management to `external-dns` by cleaning up legacy TXT records in AWS Route 53.
|
||||
# It identifies and deletes TXT records that match a specified pattern, ensuring that `external-dns` can take over managing these resources.
|
||||
# The script performs the following steps:
|
||||
#
|
||||
# 1. **Setup and Configuration**:
|
||||
# - Imports necessary libraries (`boto3`, `argparse`, etc.).
|
||||
# - Defines constants and utility functions.
|
||||
# - Parses command-line arguments for configuration.
|
||||
#
|
||||
# 2. **Record Class**:
|
||||
# - Represents a DNS record with methods to check if it should be deleted.
|
||||
#
|
||||
# 3. **Main Functionality**:
|
||||
# - Connects to AWS Route 53 using `boto3`.
|
||||
# - Support single zone cleanup at a time.
|
||||
# - Lists and filters TXT records based on the specified pattern.
|
||||
# - Deletes the filtered records in batches, with an option for a dry run or actual deletion.
|
||||
#
|
||||
# 4. **Execution**:
|
||||
# - The script is executed with command-line arguments specifying the hosted zone ID, record pattern, total items to delete, batch size, and whether to perform a dry run or actual deletion.
|
||||
# - Check 'To Run script' section for more details
|
||||
|
||||
# WARNING: run this script at your own RISK. This will delete all the TXT records that do contain certain string.
|
||||
# To Run script
|
||||
# 1. Python, pip and pipenv installed https://pipenv.pypa.io/en/latest/
|
||||
# 2. AWS Access https://docs.aws.amazon.com/signin/latest/userguide/command-line-sign-in.html
|
||||
# 3. pipenv shell
|
||||
# 4. pip install boto3
|
||||
# 5. python scripts/aws-cleanup-legacy-txt-records.py --help
|
||||
# 6. DRY RUN python scripts/aws-cleanup-legacy-txt-records.py --zone-id ASDFQEQREWRQADF --record-match text
|
||||
# 6.1 Before execution consider to stop `external-dns`
|
||||
# 7. Execute Deletion. First few times with reduced number of items
|
||||
# - python scripts/aws-cleanup-legacy-txt-records.py --zone-id ASDFQEQREWRQADF --total-items 3 --batch-delete-count 1 --record-match 'external-dns'
|
||||
# - python scripts/aws-cleanup-legacy-txt-records.py --zone-id ASDFQEQREWRQADF --total-items 10000 --batch-delete-count 50 --run --record-match "external-dns/owner=default"
|
||||
|
||||
# python scripts/aws-cleanup-legacy-txt-records.py --help
|
||||
# python scripts/aws-cleanup-legacy-txt-records.py --zone-id Z06155043AVN8RVC88TYY --total-items 300 --batch-delete-count 20 --record-match "external-dns/owner=default" --run
|
||||
|
||||
import boto3
|
||||
from botocore.config import Config as AwsConfig
|
||||
import json, argparse, os, uuid, time
|
||||
|
||||
MAX_ITEMS=300 # max is 300 https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/route53/client/list_resource_record_sets.html
|
||||
SLEEP=1 # in seconds, required to make sure Route53 API is not throttled
|
||||
SESSION_ID=uuid.uuid4()
|
||||
|
||||
def json_prettify(data):
|
||||
return json.dumps(data, indent=4, default=str)
|
||||
|
||||
class Record:
|
||||
|
||||
def __init__(self, record):
|
||||
# static
|
||||
self.type = 'TXT'
|
||||
self.record = record
|
||||
self.name = record['Name']
|
||||
self.resource_records = record['ResourceRecords']
|
||||
resource_record = ''
|
||||
for r in self.resource_records:
|
||||
resource_record += r['Value']
|
||||
self.resource_record = resource_record
|
||||
|
||||
def is_for_deletion(self, contains):
|
||||
|
||||
if contains in self.resource_record:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
return f'record: name: {self.name}, type: {self.type}, records: {self.resource_record}'
|
||||
|
||||
class Config:
|
||||
|
||||
def __init__(self, zone_id, contain, total_items, batch, run):
|
||||
self.zone_id = zone_id
|
||||
self.record_contain = contain
|
||||
self.total_items = total_items
|
||||
self.batch_size = batch
|
||||
self.run = run
|
||||
self.contain = contain
|
||||
|
||||
def records(config: Config) -> None:
|
||||
print(f"calculate TXT records to cleanup for 'zone:{config.zone_id}' and 'max records:{config.total_items}'")
|
||||
# https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html
|
||||
cfg = AwsConfig(
|
||||
user_agent=f"ExternalDNS/boto3-{SESSION_ID}",
|
||||
)
|
||||
r53client = boto3.client('route53', config=cfg)
|
||||
dns_records_to_cleanup = []
|
||||
items = 0
|
||||
try:
|
||||
params = {
|
||||
'HostedZoneId': config.zone_id,
|
||||
'MaxItems': str(MAX_ITEMS),
|
||||
}
|
||||
dns_in_iteration = r53client.list_resource_record_sets(**params)
|
||||
elements = dns_in_iteration['ResourceRecordSets']
|
||||
for el in elements:
|
||||
if el['Type'] == 'TXT':
|
||||
record = Record(el)
|
||||
if record.is_for_deletion(config.contain):
|
||||
dns_records_to_cleanup.append(record)
|
||||
print("to cleanup >>", record)
|
||||
items += 1
|
||||
if items >= config.total_items:
|
||||
break
|
||||
|
||||
while len(elements) > 0 and 'NextRecordName' in dns_in_iteration.keys() and items < config.total_items:
|
||||
dns_in_iteration = r53client.list_resource_record_sets(
|
||||
HostedZoneId= config.zone_id,
|
||||
StartRecordName= dns_in_iteration['NextRecordName'],
|
||||
MaxItems= str(MAX_ITEMS),
|
||||
)
|
||||
elements = dns_in_iteration['ResourceRecordSets']
|
||||
for el in elements:
|
||||
if el['Type'] == 'TXT':
|
||||
record = Record(el)
|
||||
if record.is_for_deletion(config.contain):
|
||||
dns_records_to_cleanup.append(record)
|
||||
print("to cleanup >>", record)
|
||||
items += 1
|
||||
if items >= config.total_items:
|
||||
break
|
||||
|
||||
if len(dns_records_to_cleanup) > 0:
|
||||
delete_records(r53client, config, dns_records_to_cleanup)
|
||||
else:
|
||||
print("No 'TXT' records found to cleanup....")
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
os._exit(os.EX_OSERR)
|
||||
|
||||
def delete_records(client: boto3.client, config: Config, records: list[Record]) -> None:
|
||||
total=len(records)
|
||||
print(f"will cleanup '{total}' records with batch '{config.batch_size}' at a time")
|
||||
count = 0
|
||||
|
||||
if config.run:
|
||||
print("deletion of records!!")
|
||||
else:
|
||||
print("dry run execution")
|
||||
|
||||
for i in range(0, total, config.batch_size):
|
||||
if config.batch_size <= 0:
|
||||
break
|
||||
batch = records[i:min(i + config.batch_size, total)]
|
||||
count += config.batch_size
|
||||
if count >= total:
|
||||
count = total
|
||||
|
||||
changes = []
|
||||
|
||||
for el in batch:
|
||||
# https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/route53/client/change_resource_record_sets.html
|
||||
changes.append({
|
||||
'Action': 'DELETE',
|
||||
'ResourceRecordSet': el.record
|
||||
})
|
||||
|
||||
print(f"BATCH deletion(start). {len(changes)} records > {changes}")
|
||||
|
||||
if config.run:
|
||||
client.change_resource_record_sets(
|
||||
HostedZoneId=config.zone_id,
|
||||
ChangeBatch={
|
||||
"Comment": "external-dns legacy record cleanup. batch of ",
|
||||
"Changes": changes,
|
||||
}
|
||||
)
|
||||
time.sleep(SLEEP)
|
||||
|
||||
print(f"BATCH deletion(success). {count}/{total}(deleted/total)")
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Cleanup legacy TXT records")
|
||||
parser.add_argument("--zone-id", type=str, required=True, help="Hosted Zone ID for which to run a cleanup.")
|
||||
parser.add_argument("--record-match", type=str, required=True, help="Record to match specific value. Example 'external-dns/owner=default'")
|
||||
parser.add_argument("--total-items", type=int, required=False, default=10, help="Number of items to delete. Default to 10")
|
||||
parser.add_argument("--batch-delete-count", type=int, required=False, default=2, help="Number of items to delete in single DELETE batch. Default to 2")
|
||||
parser.add_argument("--run", action="store_true", help="Execute the cleanup. The tool will do a dry-run if --run is not specified.")
|
||||
|
||||
answer = input("Run this script at your own RISKS!!! Please enter 'yes' or 'no': ")
|
||||
if answer != 'yes':
|
||||
os._exit(0)
|
||||
|
||||
print(f"Session ID '{SESSION_ID}'")
|
||||
|
||||
args = parser.parse_args()
|
||||
print("arguments:",args)
|
||||
cfg = Config(
|
||||
zone_id=args.zone_id,
|
||||
contain=args.record_match,
|
||||
total_items=args.total_items,
|
||||
batch=args.batch_delete_count,
|
||||
run=args.run,
|
||||
)
|
||||
records(cfg)
|
Loading…
Reference in New Issue
Block a user