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
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
package bind
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
bindv1alpha1 "git.unkin.net/unkin/bind-operator/api/v1alpha1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func newCluster(mode bindv1alpha1.BindMode) *bindv1alpha1.BindCluster {
|
||||
return &bindv1alpha1.BindCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "auth", Namespace: "dns"},
|
||||
Spec: bindv1alpha1.BindClusterSpec{Mode: mode, Replicas: 3},
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderResolverEnablesRecursion(t *testing.T) {
|
||||
primary, secondary := RenderNamedConf(RenderInput{Cluster: newCluster(bindv1alpha1.ModeResolver)})
|
||||
if !strings.Contains(primary, "recursion yes;") {
|
||||
t.Fatalf("resolver primary should enable recursion:\n%s", primary)
|
||||
}
|
||||
if !strings.Contains(secondary, "recursion yes;") {
|
||||
t.Fatalf("resolver secondary should enable recursion")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderAuthoritativeDisablesRecursion(t *testing.T) {
|
||||
primary, _ := RenderNamedConf(RenderInput{Cluster: newCluster(bindv1alpha1.ModeAuthoritative)})
|
||||
if !strings.Contains(primary, "recursion no;") {
|
||||
t.Fatalf("authoritative should disable recursion:\n%s", primary)
|
||||
}
|
||||
if !strings.Contains(primary, "allow-new-zones yes;") {
|
||||
t.Fatalf("authoritative should allow new zones for dynamic provisioning")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderCatalogOnSecondaryOnly(t *testing.T) {
|
||||
in := RenderInput{
|
||||
Cluster: newCluster(bindv1alpha1.ModeAuthoritative),
|
||||
Catalog: &bindv1alpha1.BindCatalogZone{Spec: bindv1alpha1.BindCatalogZoneSpec{ZoneName: "catalog.internal", DefaultPrimaries: []string{"10.0.0.1"}}},
|
||||
PrimaryAddress: "auth-0.auth-headless.dns.svc.cluster.local",
|
||||
}
|
||||
primary, secondary := RenderNamedConf(in)
|
||||
if strings.Contains(primary, "catalog-zones") {
|
||||
t.Fatalf("primary must not consume the catalog it publishes:\n%s", primary)
|
||||
}
|
||||
if !strings.Contains(secondary, "catalog-zones") {
|
||||
t.Fatalf("secondary must consume the catalog zone:\n%s", secondary)
|
||||
}
|
||||
if !strings.Contains(secondary, "type secondary;") {
|
||||
t.Fatalf("secondary must declare the catalog zone as a secondary")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderACL(t *testing.T) {
|
||||
in := RenderInput{
|
||||
Cluster: newCluster(bindv1alpha1.ModeAuthoritative),
|
||||
ACLs: []bindv1alpha1.BindACL{{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "internal"},
|
||||
Spec: bindv1alpha1.BindACLSpec{Entries: []string{"10.0.0.0/8", "192.168.0.0/16"}},
|
||||
}},
|
||||
}
|
||||
primary, _ := RenderNamedConf(in)
|
||||
if !strings.Contains(primary, `acl "internal" { 10.0.0.0/8; 192.168.0.0/16; };`) {
|
||||
t.Fatalf("ACL not rendered correctly:\n%s", primary)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCatalogHashStable(t *testing.T) {
|
||||
// SHA-1 of the wire format of "example.com" is well-defined and stable.
|
||||
h1 := catalogHash("example.com")
|
||||
h2 := catalogHash("example.com.")
|
||||
if h1 != h2 {
|
||||
t.Fatalf("trailing dot should not change hash: %s vs %s", h1, h2)
|
||||
}
|
||||
if len(h1) != 40 {
|
||||
t.Fatalf("expected 40-char hex sha1, got %d: %s", len(h1), h1)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user