# Issue
Fixes https://github.com/StackExchange/dnscontrol/issues/3898
After the v4.28.0 upgrade, the `%D` option in BIND's `"filenameformat"`
option no longer works (returns "") when reading the zonefile (but it
works for writing to one!). As a result, "push" always re-creates the
zonefile even if there are no changes.
# Resolution
Fix `%D` in the zone reading code.
Fixes https://github.com/StackExchange/dnscontrol/issues/3894
# Issue
* Split horizon DNS broke in 4.28.0
* Insufficient automated testing to detect this in the future
# Resolution
* domain.PostProcess() was called twice. The first time the tag was
properly parsed, removing the tag from DomainConfig.Name and putting it
in DomainConfig.Tag. The second time DomainConfig.Name no longer had the
tag and .Tag was set to "".
* The JSON output of DomainConfig doesn't output .UniqueName. If it had,
this bug would have been noticed prior to release. Test updated to
include that field.
# Issue
* New record type: "RP" (supported by BIND and GANDI_V5)
* Cloudflare: CF_REDIRECT/CF_TEMP_REDIRECT now generate
CF_SINGLE_REDIRECT records. All PAGE_RULE-based code is removed.
PAGE_RULEs are deprecated at Cloudflare. (be careful when upgrading!)
* New "v2" RecordConfig: RP and CF_SINGLE_REDIRECT are the only record
types that use this method. It shifts most of the work out of JavaScript
and into the Go code, making new record types easier to make, easier to
test, and easier to use by providers. This opens the door to new things
like a potential code-generator for rtypes. Converting existing rtypes
will happen over the next year.
* When only the TTL changes (MODIFY-TTL), the output lists the TTL
change first, not at the end of the line where it is visually lost.
* CF_REDIRECT/CF_TEMP_REDIRECT generate different rule "names". They
will be updated the first time you "push" with this release. The order
of the rules may also change. If you rules depend on a particular order,
be very careful with this upgrade!
Refactoring:
* New "v2" RecordConfig: Record types using this new method simply
package the parameters from dnsconfig.js statements like
CF_REDIRECT(foo,bar) and send them (raw) to the Go code. The Go code
does all processing, validation, etc. and turns them into RecordConfig
that store all the rdata in `RecordConfig.F`. No more adding fields to
RecordConfig for each new record type!
* RecordConfig.IsModernType() returns true if the record uses the new v2
record mechanism.
* PostProcess is now a method on DnsConfig and DomainConfig.
* DOC: How to create new rtypes using the v2 method (incomplete)
Other things:
* Integration tests for CF "full proxy" are removed. This feature
doesn't exist any more.
* DEV: Debugger tips now includes VSCode advice
* TESTING: The names of testgroup's can now have extra spaces to make
data align better
* CF_TEMP_REDIRECT/CF_REDIRECT is now a "builder" that generates
CLOUDFLAREAPI_SINGLE_REDIRECT records.
* And more!
# Resolution
---------
Co-authored-by: Jakob Ackermann <das7pad@outlook.com>
## Summary
This PR adds a new domain modifier `IGNORE_EXTERNAL_DNS()` that
automatically detects and ignores DNS records managed by Kubernetes
[external-dns](https://github.com/kubernetes-sigs/external-dns)
controller.
**Related Issue:** This addresses the feature request discussed in
StackExchange/dnscontrol#935 (Idea: Ownership system), where
@tlimoncelli indicated openness to accepting a PR for this
functionality.
## Problem
When running DNSControl alongside Kubernetes external-dns, users face a
challenge:
- **external-dns** dynamically creates DNS records based on Kubernetes
Ingress/Service resources
- Users cannot use `IGNORE()` because they cannot predict which record
names external-dns will create
- Using `NO_PURGE()` is too broad - it prevents DNSControl from cleaning
up any orphaned records
The fundamental issue is that `IGNORE()` requires static patterns known
at config-time, but external-dns creates records dynamically at runtime.
## Solution
`IGNORE_EXTERNAL_DNS()` solves this by detecting external-dns managed
records at runtime:
```javascript
D("example.com", REG_CHANGEME, DnsProvider(DSP_MY_PROVIDER),
IGNORE_EXTERNAL_DNS(), // Automatically ignore external-dns managed records
A("@", "1.2.3.4"),
CNAME("www", "@")
);
```
### How It Works
external-dns uses a TXT record registry to track ownership. For each
managed record, it creates a TXT record like:
- `a-myapp.example.com` → TXT containing
`heritage=external-dns,external-dns/owner=...`
- `cname-api.example.com` → TXT containing
`heritage=external-dns,external-dns/owner=...`
This PR:
1. Scans existing TXT records for the `heritage=external-dns` marker
2. Parses the TXT record name prefix (e.g., `a-`, `cname-`) to determine
the managed record type
3. Automatically adds those records to the ignore list during diff
operations
## Changes
| File | Purpose |
|------|---------|
| `models/domain.go` | Add `IgnoreExternalDNS` field to DomainConfig |
| `pkg/js/helpers.js` | Add `IGNORE_EXTERNAL_DNS()` JavaScript helper |
| `pkg/diff2/externaldns.go` | Core detection logic for external-dns TXT
records |
| `pkg/diff2/externaldns_test.go` | Unit tests for detection logic |
| `pkg/diff2/handsoff.go` | Integrate external-dns detection into
handsoff() |
| `pkg/diff2/diff2.go` | Pass IgnoreExternalDNS flag to handsoff() |
| `commands/types/dnscontrol.d.ts` | TypeScript definitions for IDE
support |
| `documentation/.../IGNORE_EXTERNAL_DNS.md` | User documentation |
## Design Philosophy
This follows DNSControl's pattern of convenience builders (like
`M365_BUILDER`, `SPF_BUILDER`, `DKIM_BUILDER`) that make complex
operations simple. Just as those builders abstract away implementation
details, `IGNORE_EXTERNAL_DNS()` abstracts away the complexity of
detecting external-dns managed records.
## Testing
All unit tests pass:
```
go test ./pkg/diff2/... -v # Tests detection logic
go test ./pkg/js/... # Tests JS helpers
go build ./... # Builds successfully
```
## Caveats Documented
- Only supports TXT registry (the default for external-dns)
- Requires external-dns to use default naming conventions
- May need updates if external-dns changes its registry format
---------
Co-authored-by: Tom Limoncelli <6293917+tlimoncelli@users.noreply.github.com>
# Issue
Fixes https://github.com/StackExchange/dnscontrol/issues/3842
CC @das7pad
# Resolution
Convert domain.Name to IDN earlier in the pipeline. Hack the --domains
processing to convert everything to IDN.
* Domain names are now stored 3 ways: The original input from
dnsconfig.js, canonical IDN format (`xn--...`), and Unicode format. All
are downcased. Providers that haven't been updated will receive the IDN
format instead of the original input format. This might break some
providers but only for users with unicode in their D("domain.tld").
PLEASE TEST YOUR PROVIDER.
* BIND filename formatting options have been added to access the new
formats.
# Breaking changes
* BIND zonefiles may change. The default used the name input in the D()
statement. It now defaults to the IDN name + "!tag" if there is a tag.
* Providers that are not IDN-aware may break (hopefully only if they
weren't processing IDN already)
---------
Co-authored-by: Jakob Ackermann <das7pad@outlook.com>
012_duration creates multiple records under the same label, with different TTLs
We plan to deprecate this going forward, in #1372.
Modify the test in a way that creates one label per record, each having a
different TTL format as before, to be validated.
* Adds SOA record to JS, zone parsing and record validation
* adds JS parsing test for SOA record
* fix validation & regenerates static resources
* Adds label and target test for SOA record
* Removes serial from SOA JS macro
* Adds generated resources
* reformat with gofmt
Co-authored-by: Tom Limoncelli <tlimoncelli@stackoverflow.com>
* Implement main feature
* BIND: Permit printf-like file name formats
* BIND: Make filenameformat work forwards and backwards.
* Fix extrator test cases
* js_test.go: Do a zonefile test if $TESTNAME/parse_tests/{zonefile} exists.
* Add parse_tests zone files for all the tests that make sense.
* js_test.go: Addd the test datafile when displaying failures.
* 007-importTransformTTL.js: Was referring to a non-existent domain.
* 012-duration.js: Had duplicate DNS records.
* 029-dextendsub.js: Move CF-related tests to separate test.
* validate.go: Report non-existent domains as an error.
* cloudflareProvider.go: newCloudflare should not talk to the API.