feat: init commit with main func
This commit is contained in:
103
reconcile.go
Normal file
103
reconcile.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// reconcileMu ensures only one reconcile runs at a time.
|
||||
// TryLock is used so that concurrent triggers simply skip rather than queue.
|
||||
var reconcileMu sync.Mutex
|
||||
|
||||
// Reconcile is the core reconciliation loop:
|
||||
// 1. Fetches the active router list from Traefik (source of truth).
|
||||
// 2. Pulls the DNS repository to ensure it is up to date.
|
||||
// 3. Writes desired zone files for each managed zone.
|
||||
// 4. Commits and pushes if anything changed.
|
||||
//
|
||||
// If Traefik is unreachable or git pull fails, the reconcile is aborted
|
||||
// without modifying any DNS files (safe-fail behaviour).
|
||||
func Reconcile(cfg *Config) {
|
||||
if !reconcileMu.TryLock() {
|
||||
slog.Info("reconcile already in progress, skipping")
|
||||
return
|
||||
}
|
||||
defer reconcileMu.Unlock()
|
||||
|
||||
slog.Info("reconcile started")
|
||||
|
||||
routers, err := FetchRouters(cfg)
|
||||
if err != nil {
|
||||
slog.Error("failed to fetch Traefik routers — skipping reconcile to avoid stale DNS removal",
|
||||
"error", err)
|
||||
return
|
||||
}
|
||||
slog.Info("fetched routers from Traefik", "count", len(routers))
|
||||
|
||||
desired := FilterDomains(routers, cfg)
|
||||
|
||||
if err := GitPull(cfg); err != nil {
|
||||
slog.Error("git pull failed — skipping reconcile", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
var total RecordStats
|
||||
for zone, subdomains := range desired {
|
||||
path := ZoneFilePath(cfg, zone)
|
||||
|
||||
current, err := ReadZoneFile(path)
|
||||
if err != nil {
|
||||
slog.Error("failed to read zone file", "zone", zone, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
newZF := BuildZoneFile(subdomains, cfg)
|
||||
stats := DiffZoneFile(current, newZF)
|
||||
total.Added += stats.Added
|
||||
total.Removed += stats.Removed
|
||||
total.Changed += stats.Changed
|
||||
|
||||
if err := WriteZoneFile(path, newZF); err != nil {
|
||||
slog.Error("failed to write zone file", "zone", zone, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
if stats.Added+stats.Removed+stats.Changed > 0 {
|
||||
slog.Info("zone updated",
|
||||
"zone", zone,
|
||||
"records", len(subdomains),
|
||||
"added", stats.Added,
|
||||
"removed", stats.Removed,
|
||||
"changed", stats.Changed,
|
||||
)
|
||||
} else {
|
||||
slog.Info("zone unchanged", "zone", zone, "records", len(subdomains))
|
||||
}
|
||||
}
|
||||
|
||||
changed, err := GitStatusChanged(cfg)
|
||||
if err != nil {
|
||||
slog.Error("git status check failed", "error", err)
|
||||
return
|
||||
}
|
||||
if !changed {
|
||||
slog.Info("reconcile complete — no changes to commit")
|
||||
return
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf(
|
||||
"chore(dns): reconcile dynamic records from traefik\n\nadded: %d, removed: %d, updated: %d",
|
||||
total.Added, total.Removed, total.Changed,
|
||||
)
|
||||
if err := GitCommitAndPush(cfg, msg); err != nil {
|
||||
slog.Error("failed to commit/push DNS changes", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
slog.Info("reconcile complete — changes pushed",
|
||||
"added", total.Added,
|
||||
"removed", total.Removed,
|
||||
"updated", total.Changed,
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user