Add --bind-conf flag for BIND domains.conf generation

Generate a BIND-format domains.conf file alongside catalog zones.
New input properties: file= (zone data path) and dnssec (bare flag).
When --bind-conf is set, every zone must have file= or it errors.

Renames ZoneEntry.File to ZonesFile (input path for error messages)
and adds ZoneFile (BIND file path) and DNSSEC (bool) fields.
This commit is contained in:
2026-03-28 11:15:06 -07:00
parent 44d7867a0c
commit 0eddb9fcfe
9 changed files with 451 additions and 51 deletions

View File

@@ -13,6 +13,8 @@ func TestParseLine(t *testing.T) {
wantCats []string
wantGrp string
wantCOO string
wantFile string
wantDNS bool
wantErr bool
}{
{
@@ -55,6 +57,28 @@ func TestParseLine(t *testing.T) {
wantZone: "zone.example.org.",
wantCats: []string{"catalog1"},
},
{
name: "with file property",
line: "zone.example.org catalog1, file=data/zones/example.org",
wantZone: "zone.example.org.",
wantCats: []string{"catalog1"},
wantFile: "data/zones/example.org",
},
{
name: "with dnssec flag",
line: "zone.example.org catalog1, dnssec",
wantZone: "zone.example.org.",
wantCats: []string{"catalog1"},
wantDNS: true,
},
{
name: "with file and dnssec",
line: "zone.example.org catalog1, file=data/zones/example.org, dnssec",
wantZone: "zone.example.org.",
wantCats: []string{"catalog1"},
wantFile: "data/zones/example.org",
wantDNS: true,
},
{
name: "no catalog",
line: "zone.example.org",
@@ -80,6 +104,16 @@ func TestParseLine(t *testing.T) {
line: "zone.example.org catalog1, coo=",
wantErr: true,
},
{
name: "empty file value",
line: "zone.example.org catalog1, file=",
wantErr: true,
},
{
name: "dnssec with equals",
line: "zone.example.org catalog1, dnssec=yes",
wantErr: true,
},
}
for _, tt := range tests {
@@ -111,6 +145,12 @@ func TestParseLine(t *testing.T) {
if entry.COO != tt.wantCOO {
t.Errorf("coo = %q, want %q", entry.COO, tt.wantCOO)
}
if entry.ZoneFile != tt.wantFile {
t.Errorf("zonefile = %q, want %q", entry.ZoneFile, tt.wantFile)
}
if entry.DNSSEC != tt.wantDNS {
t.Errorf("dnssec = %v, want %v", entry.DNSSEC, tt.wantDNS)
}
})
}
}
@@ -150,8 +190,8 @@ func TestBuildCatalogMembers(t *testing.T) {
t.Run("valid input", func(t *testing.T) {
entries := []ZoneEntry{
{Zone: "a.example.com.", Catalogs: []string{"catalog1"}, File: "test", Line: 1},
{Zone: "b.example.com.", Catalogs: []string{"catalog1", "catalog2"}, File: "test", Line: 2},
{Zone: "a.example.com.", Catalogs: []string{"catalog1"}, ZonesFile: "test", Line: 1},
{Zone: "b.example.com.", Catalogs: []string{"catalog1", "catalog2"}, ZonesFile: "test", Line: 2},
}
members, err := buildCatalogMembers(entries, cfg)
@@ -169,7 +209,7 @@ func TestBuildCatalogMembers(t *testing.T) {
t.Run("unknown catalog", func(t *testing.T) {
entries := []ZoneEntry{
{Zone: "a.example.com.", Catalogs: []string{"unknown"}, File: "test", Line: 1},
{Zone: "a.example.com.", Catalogs: []string{"unknown"}, ZonesFile: "test", Line: 1},
}
_, err := buildCatalogMembers(entries, cfg)
if err == nil {
@@ -179,8 +219,8 @@ func TestBuildCatalogMembers(t *testing.T) {
t.Run("duplicate zone in same catalog", func(t *testing.T) {
entries := []ZoneEntry{
{Zone: "a.example.com.", Catalogs: []string{"catalog1"}, File: "test", Line: 1},
{Zone: "a.example.com.", Catalogs: []string{"catalog1"}, File: "test", Line: 2},
{Zone: "a.example.com.", Catalogs: []string{"catalog1"}, ZonesFile: "test", Line: 1},
{Zone: "a.example.com.", Catalogs: []string{"catalog1"}, ZonesFile: "test", Line: 2},
}
_, err := buildCatalogMembers(entries, cfg)
if err == nil {
@@ -207,7 +247,7 @@ test.example.net catalog1, group=internal
`
writeTestFile(t, dir, "zones.txt", content)
members, err := parseInput(inputPath, cfg)
_, members, err := parseInput(inputPath, cfg)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
@@ -228,7 +268,7 @@ func TestParseInputErrors(t *testing.T) {
}
t.Run("missing file", func(t *testing.T) {
_, err := parseInput("/nonexistent/zones.txt", cfg)
_, _, err := parseInput("/nonexistent/zones.txt", cfg)
if err == nil {
t.Fatal("expected error for missing file")
}
@@ -238,7 +278,7 @@ func TestParseInputErrors(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "zones.txt")
writeTestFile(t, dir, "zones.txt", "zone-with-no-catalog\n")
_, err := parseInput(path, cfg)
_, _, err := parseInput(path, cfg)
if err == nil {
t.Fatal("expected error for invalid line")
}
@@ -255,7 +295,7 @@ func TestBuildCatalogMembersSameZoneDifferentCatalogs(t *testing.T) {
// Same zone in different catalogs is OK
entries := []ZoneEntry{
{Zone: "a.example.com.", Catalogs: []string{"catalog1", "catalog2"}, File: "test", Line: 1},
{Zone: "a.example.com.", Catalogs: []string{"catalog1", "catalog2"}, ZonesFile: "test", Line: 1},
}
members, err := buildCatalogMembers(entries, cfg)