Clean up code reuse, consistency, and efficiency issues
Merge readExistingSerial and readExistingContent into a single readExisting function to eliminate duplicate file I/O. Extract dateBase helper to deduplicate serial formula between defaultSerial and bumpSerial. Cache hash results during collision check to avoid recomputing per member. Normalize error prefixes (remove "error:" from fmt.Errorf, add uniformly at print sites). Use filepath.Join instead of manual "/" concatenation. Replace trivial containsStr wrapper with strings.Contains. Simplify tokenize to a single return. Use writeTestFile and fixedTime helpers consistently in tests.
This commit is contained in:
81
catalog.go
81
catalog.go
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -29,12 +30,13 @@ func hashZoneName(zone string) string {
|
||||
h := fnv.New32a()
|
||||
h.Write([]byte(zone))
|
||||
sum := h.Sum32()
|
||||
b := make([]byte, 4)
|
||||
b[0] = byte(sum >> 24)
|
||||
b[1] = byte(sum >> 16)
|
||||
b[2] = byte(sum >> 8)
|
||||
b[3] = byte(sum)
|
||||
return b32.EncodeToString(b)
|
||||
b := [4]byte{
|
||||
byte(sum >> 24),
|
||||
byte(sum >> 16),
|
||||
byte(sum >> 8),
|
||||
byte(sum),
|
||||
}
|
||||
return b32.EncodeToString(b[:])
|
||||
}
|
||||
|
||||
// generateCatalogZone builds the zone file content for a single catalog.
|
||||
@@ -76,20 +78,22 @@ func generateCatalogZone(catName string, cfg *Config, members []ZoneEntry, seria
|
||||
return sorted[i].Zone < sorted[j].Zone
|
||||
})
|
||||
|
||||
// Check for hash collisions
|
||||
hashToZone := make(map[string]string)
|
||||
// Check for hash collisions and cache results
|
||||
hashToZone := make(map[string]string, len(sorted))
|
||||
zoneHash := make(map[string]string, len(sorted))
|
||||
for _, entry := range sorted {
|
||||
h := hashZoneName(entry.Zone)
|
||||
if existing, ok := hashToZone[h]; ok && existing != entry.Zone {
|
||||
return "", fmt.Errorf("error: %s:%d: hash collision between %s and %s in catalog %q",
|
||||
return "", fmt.Errorf("%s:%d: hash collision between %s and %s in catalog %q",
|
||||
entry.File, entry.Line, existing, entry.Zone, catName)
|
||||
}
|
||||
hashToZone[h] = entry.Zone
|
||||
zoneHash[entry.Zone] = h
|
||||
}
|
||||
|
||||
// Member records
|
||||
for _, entry := range sorted {
|
||||
h := hashZoneName(entry.Zone)
|
||||
h := zoneHash[entry.Zone]
|
||||
|
||||
// PTR record
|
||||
ptrOwner := fmt.Sprintf("%s.zones.%s", h, origin)
|
||||
@@ -126,58 +130,48 @@ func generateCatalogZone(catName string, cfg *Config, members []ZoneEntry, seria
|
||||
return strings.Join(records, "\n") + "\n", nil
|
||||
}
|
||||
|
||||
// readExistingSerial reads an existing zone file and extracts the SOA serial.
|
||||
// Returns 0, nil if the file doesn't exist.
|
||||
func readExistingSerial(path string) (uint32, error) {
|
||||
f, err := os.Open(path)
|
||||
// readExisting reads an existing zone file and returns its content and SOA serial.
|
||||
// Returns ("", 0, nil) if the file doesn't exist.
|
||||
func readExisting(path string) (string, uint32, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if os.IsNotExist(err) {
|
||||
return 0, nil
|
||||
return "", 0, nil
|
||||
}
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("reading existing zone %s: %w", path, err)
|
||||
return "", 0, fmt.Errorf("reading existing zone %s: %w", path, err)
|
||||
}
|
||||
defer f.Close()
|
||||
content := string(data)
|
||||
|
||||
zp := dns.NewZoneParser(f, "", path)
|
||||
zp := dns.NewZoneParser(strings.NewReader(content), "", path)
|
||||
for rr, ok := zp.Next(); ok; rr, ok = zp.Next() {
|
||||
if soa, ok := rr.(*dns.SOA); ok {
|
||||
return soa.Serial, nil
|
||||
return content, soa.Serial, nil
|
||||
}
|
||||
}
|
||||
if err := zp.Err(); err != nil {
|
||||
return 0, fmt.Errorf("parsing existing zone %s: %w", path, err)
|
||||
return "", 0, fmt.Errorf("parsing existing zone %s: %w", path, err)
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
return content, 0, nil
|
||||
}
|
||||
|
||||
// readExistingContent reads the full content of an existing zone file.
|
||||
// Returns empty string if file doesn't exist.
|
||||
func readExistingContent(path string) (string, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if os.IsNotExist(err) {
|
||||
return "", nil
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
// dateBase returns the YYYYMMDD00 serial base for the given time.
|
||||
func dateBase(now time.Time) uint32 {
|
||||
return uint32(now.Year())*1000000 +
|
||||
uint32(now.Month())*10000 +
|
||||
uint32(now.Day())*100
|
||||
}
|
||||
|
||||
// defaultSerial returns a serial for today with sequence 01: YYYYMMDD01.
|
||||
func defaultSerial(now time.Time) uint32 {
|
||||
return uint32(now.Year())*1000000 +
|
||||
uint32(now.Month())*10000 +
|
||||
uint32(now.Day())*100 + 1
|
||||
return dateBase(now) + 1
|
||||
}
|
||||
|
||||
// bumpSerial increments a serial. If same date, bumps the sequence number.
|
||||
// If different date, starts at YYYYMMDD01.
|
||||
// Returns error if sequence reaches 99 and needs another bump.
|
||||
func bumpSerial(old uint32, now time.Time) (uint32, error) {
|
||||
todayBase := uint32(now.Year())*1000000 +
|
||||
uint32(now.Month())*10000 +
|
||||
uint32(now.Day())*100
|
||||
todayBase := dateBase(now)
|
||||
|
||||
if old >= todayBase && old < todayBase+100 {
|
||||
// Same date, bump sequence
|
||||
@@ -196,10 +190,10 @@ func bumpSerial(old uint32, now time.Time) (uint32, error) {
|
||||
// Returns true if the file was written (changed), false if unchanged.
|
||||
func processCatalog(catName string, cfg *Config, members []ZoneEntry, outputDir string, now time.Time) (bool, error) {
|
||||
catCfg := cfg.Catalogs[catName]
|
||||
outputPath := outputDir + "/" + catCfg.Zone + "zone"
|
||||
outputPath := filepath.Join(outputDir, catCfg.Zone+"zone")
|
||||
|
||||
// Read existing serial
|
||||
oldSerial, err := readExistingSerial(outputPath)
|
||||
// Read existing file (content + serial in one pass)
|
||||
existing, oldSerial, err := readExisting(outputPath)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -217,11 +211,6 @@ func processCatalog(catName string, cfg *Config, members []ZoneEntry, outputDir
|
||||
}
|
||||
|
||||
// Compare with existing file
|
||||
existing, err := readExistingContent(outputPath)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if content == existing {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user