Files
bind-operator/internal/controller/bindview_controller.go
T
unkinben fe5fbdaf6d Initial bind-operator: 9 CRDs + controllers
Implements a Kubernetes operator that manages fleets of BIND9 servers
declaratively, using controller-runtime (matching forgebot conventions).

- add BindCluster reconciler: StatefulSet (pod-0 primary, secondaries),
  headless + client Services, rendered named.conf ConfigMap, TSIG keys
  Secret and rndc control Secret; watches dependent CRs to re-render
- add BindTSIGKey reconciler that generates key material into a Secret
- add BindZone/DNSRecord reconcilers using fully-dynamic delivery
  (rndc addzone + TSIG nsupdate against the primary pod)
- add BindCatalogZone reconciler so secondaries auto-provision zones
- add BindPolicy (RPZ), BindDNSSECPolicy, BindView, BindACL reconcilers
- render primary/secondary named.conf variants selected by pod ordinal
- generate CRDs, deepcopy and RBAC; add samples mapping the three Puppet
  roles (authoritative/resolver/external-dns) to three BindClusters
- add Makefile, Dockerfile.operator, Woodpecker CI and kind manifests
2026-07-03 15:48:13 +10:00

56 lines
1.8 KiB
Go

package controller
import (
"context"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
bindv1alpha1 "git.unkin.net/unkin/bind-operator/api/v1alpha1"
)
// BindViewReconciler validates a BindView and reports the number of zones bound
// to it. The view block is rendered into named.conf by the BindCluster
// controller, which watches views.
type BindViewReconciler struct {
client.Client
Scheme *runtime.Scheme
}
// +kubebuilder:rbac:groups=bind.unkin.net,resources=bindviews,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=bind.unkin.net,resources=bindviews/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=bind.unkin.net,resources=bindzones,verbs=get;list;watch
func (r *BindViewReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
var view bindv1alpha1.BindView
if err := r.Get(ctx, req.NamespacedName, &view); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
var zones bindv1alpha1.BindZoneList
count := int32(0)
if err := r.List(ctx, &zones, client.InNamespace(view.Namespace)); err == nil {
for _, z := range zones.Items {
if z.Spec.ClusterRef == view.Spec.ClusterRef && z.Spec.ViewRef == view.Name {
count++
}
}
}
view.Status.ZoneCount = count
view.Status.Ready = view.Spec.ClusterRef != ""
view.Status.ObservedGeneration = view.Generation
setReady(&view.Status.Conditions, view.Generation, view.Status.Ready, "Validated", "view rendered into named.conf")
if err := r.Status().Update(ctx, &view); err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
func (r *BindViewReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&bindv1alpha1.BindView{}).
Complete(r)
}