This commit is contained in:
Thomas Limoncelli 2025-12-03 10:07:29 -05:00
parent 57707f2538
commit 0cb0b9aa27
No known key found for this signature in database
3 changed files with 35 additions and 30 deletions

View file

@ -28,6 +28,12 @@ Example:
``` ```
{% endcode %} {% endcode %}
As of v4.2.0 `dnscontrol push` will create subdirectories along the path to
the filename. This includes both the portion of the path created by the
`directory` setting and the `filenameformat` setting. For security reasons, the
automatic creation of subdirectories is disabled if `dnscontrol` is running as
root. (Running DNSControl as root is not recommended in general.)
## Meta configuration ## Meta configuration
This provider accepts some optional metadata in the `NewDnsProvider()` call. This provider accepts some optional metadata in the `NewDnsProvider()` call.
@ -91,14 +97,14 @@ The filenameformat is a string with a few printf-like `%` verbs:
| Verb | Description | `EXAMple.com` | `EXAMple.com!MyTag` | `рф.com!myTag` | | Verb | Description | `EXAMple.com` | `EXAMple.com!MyTag` | `рф.com!myTag` |
| ------- | ------------------------------------------------- | ------------- | ------------------- | -------------------- | | ------- | ------------------------------------------------- | ------------- | ------------------- | -------------------- |
| `%T` | the tag | `` | `myTag` | `myTag` | | `%T` | the tag | "" (null) | `myTag` | `myTag` |
| `%c` | canonical name, globally unique and comparable | `example.com` | `example.com!myTag` | `xn--p1ai.com!myTag` | | `%c` | canonical name, globally unique and comparable | `example.com` | `example.com!myTag` | `xn--p1ai.com!myTag` |
| `%a` | ASCII domain (Punycode, downcased) | `example.com` | `example.com` | `xn--p1ai.com` | | `%a` | ASCII domain (Punycode, downcased) | `example.com` | `example.com` | `xn--p1ai.com` |
| `%u` | Unicode domain (non-Unicode parts downcased) | `example.com` | `example.com` | `рф.com` | | `%u` | Unicode domain (non-Unicode parts downcased) | `example.com` | `example.com` | `рф.com` |
| `%r` | Raw (unmodified) Domain from `D()` (risky!) | `EXAMple.com` | `EXAMple.com` | `рф.com` | | `%r` | Raw (unmodified) Domain from `D()` (risky!) | `EXAMple.com` | `EXAMple.com` | `рф.com` |
| `%f` | like `%c` but better for filenames (`%a%?_%T`) | `example.com` | `example.com_myTag` | `xn--p1ai.com_myTag` | | `%f` | like `%c` but better for filenames (`%a%?_%T`) | `example.com` | `example.com_myTag` | `xn--p1ai.com_myTag` |
| `%F` | like `%f` but reversed order (`%T%?_%a`) | `example.com` | `myTag_example.com` | `myTag_xn--p1ai.com` | | `%F` | like `%f` but reversed order (`%T%?_%a`) | `example.com` | `myTag_example.com` | `myTag_xn--p1ai.com` |
| `%?x` | returns `x` if tag exists, otherwise "" | `` | `x` | `x` | | `%?x` | returns `x` if tag exists, otherwise "" | "" (null) | `x` | `x` |
| `%%` | a literal percent sign | `%` | `%` | `%` | | `%%` | a literal percent sign | `%` | `%` | `%` |
| `a-Z./` | other printable characters are copied exactly | `a-Z./` | `a-Z./` | `a-Z./` | | `a-Z./` | other printable characters are copied exactly | `a-Z./` | `a-Z./` | `a-Z./` |
| `%U` | (deprecated, use `%c`) Same as `%D%?!%T` (risky!) | `example.com` | `example.com!myTag` | `рф.com!myTag` | | `%U` | (deprecated, use `%c`) Same as `%D%?!%T` (risky!) | `example.com` | `example.com!myTag` | `рф.com!myTag` |
@ -114,17 +120,6 @@ The filenameformat is a string with a few printf-like `%` verbs:
DNSControl will write both zone files to the same file, flapping between the DNSControl will write both zone files to the same file, flapping between the
two. No error or warning will be output. two. No error or warning will be output.
Compatibility notes:
* `%D` should not be used. It downcases the string in a way that is probably
incompatible with Unicode characters. It is retained for compatibility with
pre-v4.28 releases. If your domain has capital Unicode characters, backwards
compatibility is not guaranteed.
* `%U` relies on `%D` which is deprecated. Use `%c` instead.
* As of v4.28 the default format string changed from `%U.zone` to `%c.zone`. This
should only matter if your `D()` statements included non-ASCII (Unicode)
runes that were capitalized.
Useful examples: Useful examples:
| Verb | Description | `EXAMple.com` | `EXAMple.com!MyTag` | `рф.com!myTag` | | Verb | Description | `EXAMple.com` | `EXAMple.com!MyTag` | `рф.com!myTag` |
@ -134,10 +129,20 @@ Useful examples:
| `db_%f` | Recommended in a popular DNS book | `db_example.com` | `db_example.com_myTag` | `db_xn--p1ai.com_myTag` | | `db_%f` | Recommended in a popular DNS book | `db_example.com` | `db_example.com_myTag` | `db_xn--p1ai.com_myTag` |
| `db_%a%?_%T` | same as above but using `%?_` | `db_example.com` | `db_example.com_myTag` | `db_xn--p1ai.com_myTag` | | `db_%a%?_%T` | same as above but using `%?_` | `db_example.com` | `db_example.com_myTag` | `db_xn--p1ai.com_myTag` |
(new in v4.2.0) `dnscontrol push` will create subdirectories along the path to Compatibility notes:
the filename. This includes both the portion of the path created by the
`directory` setting and the `filenameformat` setting. For security reasons, the automatic creation of * `%D` should not be used. It downcases the string in a way that is probably
subdirectories is disabled if `dnscontrol` is running as root. incompatible with Unicode characters. It is retained for compatibility with
pre-v4.28 releases. If your domain has capital Unicode characters, backwards
compatibility is not guaranteed. Use `%r` instead.
* `%U` relies on `%D` which is deprecated. Use `%c` instead.
* As of v4.28 the default format string changed from `%U.zone` to `%c.zone`. This
should only matter if your `D()` statements included non-ASCII (Unicode)
runes that were capitalized.
* If you are using pre-v4.28 releases the above table is slightly misleading
because uppercase ASCII letters do not always work. If you are using
pre-v4.28 releases, assume the above table lists `example.com` instead
of `EXAMpl.com`.
# FYI: get-zones # FYI: get-zones
@ -148,7 +153,8 @@ any files named `*.zone` and assumes they are zone files.
dnscontrol get-zones --format=nameonly - BIND all dnscontrol get-zones --format=nameonly - BIND all
``` ```
If `filenameformat` is defined, `dnscontrol` makes a guess at which If `filenameformat` is defined, `dnscontrol` makes a guess at which filenames
filenames are zones but doesn't try to hard to get it right, which is are zones by reversing the logic of the format string. It doesn't try very hard
mathematically impossible to do correctly in all chase. Feel free to file an issue if to get this right, as getting it right in all situations is mathematically
your format string doesn't work. I love a challenge! impossible. Feel free to file an issue if find a situation where it doesn't
work. I love a challenge!

