converge apex NS per record, not by RRset replace
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful

BIND ignores an RRset-wide delete of apex NS, so the previous replace only
appended to the seed placeholder.
This commit is contained in:
2026-09-26 18:50:55 +10:00
parent 4a41cbc427
commit e4ed6c8052
9 changed files with 304 additions and 130 deletions
+22 -13
View File
@@ -99,7 +99,7 @@ func (r *BindZoneReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
return r.setPhase(ctx, &zone, "Error", "ConfigError", err.Error())
}
nameservers := zoneNameservers(zone.Spec.Nameservers, cluster)
nameservers, nsDeclared := zoneNameservers(&zone, cluster)
created := !r.Exec.ZoneExists(ctx, zone.Namespace, primaryPod, zone.Spec.ZoneName, zone.Spec.ViewRef)
if created && (zone.Spec.Type == bindv1alpha1.ZonePrimary || zone.Spec.Type == "") {
@@ -118,24 +118,33 @@ func (r *BindZoneReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
return r.setPhase(ctx, &zone, "Error", "AddZoneFailed", err.Error())
}
// Sync the apex NS on every pass, not only at seed time, so an existing zone
// converges off the seed placeholder. Best-effort: a zone that permits no
// dynamic update keeps what it was seeded with rather than failing to
// reconcile.
// Converge the apex NS on every pass, not only at seed time, so a zone that
// was seeded with the placeholder moves onto its real nameservers. Only for a
// zone that declared them: otherwise the operator would fight whoever else
// manages the RRset.
recordCount := 0
if isPrimaryType(zone.Spec.Type) {
creds, credErr := r.zoneUpdateCreds(ctx, &zone)
if apex := apexNSUpdates(&zone, nameservers); len(apex) > 0 && credErr == nil {
if err := r.Exec.NSUpdate(ctx, zone.Namespace, primaryPod, zone.Spec.ZoneName, creds, apex); err != nil {
logger.V(1).Info("apex NS sync failed", "zone", zone.Spec.ZoneName, "err", err.Error())
}
}
// Seed static records.
if len(zone.Spec.Records) > 0 {
if nsDeclared {
if credErr != nil {
return r.setPhase(ctx, &zone, "Error", "NoUpdateKey", credErr.Error())
}
live, err := r.Exec.ApexNS(ctx, zone.Namespace, primaryPod, zone.Spec.ZoneName)
if err != nil {
return r.setPhase(ctx, &zone, "Error", "ApexNSQueryFailed", err.Error())
}
if apex := apexNSUpdates(&zone, nameservers, live, zone.Spec.DefaultTTL); len(apex) > 0 {
if err := r.Exec.NSUpdate(ctx, zone.Namespace, primaryPod, zone.Spec.ZoneName, creds, apex); err != nil {
return r.setPhase(ctx, &zone, "Error", "ApexNSSyncFailed", err.Error())
}
logger.Info("apex NS converged", "zone", zone.Spec.ZoneName, "nameservers", nameservers)
}
}
// Seed static records.
if updates := recordsToUpdates(zone.Spec.ZoneName, zone.Spec.Records, zone.Spec.DefaultTTL); len(updates) > 0 {
if credErr != nil {
return r.setPhase(ctx, &zone, "Error", "NoUpdateKey", credErr.Error())
}
updates := recordsToUpdates(zone.Spec.ZoneName, zone.Spec.Records, zone.Spec.DefaultTTL)
if err := r.Exec.NSUpdate(ctx, zone.Namespace, primaryPod, zone.Spec.ZoneName, creds, updates); err != nil {
return r.setPhase(ctx, &zone, "Error", "RecordUpdateFailed", err.Error())
}