Add also-notify support for BIND domains.conf generation

Extend catz.yaml config with a bind-conf section mapping catalog names
to also-notify IP lists. Zones in catalogs with also-notify configured
get an also-notify directive in the generated domains.conf. IPs are
deduplicated and sorted when a zone belongs to multiple catalogs.
This commit is contained in:
2026-03-28 14:57:37 -07:00
parent 49f7ad2987
commit 9ff9abeabd
5 changed files with 159 additions and 11 deletions

View File

@@ -7,13 +7,15 @@ import (
"testing"
)
var emptyCfg = &Config{}
func TestGenerateBindConf(t *testing.T) {
entries := []ZoneEntry{
{Zone: "bitcard.org.", ZoneFile: "data/misc/bitcard.org", DNSSEC: true, ZonesFile: "test", Line: 1},
{Zone: "askask.com.", ZoneFile: "data/ask/askask.com", ZonesFile: "test", Line: 2},
}
got := generateBindConf(entries)
got := generateBindConf(entries, emptyCfg)
// Header
assertContains(t, got, "# THIS FILE IS GENERATED BY catalog-zone-gen")
@@ -44,7 +46,7 @@ func TestGenerateBindConf(t *testing.T) {
}
func TestGenerateBindConfEmpty(t *testing.T) {
got := generateBindConf(nil)
got := generateBindConf(nil, emptyCfg)
// Should just have the header
lines := splitLines(got)
if len(lines) != 3 {
@@ -57,7 +59,7 @@ func TestGenerateBindConfNoDNSSEC(t *testing.T) {
{Zone: "example.com.", ZoneFile: "data/example.com", ZonesFile: "test", Line: 1},
}
got := generateBindConf(entries)
got := generateBindConf(entries, emptyCfg)
if strings.Contains(got, "dnssec-policy") {
t.Error("non-DNSSEC zone should not have dnssec-policy")
@@ -73,7 +75,7 @@ func TestValidateBindConf(t *testing.T) {
{Zone: "a.example.com.", ZoneFile: "data/a", ZonesFile: "zones.txt", Line: 1},
{Zone: "b.example.com.", ZoneFile: "data/b", ZonesFile: "zones.txt", Line: 2},
}
if err := writeBindConf(filepath.Join(t.TempDir(), "domains.conf"), entries); err != nil {
if err := writeBindConf(filepath.Join(t.TempDir(), "domains.conf"), entries, emptyCfg); err != nil {
t.Fatalf("unexpected error: %v", err)
}
})
@@ -85,7 +87,7 @@ func TestValidateBindConf(t *testing.T) {
}
dir := t.TempDir()
path := filepath.Join(dir, "domains.conf")
if err := writeBindConf(path, entries); err != nil {
if err := writeBindConf(path, entries, emptyCfg); err != nil {
t.Fatalf("unexpected error: %v", err)
}
data, err := os.ReadFile(path)
@@ -109,7 +111,7 @@ func TestWriteBindConf(t *testing.T) {
{Zone: "example.org.", ZoneFile: "data/example.org", DNSSEC: true, ZonesFile: "test", Line: 2},
}
if err := writeBindConf(path, entries); err != nil {
if err := writeBindConf(path, entries, emptyCfg); err != nil {
t.Fatalf("unexpected error: %v", err)
}
@@ -132,7 +134,7 @@ func TestWriteBindConfSkipsCatalogOnlyZones(t *testing.T) {
{Zone: "example.com.", ZoneFile: "", ZonesFile: "zones.txt", Line: 5},
}
if err := writeBindConf(path, entries); err != nil {
if err := writeBindConf(path, entries, emptyCfg); err != nil {
t.Fatalf("unexpected error: %v", err)
}
@@ -148,3 +150,73 @@ func TestWriteBindConfSkipsCatalogOnlyZones(t *testing.T) {
t.Error("zone without file= should not appear in BIND config")
}
}
func TestGenerateBindConfAlsoNotify(t *testing.T) {
cfg := &Config{
BindConf: BindConfConfig{
AlsoNotify: map[string][]string{
"pch": {"198.51.100.1", "198.51.100.2"},
},
},
}
t.Run("zone in catalog with also-notify", func(t *testing.T) {
entries := []ZoneEntry{
{Zone: "example.com.", Catalogs: []string{"pch"}, ZoneFile: "data/example.com", ZonesFile: "test", Line: 1},
}
got := generateBindConf(entries, cfg)
assertContains(t, got, "also-notify { 198.51.100.1; 198.51.100.2; };")
})
t.Run("zone in catalog without also-notify", func(t *testing.T) {
entries := []ZoneEntry{
{Zone: "example.com.", Catalogs: []string{"standard"}, ZoneFile: "data/example.com", ZonesFile: "test", Line: 1},
}
got := generateBindConf(entries, cfg)
if strings.Contains(got, "also-notify") {
t.Error("zone in catalog without also-notify config should not have also-notify")
}
})
t.Run("zone in multiple catalogs combines IPs", func(t *testing.T) {
cfg := &Config{
BindConf: BindConfConfig{
AlsoNotify: map[string][]string{
"pch": {"198.51.100.1"},
"extra": {"198.51.100.2"},
},
},
}
entries := []ZoneEntry{
{Zone: "example.com.", Catalogs: []string{"pch", "extra"}, ZoneFile: "data/example.com", ZonesFile: "test", Line: 1},
}
got := generateBindConf(entries, cfg)
assertContains(t, got, "also-notify { 198.51.100.1; 198.51.100.2; };")
})
t.Run("duplicate IPs across catalogs are deduplicated", func(t *testing.T) {
cfg := &Config{
BindConf: BindConfConfig{
AlsoNotify: map[string][]string{
"pch": {"198.51.100.1", "198.51.100.2"},
"extra": {"198.51.100.2", "198.51.100.3"},
},
},
}
entries := []ZoneEntry{
{Zone: "example.com.", Catalogs: []string{"pch", "extra"}, ZoneFile: "data/example.com", ZonesFile: "test", Line: 1},
}
got := generateBindConf(entries, cfg)
assertContains(t, got, "also-notify { 198.51.100.1; 198.51.100.2; 198.51.100.3; };")
})
t.Run("catalog-only zone without file skips also-notify", func(t *testing.T) {
entries := []ZoneEntry{
{Zone: "example.com.", Catalogs: []string{"pch"}, ZoneFile: "", ZonesFile: "test", Line: 1},
}
got := generateBindConf(entries, cfg)
if strings.Contains(got, "also-notify") {
t.Error("catalog-only zone should not appear in bind config at all")
}
})
}