plugin/auto: warn on duplicate zone file origins (#8191)

Signed-off-by: houyuwushang <liuluoqianqiu@outlook.com>
This commit is contained in:
houyuwushang
2026-06-29 15:59:23 +08:00
committed by GitHub
parent 70f7922ec5
commit 4faf983fb6
3 changed files with 61 additions and 3 deletions

View File

@@ -28,7 +28,8 @@ are used.
like `{<number>}` are replaced with the respective matches in the file name, e.g. `{1}` is the
first match, `{2}` is the second. The default is: `db\.(.*) {1}` i.e. from a file with the
name `db.example.com`, the extracted origin will be `example.com`. **REGEXP** must not be longer
than 10000 characters.
than 10000 characters. **REGEXP** is unanchored; use `^` and `$` to require matching the full
file name, for example `^(.*)\.zone$` to avoid matching backup files like `example.org.zone.bak`.
* `reload` interval to perform reloads of zones if SOA version changes and zonefiles. It specifies how often CoreDNS should scan the directory to watch for file removal and addition. Default is one minute.
Value of `0` means to not scan for changes and reload. eg. `30s` checks zonefile every 30 seconds
and reloads zone when serial changes.

View File

@@ -26,6 +26,7 @@ func (a Auto) Walk() error {
for _, n := range a.Names() {
toDelete[n] = true
}
seen := make(map[string]string)
filepath.Walk(dir, func(path string, info os.FileInfo, e error) error {
if e != nil {
@@ -40,14 +41,19 @@ func (a Auto) Walk() error {
return nil
}
cleanPath := filepath.Clean(path)
if previous, ok := seen[origin]; ok && previous != cleanPath {
log.Warningf("Multiple zone files match origin %q: using %q instead of %q", origin, path, previous)
}
seen[origin] = cleanPath
if z, ok := a.Z[origin]; ok {
// we already have this zone
toDelete[origin] = false
z.SetFile(path)
return nil
}
reader, err := os.Open(filepath.Clean(path)) //nolint:gosec // G122: path is from filepath.Walk rooted in a.directory; symlinks must be followed for configmap-style mounts
reader, err := os.Open(cleanPath) //nolint:gosec // G122: path is from filepath.Walk rooted in a.directory; symlinks must be followed for configmap-style mounts
if err != nil {
log.Warningf("Opening %s failed: %s", path, err)
return nil

View File

@@ -1,9 +1,13 @@
package auto
import (
"bytes"
"io"
golog "log"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
)
@@ -98,6 +102,53 @@ func TestWalkNonExistent(t *testing.T) {
a.Walk()
}
func TestWalkWarnsForDuplicateOrigin(t *testing.T) {
dir := t.TempDir()
zone := filepath.Join(dir, "example.org.zone")
backup := filepath.Join(dir, "example.org.zone.bak-20260528")
for _, path := range []string{zone, backup} {
if err := os.WriteFile(path, []byte(zoneContent), 0644); err != nil {
t.Fatal(err)
}
}
var logBuf bytes.Buffer
golog.SetOutput(&logBuf)
defer golog.SetOutput(io.Discard)
a := Auto{
loader: loader{
directory: dir,
re: regexp.MustCompile(`(.*)\.zone`),
template: `${1}`,
},
Zones: &Zones{},
}
a.Walk()
got := logBuf.String()
if count := strings.Count(got, `[WARNING] plugin/auto: Multiple zone files match origin "example.org."`); count != 1 {
t.Fatalf("Expected one duplicate origin warning, got %d in %q", count, got)
}
for _, want := range []string{
`[WARNING] plugin/auto: Multiple zone files match origin "example.org."`,
"example.org.zone",
"example.org.zone.bak-20260528",
} {
if !strings.Contains(got, want) {
t.Fatalf("Expected log to contain %q, got %q", want, got)
}
}
logBuf.Reset()
a.Walk()
if count := strings.Count(logBuf.String(), `[WARNING] plugin/auto: Multiple zone files match origin "example.org."`); count != 1 {
t.Fatalf("Expected one duplicate origin warning after reload, got %d in %q", count, logBuf.String())
}
}
func createFiles(t *testing.T) (string, error) {
t.Helper()
dir := t.TempDir()