9bc4436c79
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
131 lines
4.9 KiB
Go
131 lines
4.9 KiB
Go
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 TestRenderCatalogOmittedWhenPrimaryIPUnknown(t *testing.T) {
|
|
// Primary IP not known yet and no explicit default-primaries: the secondary
|
|
// must not emit a catalog-zones / secondary catalog zone with an empty
|
|
// primaries list (which BIND rejects at config load).
|
|
in := RenderInput{
|
|
Cluster: newCluster(bindv1alpha1.ModeAuthoritative),
|
|
Catalog: &bindv1alpha1.BindCatalogZone{Spec: bindv1alpha1.BindCatalogZoneSpec{ZoneName: "catalog.internal"}},
|
|
PrimaryAddress: "",
|
|
}
|
|
_, secondary := RenderNamedConf(in)
|
|
if strings.Contains(secondary, "catalog-zones") || strings.Contains(secondary, "primaries {") {
|
|
t.Fatalf("secondary must omit catalog primaries when the primary IP is unknown:\n%s", secondary)
|
|
}
|
|
}
|
|
|
|
func TestRenderCatalogUsesPrimaryIP(t *testing.T) {
|
|
in := RenderInput{
|
|
Cluster: newCluster(bindv1alpha1.ModeAuthoritative),
|
|
Catalog: &bindv1alpha1.BindCatalogZone{Spec: bindv1alpha1.BindCatalogZoneSpec{ZoneName: "catalog.internal"}},
|
|
PrimaryAddress: "10.42.0.7",
|
|
}
|
|
_, secondary := RenderNamedConf(in)
|
|
if !strings.Contains(secondary, "primaries { 10.42.0.7; }") {
|
|
t.Fatalf("secondary should point primaries at the primary pod IP:\n%s", secondary)
|
|
}
|
|
}
|
|
|
|
func TestRenderForwardZoneInView(t *testing.T) {
|
|
rec := true
|
|
in := RenderInput{
|
|
Cluster: newCluster(bindv1alpha1.ModeResolver),
|
|
Views: []bindv1alpha1.BindView{{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "openforwarder"},
|
|
Spec: bindv1alpha1.BindViewSpec{ClusterRef: "auth", MatchClients: []string{"acl-main"}, Recursion: &rec},
|
|
}},
|
|
Forwards: []bindv1alpha1.BindZone{{
|
|
Spec: bindv1alpha1.BindZoneSpec{ClusterRef: "auth", ZoneName: "unkin.net", Type: bindv1alpha1.ZoneForward, ViewRef: "openforwarder", Forwarders: []string{"198.18.19.15"}},
|
|
}},
|
|
}
|
|
primary, secondary := RenderNamedConf(in)
|
|
for _, out := range []string{primary, secondary} {
|
|
if !strings.Contains(out, `view "openforwarder"`) {
|
|
t.Fatalf("view missing:\n%s", out)
|
|
}
|
|
if !strings.Contains(out, `zone "unkin.net" {`) || !strings.Contains(out, "type forward;") || !strings.Contains(out, "forwarders { 198.18.19.15; }") {
|
|
t.Fatalf("forward zone not rendered inside view (must be on all pods):\n%s", out)
|
|
}
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|