Files
coredns/plugin/file/setup.go
jxj 72160e6e20 plugin/file: track zone mtime for reload_by_mtime (#8556)
* plugin/file: track zone mtime for reload_by_mtime

Signed-off-by: git-jxj <65210887+git-jxj@users.noreply.github.com>

* plugin/file: capture initial mtime before parsing

Signed-off-by: git-jxj <65210887+git-jxj@users.noreply.github.com>

* plugin/file: keep unloaded zones retryable after open error

Signed-off-by: git-jxj <65210887+git-jxj@users.noreply.github.com>

---------

Signed-off-by: git-jxj <65210887+git-jxj@users.noreply.github.com>
2026-09-28 00:22:39 -07:00

176 lines
4.0 KiB
Go

package file
import (
"errors"
"io"
"os"
"path/filepath"
"time"
"github.com/coredns/caddy"
"github.com/coredns/coredns/core/dnsserver"
"github.com/coredns/coredns/plugin"
"github.com/coredns/coredns/plugin/pkg/fall"
"github.com/coredns/coredns/plugin/pkg/upstream"
"github.com/coredns/coredns/plugin/transfer"
)
func init() { plugin.Register("file", setup) }
func setup(c *caddy.Controller) error {
zones, fall, err := fileParse(c)
if err != nil {
return plugin.Error("file", err)
}
f := File{Zones: zones, Fall: fall}
// get the transfer plugin, so we can send notifies and send notifies on startup as well.
c.OnStartup(func() error {
t := dnsserver.GetConfig(c).Handler("transfer")
if t == nil {
return nil
}
f.Xfer = t.(*transfer.Transfer) // if found this must be OK.
go func() {
for _, n := range zones.Names {
f.Xfer.Notify(n)
}
}()
return nil
})
c.OnRestartFailed(func() error {
t := dnsserver.GetConfig(c).Handler("transfer")
if t == nil {
return nil
}
go func() {
for _, n := range zones.Names {
f.Xfer.Notify(n)
}
}()
return nil
})
for _, n := range zones.Names {
z := zones.Z[n]
c.OnShutdown(z.OnShutdown)
c.OnStartup(func() error {
z.StartupOnce.Do(func() { z.Reload(f.Xfer) })
return nil
})
}
dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
f.Next = next
return f
})
return nil
}
func fileParse(c *caddy.Controller) (Zones, fall.F, error) {
return fileParseWithParser(c, Parse)
}
func fileParseWithParser(c *caddy.Controller, parse func(io.Reader, string, string, int64) (*Zone, error)) (Zones, fall.F, error) {
z := make(map[string]*Zone)
names := []string{}
fall := fall.F{}
config := dnsserver.GetConfig(c)
var openErr error
reload := 1 * time.Minute
reload_by_mtime := false
for c.Next() {
// file db.file [zones...]
if !c.NextArg() {
return Zones{}, fall, c.ArgErr()
}
fileName := c.Val()
origins := plugin.OriginsFromArgsOrServerBlock(c.RemainingArgs(), c.ServerBlockKeys)
if !filepath.IsAbs(fileName) && config.Root != "" {
fileName = filepath.Join(config.Root, fileName)
}
reader, err := os.Open(filepath.Clean(fileName))
if err != nil {
openErr = err
}
var openedFileInfo os.FileInfo
if err == nil {
// Capture the version represented by the opened reader, before
// parsing can race with a replacement at the same pathname.
openedFileInfo, _ = reader.Stat()
}
err = func() error {
defer reader.Close()
for i := range origins {
z[origins[i]] = NewZone(origins[i], fileName)
if openErr == nil {
reader.Seek(0, 0)
zone, err := parse(reader, origins[i], fileName, 0)
if err != nil {
return err
}
z[origins[i]] = zone
}
names = append(names, origins[i])
}
return nil
}()
if err != nil {
return Zones{}, fall, err
}
for c.NextBlock() {
switch c.Val() {
case "fallthrough":
fall.SetZonesFromArgs(c.RemainingArgs())
case "reload":
t := c.RemainingArgs()
if len(t) < 1 {
return Zones{}, fall, errors.New("reload duration value is expected")
}
d, err := time.ParseDuration(t[0])
if err != nil {
return Zones{}, fall, plugin.Error("file", err)
}
reload = d
case "reload_by_mtime":
reload_by_mtime = true
case "upstream":
// remove soon
c.RemainingArgs()
default:
return Zones{}, fall, c.Errf("unknown property '%s'", c.Val())
}
}
for i := range origins {
z[origins[i]].ReloadInterval = reload
z[origins[i]].Upstream = upstream.New()
z[origins[i]].ReloadByMtime = reload_by_mtime
if reload_by_mtime && openErr == nil && openedFileInfo != nil {
z[origins[i]].file_mtime = openedFileInfo.ModTime()
}
}
}
if openErr != nil {
if reload == 0 {
// reload hasn't been set make this a fatal error
return Zones{}, fall, plugin.Error("file", openErr)
}
log.Warningf("Failed to open %q: trying again in %s", openErr, reload)
}
return Zones{Z: z, Names: names}, fall, nil
}