View file

@ -181,9 +181,9 @@ func (c *bindProvider) GetZoneRecords(domain string, meta map[string]string) (mo
ff, ff,
), ),
) )
// fmt.Printf("DEBUG: Reading zonefile %q\n", zonefile) //fmt.Printf("DEBUG: Reading zonefile %q\n", zonefile)
// fmt.Printf("DEBUG: Meta %+v\n", meta) //fmt.Printf("DEBUG: Meta %+v\n", meta)
// fmt.Printf("DEBUG: Domain Names %+v\n", ff) //fmt.Printf("DEBUG: Domain Names %+v\n", ff)
content, err := os.ReadFile(zonefile) content, err := os.ReadFile(zonefile)
if os.IsNotExist(err) { if os.IsNotExist(err) {

View file

@ -1,7 +1,6 @@
package bind package bind
import ( import (
"fmt"
"reflect" "reflect"
"testing" "testing"
@ -74,7 +73,7 @@ func Test_makeFileName_2(t *testing.T) {
want1 string want1 string
want2 string want2 string
want3 string want3 string
descr string descr string // Not used in test, just for documentation generation
}{ }{
// NOTE: "Domain" in these descriptions means the domain name without any split horizon tag. Technically the "Zone". // NOTE: "Domain" in these descriptions means the domain name without any split horizon tag. Technically the "Zone".
{`T`, `%T`, ``, `myTag`, `myTag`, `the tag`}, {`T`, `%T`, ``, `myTag`, `myTag`, `the tag`},
@ -115,10 +114,10 @@ func Test_makeFileName_2(t *testing.T) {
if got3 != tt.want3 { if got3 != tt.want3 {
t.Errorf("makeFileName(%q) = ff3 %q, want %q", tt.format, got3, tt.want3) t.Errorf("makeFileName(%q) = ff3 %q, want %q", tt.format, got3, tt.want3)
} }
// Uncomment to regenerate the table in the docs: //Uncomment to regenerate lines used in documentation/provider/bind.md 's table:
fmt.Printf("MD | `%s` | %s | `%s` | `%s` | `%s` |\n", tt.format, tt.descr, got1, got2, got3) // fmt.Print(strings.ReplaceAll(fmt.Sprintf("| `%s` | %s | `%s` | `%s` | `%s` |\n", tt.format, tt.descr, got1, got2, got3), "``", "`\"\"` (null)"))
// Uncomment to regenerate the above test cases: //Uncomment to regenerate the above test cases:
//fmt.Printf("{`%s`, `%s`, `%s`, `%s`, `%s`, %q},\n", tt.name, tt.format, got1, got2, got3, tt.descr) // fmt.Printf("{`%s`, `%s`, `%s`, `%s`, `%s`, %q},\n", tt.name, tt.format, got1, got2, got3, tt.descr)
}) })
} }
} }