reconcile zone apex NS off the pod IP placeholder
Add BindZone.spec.nameservers and sync the apex NS RRset on every reconcile.
This commit is contained in:
+41
-12
@@ -26,36 +26,65 @@ func (e *Executor) ZoneExists(ctx context.Context, namespace, pod, zone, view st
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// renderSeedZone renders a minimal loadable zone (SOA + apex NS + glue). The
|
||||
// apex NS is the in-zone name ns1, and a glue A record pointing at primaryIP is
|
||||
// included so BIND's check-integrity accepts the zone (an in-zone NS without an
|
||||
// address record is a load error).
|
||||
func renderSeedZone(zone, primaryIP string, serial int64) string {
|
||||
// renderSeedZone renders a minimal loadable zone (SOA + apex NS). nameservers
|
||||
// are the names published in the apex NS RRset; when empty the in-zone name ns1
|
||||
// is used. A glue A pointing at primaryIP is emitted only for a nameserver that
|
||||
// falls inside the zone, because BIND refuses to load a zone whose in-zone NS
|
||||
// has no address record. Out-of-zone nameservers therefore keep pod IPs out of
|
||||
// the zone file entirely.
|
||||
func renderSeedZone(zone, primaryIP string, nameservers []string, serial int64) string {
|
||||
origin := dot(zone)
|
||||
ns := "ns1." + origin
|
||||
ns := make([]string, 0, len(nameservers))
|
||||
for _, n := range nameservers {
|
||||
ns = append(ns, dot(n))
|
||||
}
|
||||
if len(ns) == 0 {
|
||||
ns = []string{"ns1." + origin}
|
||||
}
|
||||
// Short refresh/retry so a secondary that misses a NOTIFY (e.g. its pod IP
|
||||
// changed and the primary's also-notify was briefly stale) still converges
|
||||
// in minutes, not the hour a 3600s refresh would impose. minimum is the
|
||||
// negative-cache TTL: keep it low so a stale-secondary NXDOMAIN does not
|
||||
// stick in downstream resolvers for long. NOTIFY (also-notify on the
|
||||
// primary) remains the fast path; these are the fallback.
|
||||
return fmt.Sprintf(`$TTL 3600
|
||||
var b strings.Builder
|
||||
fmt.Fprintf(&b, `$TTL 3600
|
||||
@ IN SOA %s hostmaster.%s (
|
||||
%d ; serial
|
||||
300 ; refresh
|
||||
60 ; retry
|
||||
1209600 ; expire
|
||||
60 ) ; minimum
|
||||
@ IN NS %s
|
||||
ns1 IN A %s
|
||||
`, ns, origin, serial, ns, primaryIP)
|
||||
`, ns[0], origin, serial)
|
||||
for _, n := range ns {
|
||||
fmt.Fprintf(&b, "@ IN NS %s\n", n)
|
||||
}
|
||||
for _, n := range ns {
|
||||
if owner, ok := InZoneOwner(n, zone); ok {
|
||||
fmt.Fprintf(&b, "%s IN A %s\n", owner, primaryIP)
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// InZoneOwner reports whether name sits inside zone, and if so returns its owner
|
||||
// name relative to the apex ("@" for the apex itself).
|
||||
func InZoneOwner(name, zone string) (string, bool) {
|
||||
name, origin := dot(name), dot(zone)
|
||||
switch {
|
||||
case name == origin:
|
||||
return "@", true
|
||||
case strings.HasSuffix(name, "."+origin):
|
||||
return strings.TrimSuffix(name, "."+origin), true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// EnsureSeedZone makes path loadable without discarding live data: it probes
|
||||
// the zone file and journal, moves aside whatever cannot load, and writes a
|
||||
// skeleton only when there is nothing to preserve. Every caller that needs a
|
||||
// zone database file on disk goes through here.
|
||||
func (e *Executor) EnsureSeedZone(ctx context.Context, namespace, pod, zone, path, primaryIP string) error {
|
||||
func (e *Executor) EnsureSeedZone(ctx context.Context, namespace, pod, zone, path, primaryIP string, nameservers []string) error {
|
||||
state, err := e.ZoneDiskState(ctx, namespace, pod, path)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -67,7 +96,7 @@ func (e *Executor) EnsureSeedZone(ctx context.Context, namespace, pod, zone, pat
|
||||
if !plan.WriteSeed {
|
||||
return e.Quarantine(ctx, namespace, pod, path, plan)
|
||||
}
|
||||
content := renderSeedZone(zone, primaryIP, plan.Serial)
|
||||
content := renderSeedZone(zone, primaryIP, nameservers, plan.Serial)
|
||||
cmd := []string{"sh", "-c", seedScript(path, plan, len(content))}
|
||||
if out, err := e.Exec(ctx, namespace, pod, cmd, content); err != nil {
|
||||
return fmt.Errorf("seed zone %s: %w (out: %s)", zone, err, out)
|
||||
|
||||
Reference in New Issue
Block a user