From 376871961f9e2afb555c1b5a9c1b5a1bd334e6de Mon Sep 17 00:00:00 2001 From: Thomas Limoncelli Date: Wed, 3 Dec 2025 14:23:03 -0500 Subject: [PATCH 1/6] fixup! --- integrationTest/integration_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/integrationTest/integration_test.go b/integrationTest/integration_test.go index 6f999cdfc..a4320fad7 100644 --- a/integrationTest/integration_test.go +++ b/integrationTest/integration_test.go @@ -223,10 +223,10 @@ func makeTests() []*TestGroup { testgroup("TTL", not("NETCUP"), // NETCUP does not support TTLs. not("LINODE"), // Linode does not support arbitrary TTLs: 666 and 1000 are both rounded up to 3600. - tc("Start", ttl(a("@", "8.8.8.8"), 666), a("www", "1.2.3.4"), a("www", "5.6.7.8")), - tc("Change a ttl", ttl(a("@", "8.8.8.8"), 1000), a("www", "1.2.3.4"), a("www", "5.6.7.8")), - tc("Change single target from set", ttl(a("@", "8.8.8.8"), 1000), a("www", "2.2.2.2"), a("www", "5.6.7.8")), - tc("Change all ttls", ttl(a("@", "8.8.8.8"), 900), ttl(a("www", "2.2.2.2"), 800), ttl(a("www", "5.6.7.8"), 700)), + tc("Start", ttl(a("@", "8.8.8.8"), 900), ttl(a("www", "1.2.3.4"), 800), ttl(a("www", "5.6.7.8"), 700)), + tc("Change a ttl", ttl(a("@", "8.8.8.8"), 911), ttl(a("www", "1.2.3.4"), 800), ttl(a("www", "5.6.7.8"), 700)), + tc("Change single ttl in set", ttl(a("@", "8.8.8.8"), 911), ttl(a("www", "1.2.3.4"), 822), ttl(a("www", "5.6.7.8"), 700)), + tc("Change all ttls", ttl(a("@", "8.8.8.8"), 933), ttl(a("www", "2.2.2.2"), 933), ttl(a("www", "5.6.7.8"), 933)), ), // Narrative: Did you see that `not("NETCUP")` code? NETCUP just From 24270bf1b8ea8ee52eee76729a978c791b257dc9 Mon Sep 17 00:00:00 2001 From: Thomas Limoncelli Date: Wed, 3 Dec 2025 14:38:28 -0500 Subject: [PATCH 2/6] wip! --- commands/types/dnscontrol.d.ts | 7 ++++ models/domain.go | 4 +- pkg/diff2/handsoff_test.go | 14 +++---- .../parse_tests/058-ignore-external-dns.json | 40 +++++++++---------- 4 files changed, 36 insertions(+), 29 deletions(-) diff --git a/commands/types/dnscontrol.d.ts b/commands/types/dnscontrol.d.ts index ae3ab5ac1..65d694af9 100644 --- a/commands/types/dnscontrol.d.ts +++ b/commands/types/dnscontrol.d.ts @@ -1773,6 +1773,13 @@ declare function IGNORE(labelSpec: string, typeSpec?: string, targetSpec?: strin * * ## Caveats * + * ### One per domain + * + * Only one `IGNORE_EXTERNAL_DNS()` should be used per domain. If you call it multiple + * times, the last prefix wins. If you have multiple external-dns instances with + * different prefixes managing the same zone, use `IGNORE()` patterns for additional + * prefixes. + * * ### TXT Registry Format * * This feature relies on external-dns's [TXT registry](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/registry/txt.md), diff --git a/models/domain.go b/models/domain.go index fa8bd9ba6..e45b1fbb8 100644 --- a/models/domain.go +++ b/models/domain.go @@ -40,8 +40,8 @@ type DomainConfig struct { Unmanaged []*UnmanagedConfig `json:"unmanaged,omitempty"` // IGNORE() UnmanagedUnsafe bool `json:"unmanaged_disable_safety_check,omitempty"` // DISABLE_IGNORE_SAFETY_CHECK - IgnoreExternalDNS bool `json:"ignore_external_dns,omitempty"` // IGNORE_EXTERNAL_DNS - ExternalDNSPrefix string `json:"external_dns_prefix,omitempty"` // IGNORE_EXTERNAL_DNS prefix + IgnoreExternalDNS bool `json:"ignore_external_dns,omitempty"` // IGNORE_EXTERNAL_DNS + ExternalDNSPrefix string `json:"external_dns_prefix,omitempty"` // IGNORE_EXTERNAL_DNS prefix AutoDNSSEC string `json:"auto_dnssec,omitempty"` // "", "on", "off" // DNSSEC bool `json:"dnssec,omitempty"` diff --git a/pkg/diff2/handsoff_test.go b/pkg/diff2/handsoff_test.go index ebf2b55c4..b0345d1f7 100644 --- a/pkg/diff2/handsoff_test.go +++ b/pkg/diff2/handsoff_test.go @@ -266,8 +266,8 @@ func Test_ignore_external_dns(t *testing.T) { domain, existing, desired, - nil, // absences - nil, // unmanagedConfigs + nil, // absences + nil, // unmanagedConfigs false, // unmanagedSafely false, // noPurge true, // ignoreExternalDNS @@ -352,11 +352,11 @@ func Test_ignore_external_dns_custom_prefix(t *testing.T) { domain, existing, desired, - nil, // absences - nil, // unmanagedConfigs - false, // unmanagedSafely - false, // noPurge - true, // ignoreExternalDNS + nil, // absences + nil, // unmanagedConfigs + false, // unmanagedSafely + false, // noPurge + true, // ignoreExternalDNS "extdns-", // externalDNSPrefix ) if err != nil { diff --git a/pkg/js/parse_tests/058-ignore-external-dns.json b/pkg/js/parse_tests/058-ignore-external-dns.json index 7bd32d3a0..0a4f1306d 100644 --- a/pkg/js/parse_tests/058-ignore-external-dns.json +++ b/pkg/js/parse_tests/058-ignore-external-dns.json @@ -1,52 +1,52 @@ { - "registrars": [], "dns_providers": [], "domains": [ { - "name": "extdns-default.com", - "registrar": "none", "dnsProviders": {}, + "ignore_external_dns": true, "meta": { "dnscontrol_uniquename": "extdns-default.com" }, + "name": "extdns-default.com", "records": [], - "ignore_external_dns": true + "registrar": "none" }, { - "name": "extdns-custom.com", - "registrar": "none", "dnsProviders": {}, + "external_dns_prefix": "extdns-", + "ignore_external_dns": true, "meta": { "dnscontrol_uniquename": "extdns-custom.com" }, + "name": "extdns-custom.com", "records": [], - "ignore_external_dns": true, - "external_dns_prefix": "extdns-" + "registrar": "none" }, { - "name": "extdns-combined.com", - "registrar": "none", "dnsProviders": {}, + "ignore_external_dns": true, "meta": { "dnscontrol_uniquename": "extdns-combined.com" }, + "name": "extdns-combined.com", "records": [ { - "type": "CNAME", - "name": "api", - "ttl": 300, "filepos": "[line:12:5]", - "target": "www.extdns-combined.com." + "name": "api", + "target": "www.extdns-combined.com.", + "ttl": 300, + "type": "CNAME" }, { - "type": "A", - "name": "www", - "ttl": 300, "filepos": "[line:11:5]", - "target": "1.2.3.4" + "name": "www", + "target": "1.2.3.4", + "ttl": 300, + "type": "A" } ], - "ignore_external_dns": true + "registrar": "none" } - ] + ], + "registrars": [] } From e87f03a8a38310359f8d990802c37f940bfb2d34 Mon Sep 17 00:00:00 2001 From: Tom Limoncelli <6293917+tlimoncelli@users.noreply.github.com> Date: Wed, 3 Dec 2025 14:53:02 -0500 Subject: [PATCH 3/6] CHORE: fmt (#3882) --- commands/types/dnscontrol.d.ts | 7 ++++ models/domain.go | 4 +- package-lock.json | 11 ++--- package.json | 2 +- pkg/diff2/handsoff_test.go | 14 +++---- pkg/js/helpers.js | 12 +++--- .../parse_tests/058-ignore-external-dns.json | 40 +++++++++---------- 7 files changed, 49 insertions(+), 41 deletions(-) diff --git a/commands/types/dnscontrol.d.ts b/commands/types/dnscontrol.d.ts index ae3ab5ac1..65d694af9 100644 --- a/commands/types/dnscontrol.d.ts +++ b/commands/types/dnscontrol.d.ts @@ -1773,6 +1773,13 @@ declare function IGNORE(labelSpec: string, typeSpec?: string, targetSpec?: strin * * ## Caveats * + * ### One per domain + * + * Only one `IGNORE_EXTERNAL_DNS()` should be used per domain. If you call it multiple + * times, the last prefix wins. If you have multiple external-dns instances with + * different prefixes managing the same zone, use `IGNORE()` patterns for additional + * prefixes. + * * ### TXT Registry Format * * This feature relies on external-dns's [TXT registry](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/registry/txt.md), diff --git a/models/domain.go b/models/domain.go index fa8bd9ba6..e45b1fbb8 100644 --- a/models/domain.go +++ b/models/domain.go @@ -40,8 +40,8 @@ type DomainConfig struct { Unmanaged []*UnmanagedConfig `json:"unmanaged,omitempty"` // IGNORE() UnmanagedUnsafe bool `json:"unmanaged_disable_safety_check,omitempty"` // DISABLE_IGNORE_SAFETY_CHECK - IgnoreExternalDNS bool `json:"ignore_external_dns,omitempty"` // IGNORE_EXTERNAL_DNS - ExternalDNSPrefix string `json:"external_dns_prefix,omitempty"` // IGNORE_EXTERNAL_DNS prefix + IgnoreExternalDNS bool `json:"ignore_external_dns,omitempty"` // IGNORE_EXTERNAL_DNS + ExternalDNSPrefix string `json:"external_dns_prefix,omitempty"` // IGNORE_EXTERNAL_DNS prefix AutoDNSSEC string `json:"auto_dnssec,omitempty"` // "", "on", "off" // DNSSEC bool `json:"dnssec,omitempty"` diff --git a/package-lock.json b/package-lock.json index 4fa5d2c5a..8a58e55a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "dependencies": { "@umbrelladocs/linkspector": "^0.3.13", - "prettier": "^3.6.2" + "prettier": "^3.7.4" } }, "node_modules/@babel/code-frame": { @@ -715,7 +715,8 @@ "version": "0.0.1367902", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1367902.tgz", "integrity": "sha512-XxtPuC3PGakY6PD7dG66/o8KwJ/LkH2/EKe19Dcw58w53dv4/vSQEkn/SzuyhHE2q4zPgCkxQBxus3VV4ql+Pg==", - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/eastasianwidth": { "version": "0.2.0", @@ -2221,9 +2222,9 @@ "license": "ISC" }, "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" diff --git a/package.json b/package.json index ea57754e9..251b962a0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "dependencies": { "@umbrelladocs/linkspector": "^0.3.13", - "prettier": "^3.6.2" + "prettier": "^3.7.4" } } diff --git a/pkg/diff2/handsoff_test.go b/pkg/diff2/handsoff_test.go index ebf2b55c4..b0345d1f7 100644 --- a/pkg/diff2/handsoff_test.go +++ b/pkg/diff2/handsoff_test.go @@ -266,8 +266,8 @@ func Test_ignore_external_dns(t *testing.T) { domain, existing, desired, - nil, // absences - nil, // unmanagedConfigs + nil, // absences + nil, // unmanagedConfigs false, // unmanagedSafely false, // noPurge true, // ignoreExternalDNS @@ -352,11 +352,11 @@ func Test_ignore_external_dns_custom_prefix(t *testing.T) { domain, existing, desired, - nil, // absences - nil, // unmanagedConfigs - false, // unmanagedSafely - false, // noPurge - true, // ignoreExternalDNS + nil, // absences + nil, // unmanagedConfigs + false, // unmanagedSafely + false, // noPurge + true, // ignoreExternalDNS "extdns-", // externalDNSPrefix ) if err != nil { diff --git a/pkg/js/helpers.js b/pkg/js/helpers.js index 46260624f..f46f1e20d 100644 --- a/pkg/js/helpers.js +++ b/pkg/js/helpers.js @@ -866,15 +866,15 @@ function locStringBuilder(record, args) { (args.alt < -100000 ? -100000 : args.alt > 42849672.95 - ? 42849672.95 - : args.alt.toString()) + 'm'; + ? 42849672.95 + : args.alt.toString()) + 'm'; precisionbuffer += ' ' + (args.siz > 90000000 ? 90000000 : args.siz < 0 - ? 0 - : args.siz.toString()) + + ? 0 + : args.siz.toString()) + 'm'; precisionbuffer += ' ' + @@ -914,8 +914,8 @@ function locDMSBuilder(record, args) { record.localtitude > 4294967295 ? 4294967295 : record.localtitude < 0 - ? 0 - : record.localtitude; + ? 0 + : record.localtitude; // Size record.locsize = getENotationInt(args.siz); // Horizontal Precision diff --git a/pkg/js/parse_tests/058-ignore-external-dns.json b/pkg/js/parse_tests/058-ignore-external-dns.json index 7bd32d3a0..0a4f1306d 100644 --- a/pkg/js/parse_tests/058-ignore-external-dns.json +++ b/pkg/js/parse_tests/058-ignore-external-dns.json @@ -1,52 +1,52 @@ { - "registrars": [], "dns_providers": [], "domains": [ { - "name": "extdns-default.com", - "registrar": "none", "dnsProviders": {}, + "ignore_external_dns": true, "meta": { "dnscontrol_uniquename": "extdns-default.com" }, + "name": "extdns-default.com", "records": [], - "ignore_external_dns": true + "registrar": "none" }, { - "name": "extdns-custom.com", - "registrar": "none", "dnsProviders": {}, + "external_dns_prefix": "extdns-", + "ignore_external_dns": true, "meta": { "dnscontrol_uniquename": "extdns-custom.com" }, + "name": "extdns-custom.com", "records": [], - "ignore_external_dns": true, - "external_dns_prefix": "extdns-" + "registrar": "none" }, { - "name": "extdns-combined.com", - "registrar": "none", "dnsProviders": {}, + "ignore_external_dns": true, "meta": { "dnscontrol_uniquename": "extdns-combined.com" }, + "name": "extdns-combined.com", "records": [ { - "type": "CNAME", - "name": "api", - "ttl": 300, "filepos": "[line:12:5]", - "target": "www.extdns-combined.com." + "name": "api", + "target": "www.extdns-combined.com.", + "ttl": 300, + "type": "CNAME" }, { - "type": "A", - "name": "www", - "ttl": 300, "filepos": "[line:11:5]", - "target": "1.2.3.4" + "name": "www", + "target": "1.2.3.4", + "ttl": 300, + "type": "A" } ], - "ignore_external_dns": true + "registrar": "none" } - ] + ], + "registrars": [] } From 46c4ec431e2b9ebd1be5ced131a814c46a2e0699 Mon Sep 17 00:00:00 2001 From: Thomas Limoncelli Date: Wed, 3 Dec 2025 15:26:09 -0500 Subject: [PATCH 4/6] try DefaultTTL --- integrationTest/integration_test.go | 12 ++++++++---- models/dns.go | 2 +- providers/route53/route53Provider.go | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/integrationTest/integration_test.go b/integrationTest/integration_test.go index a4320fad7..501b2f401 100644 --- a/integrationTest/integration_test.go +++ b/integrationTest/integration_test.go @@ -6,11 +6,14 @@ import ( "strings" "testing" + "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/providers" _ "github.com/StackExchange/dnscontrol/v4/providers/_all" ) func TestDNSProviders(t *testing.T) { + models.DefaultTTL = 600 // Avoid problems due to minimum TTLs + provider, domain, cfg := getProvider(t) if provider == nil { return @@ -223,10 +226,11 @@ func makeTests() []*TestGroup { testgroup("TTL", not("NETCUP"), // NETCUP does not support TTLs. not("LINODE"), // Linode does not support arbitrary TTLs: 666 and 1000 are both rounded up to 3600. - tc("Start", ttl(a("@", "8.8.8.8"), 900), ttl(a("www", "1.2.3.4"), 800), ttl(a("www", "5.6.7.8"), 700)), - tc("Change a ttl", ttl(a("@", "8.8.8.8"), 911), ttl(a("www", "1.2.3.4"), 800), ttl(a("www", "5.6.7.8"), 700)), - tc("Change single ttl in set", ttl(a("@", "8.8.8.8"), 911), ttl(a("www", "1.2.3.4"), 822), ttl(a("www", "5.6.7.8"), 700)), - tc("Change all ttls", ttl(a("@", "8.8.8.8"), 933), ttl(a("www", "2.2.2.2"), 933), ttl(a("www", "5.6.7.8"), 933)), + // NOTE: Many providers require all records in a recordset have the same TTL. Don't add tests that break that rule. + tc("Start ", ttl(a("@", "8.8.8.8"), 600), ttl(a("www", "1.2.3.4"), 600), ttl(a("www", "5.6.7.8"), 600)), + tc("Change a ttl ", ttl(a("@", "8.8.8.8"), 700), ttl(a("www", "1.2.3.4"), 600), ttl(a("www", "5.6.7.8"), 600)), + tc("Change others ", ttl(a("@", "8.8.8.8"), 700), ttl(a("www", "2.2.2.2"), 800), ttl(a("www", "5.6.7.8"), 800)), + tc("Change all ttls", ttl(a("@", "8.8.8.8"), 900), ttl(a("www", "2.2.2.2"), 900), ttl(a("www", "5.6.7.8"), 900)), ), // Narrative: Did you see that `not("NETCUP")` code? NETCUP just diff --git a/models/dns.go b/models/dns.go index 384aea4d7..8e0243fa0 100644 --- a/models/dns.go +++ b/models/dns.go @@ -8,7 +8,7 @@ import ( ) // DefaultTTL is applied to any DNS record without an explicit TTL. -const DefaultTTL = uint32(300) +var DefaultTTL = uint32(300) // DNSConfig describes the desired DNS configuration, usually loaded from dnsconfig.js. type DNSConfig struct { diff --git a/providers/route53/route53Provider.go b/providers/route53/route53Provider.go index a695d4954..b605efa04 100644 --- a/providers/route53/route53Provider.go +++ b/providers/route53/route53Provider.go @@ -442,7 +442,7 @@ func nativeToRecords(set r53Types.ResourceRecordSet, origin string) ([]*models.R if set.AliasTarget != nil { rc := &models.RecordConfig{ Type: "R53_ALIAS", - TTL: 300, + TTL: models.DefaultTTL, R53Alias: map[string]string{ "type": string(set.Type), "zone_id": aws.ToString(set.AliasTarget.HostedZoneId), From a4cf909357dc6be3caaa3fb4eaddb86ea8f70382 Mon Sep 17 00:00:00 2001 From: Thomas Limoncelli Date: Wed, 3 Dec 2025 15:35:46 -0500 Subject: [PATCH 5/6] Only ALIDNS should get TTL=600 --- integrationTest/helpers_integration_test.go | 2 +- integrationTest/helpers_test.go | 5 +++++ integrationTest/integration_test.go | 3 --- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/integrationTest/helpers_integration_test.go b/integrationTest/helpers_integration_test.go index 0b29bcf43..052d31047 100644 --- a/integrationTest/helpers_integration_test.go +++ b/integrationTest/helpers_integration_test.go @@ -433,7 +433,7 @@ func loc(name string, d1 uint8, m1 uint8, s1 float32, ns string, func makeRec(name, target, typ string) *models.RecordConfig { r := &models.RecordConfig{ Type: typ, - TTL: 600, + TTL: models.DefaultTTL, } SetLabel(r, name, "**current-domain**.") r.MustSetTarget(target) diff --git a/integrationTest/helpers_test.go b/integrationTest/helpers_test.go index 3c174b716..d18640684 100644 --- a/integrationTest/helpers_test.go +++ b/integrationTest/helpers_test.go @@ -9,6 +9,7 @@ import ( "strings" "testing" + "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/credsfile" "github.com/StackExchange/dnscontrol/v4/providers" "github.com/StackExchange/dnscontrol/v4/providers/cloudflare" @@ -117,6 +118,10 @@ func getProvider(t *testing.T) (providers.DNSServiceProvider, string, map[string metadata = []byte(`{ ` + strings.Join(items, `, `) + ` }`) } + if profileType == "ALIDNS" { + models.DefaultTTL = 600 + } + provider, err := providers.CreateDNSProvider(profileType, cfg, metadata) if err != nil { t.Fatal(err) diff --git a/integrationTest/integration_test.go b/integrationTest/integration_test.go index 501b2f401..ec01e0a49 100644 --- a/integrationTest/integration_test.go +++ b/integrationTest/integration_test.go @@ -6,14 +6,11 @@ import ( "strings" "testing" - "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/providers" _ "github.com/StackExchange/dnscontrol/v4/providers/_all" ) func TestDNSProviders(t *testing.T) { - models.DefaultTTL = 600 // Avoid problems due to minimum TTLs - provider, domain, cfg := getProvider(t) if provider == nil { return From 24db38bf582ad79a69d50a689cd748ab8f8b728c Mon Sep 17 00:00:00 2001 From: Thomas Limoncelli Date: Wed, 3 Dec 2025 15:43:33 -0500 Subject: [PATCH 6/6] fixup! --- integrationTest/helpers_integration_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/integrationTest/helpers_integration_test.go b/integrationTest/helpers_integration_test.go index 052d31047..348193efb 100644 --- a/integrationTest/helpers_integration_test.go +++ b/integrationTest/helpers_integration_test.go @@ -570,6 +570,7 @@ func testgroup(desc string, items ...interface{}) *TestGroup { } func tc(desc string, recs ...*models.RecordConfig) *TestCase { + desc = strings.TrimSpace(desc) var records []*models.RecordConfig var unmanagedItems []*models.UnmanagedConfig for _, r := range recs {