Render forward zones into named.conf on every pod
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful

type:forward zones are pure configuration (conditional forwarding), not
replicated data, so a resolver needs them on all pods. They were being
rndc-addzone'd on the primary only, so queries hitting a secondary pod
missed the forwarding. Render them into named.conf instead.

- render forward zones inside their view (or top-level when no views)
- BindCluster lists type:forward zones and watches BindZone to re-render
- BindZone controller skips forward zones (config-managed, no addzone)
- unit test for forward-zone-in-view rendering
This commit is contained in:
2026-07-04 11:55:03 +10:00
parent f0e851c0bc
commit 9bc4436c79
4 changed files with 89 additions and 0 deletions
@@ -206,6 +206,17 @@ func (r *BindClusterReconciler) reconcileConfigMap(ctx context.Context, c *bindv
}
}
// Forward zones are configuration (no data), so they are rendered into
// named.conf on every pod rather than added dynamically to the primary.
var zones bindv1alpha1.BindZoneList
if err := r.List(ctx, &zones, client.InNamespace(c.Namespace)); err == nil {
for _, z := range zones.Items {
if z.Spec.ClusterRef == c.Name && z.Spec.Type == bindv1alpha1.ZoneForward {
in.Forwards = append(in.Forwards, z)
}
}
}
var catalogs bindv1alpha1.BindCatalogZoneList
if err := r.List(ctx, &catalogs, client.InNamespace(c.Namespace)); err == nil {
for i := range catalogs.Items {
@@ -416,6 +427,15 @@ func (r *BindClusterReconciler) SetupWithManager(mgr ctrl.Manager) error {
Watches(&bindv1alpha1.BindDNSSECPolicy{}, handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, o client.Object) []reconcile.Request {
return mapToCluster(o.(*bindv1alpha1.BindDNSSECPolicy).Spec.ClusterRef, o.GetNamespace())
})).
Watches(&bindv1alpha1.BindZone{}, handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, o client.Object) []reconcile.Request {
// Only forward zones affect named.conf; primary/secondary zones are
// managed dynamically by the BindZone controller.
z := o.(*bindv1alpha1.BindZone)
if z.Spec.Type != bindv1alpha1.ZoneForward {
return nil
}
return mapToCluster(z.Spec.ClusterRef, o.GetNamespace())
})).
Watches(&bindv1alpha1.BindCatalogZone{}, handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, o client.Object) []reconcile.Request {
return mapToCluster(o.(*bindv1alpha1.BindCatalogZone).Spec.ClusterRef, o.GetNamespace())
})).
@@ -35,6 +35,19 @@ func (r *BindZoneReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// Forward zones are pure configuration rendered into named.conf by the
// BindCluster controller (on every pod), not added dynamically to the
// primary. Nothing to do here beyond reporting readiness.
if zone.Spec.Type == bindv1alpha1.ZoneForward {
zone.Status.Phase = "Ready"
zone.Status.ObservedGeneration = zone.Generation
setReady(&zone.Status.Conditions, zone.Generation, true, "Configured", "forward zone rendered into named.conf")
if err := r.Status().Update(ctx, &zone); err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
cluster, err := getCluster(ctx, r.Client, zone.Namespace, zone.Spec.ClusterRef)
if err != nil {
return r.setPhase(ctx, &zone, "Error", "ClusterMissing", err.Error())