Add UI editing for local DNS zones
build / build-push (push) Successful in 29s

Local zones were only editable as mounted files, so operators could not manage DNS entries from the console. Add a Local zones editor backed by a shared Kubernetes ConfigMap, validate master files before saving, scope the pod service account to that ConfigMap, and reload changed zones on every replica while flushing stale cache entries.
This commit is contained in:
mats committed 2026-09-28 08:50:50 +02:00
1 parent 2a7ac824f7
commit 32f940b763
18 files changed
+727 -36

No files matched your search

+12 -1
View File
@@ -25,6 +25,7 @@ import (
"git.k8s-git-k6n.net/mats/fancy-dns/internal/cluster"
"git.k8s-git-k6n.net/mats/fancy-dns/internal/config"
"git.k8s-git-k6n.net/mats/fancy-dns/internal/engine"
"git.k8s-git-k6n.net/mats/fancy-dns/internal/kubezones"
"git.k8s-git-k6n.net/mats/fancy-dns/internal/localzone"
"git.k8s-git-k6n.net/mats/fancy-dns/internal/metrics"
"git.k8s-git-k6n.net/mats/fancy-dns/internal/qlog"
@@ -61,6 +62,7 @@ type App struct {
logger *qlog.Logger
aclH *acl.Holder
local *localzone.Holder
zones *kubezones.Client
pool *resolver.Pool
reverse *engine.ReverseCache
engine *engine.Engine
@@ -75,6 +77,8 @@ type App struct {
// reloadMu serialises reload work, since SIGHUP, the API and the cluster
// can all trigger one at the same time.
reloadMu sync.Mutex
// zoneReloadMu serialises file-watcher and config-swap recompiles.
zoneReloadMu sync.Mutex
startedAt time.Time
// degraded is set when a background component failed; surfaced in health.
@@ -96,7 +100,11 @@ func New(opt Options) (*App, error) {
return nil, err
}
store := config.NewStore(cfg, opt.ConfigPath)
return &App{opt: opt, log: log, cfg: store, ver: opt.Version}, nil
zones, err := kubezones.InCluster()
if err != nil {
return nil, err
}
return &App{opt: opt, log: log, cfg: store, ver: opt.Version, zones: zones}, nil
}
// newLogger builds the structured logger from config plus CLI overrides.
@@ -322,6 +330,8 @@ func (a *App) startLocalZones(cfg *config.Config) {
// reloadLocalZones recompiles the local zone store. A failure keeps the
// previous store: serving stale local data beats serving nothing.
func (a *App) reloadLocalZones() {
a.zoneReloadMu.Lock()
defer a.zoneReloadMu.Unlock()
cfg := a.cfg.Get()
if len(cfg.Block.LocalZones) == 0 {
a.local.Set(nil)
@@ -550,6 +560,7 @@ func (a *App) startCluster(ctx context.Context, cfg *config.Config) {
// startBackground runs the periodic jobs: cache snapshots, upstream health
// probes and the metrics gauge.
func (a *App) startBackground(ctx context.Context, cfg *config.Config) {
go a.watchLocalZones(ctx)
// Cache snapshots.
if a.cstore != nil {
interval := cfg.Cache.Persist.Interval.D()