fb103a9e95
Two bugs made every provisioned zone fail to load: 1. The seed zone's apex NS (ns1.<zone>) is in-zone but had no address record, so BIND check-integrity refused to load it and rndc addzone reverted. Add a glue A record pointing at the primary pod IP. 2. Secondaries rendered primaries/default-primaries with the primary's DNS name, but BIND only accepts IP addresses there (it read the name as a remote-servers list and failed config load, crash-looping the secondary). Render the primary pod IP instead, and watch Pods so the config re-renders when that IP appears or changes. - bind.WriteSeedZone writes 'ns1 IN A <primaryIP>' glue - controllers resolve primaryPodIP and pass it to the seed (requeue if the primary has no IP yet) - BindCluster renders PrimaryAddress from pod-0's IP and watches Pods - render omits catalog primaries when the IP is unknown (no empty list)
77 lines
2.8 KiB
Go
77 lines
2.8 KiB
Go
package bind
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// ZoneFilePath returns the on-pod path of a zone database file.
|
|
func ZoneFilePath(zone string) string {
|
|
return fmt.Sprintf("%s/zones/db.%s", DataDir, strings.TrimSuffix(zone, "."))
|
|
}
|
|
|
|
// CatalogFilePath returns the on-pod path of a catalog zone database file.
|
|
func CatalogFilePath(zone string) string {
|
|
return fmt.Sprintf("%s/catalog/db.%s", DataDir, strings.TrimSuffix(zone, "."))
|
|
}
|
|
|
|
// ZoneExists reports whether a zone is currently loaded on the pod.
|
|
func (e *Executor) ZoneExists(ctx context.Context, namespace, pod, zone, view string) bool {
|
|
args := []string{"zonestatus", zone}
|
|
if view != "" {
|
|
args = append(args, "in", view)
|
|
}
|
|
_, err := e.Rndc(ctx, namespace, pod, args...)
|
|
return err == nil
|
|
}
|
|
|
|
// WriteSeedZone writes a minimal loadable zone file (SOA + apex NS + glue) to
|
|
// path, creating parent directories. The apex NS is the in-zone name ns1, and a
|
|
// glue A record pointing at primaryIP is included so BIND's check-integrity
|
|
// accepts the zone (an in-zone NS without an address record is a load error).
|
|
// It is only safe to call when creating a zone, as it overwrites any existing
|
|
// file. This is a placeholder that is replaced once real records are loaded.
|
|
func (e *Executor) WriteSeedZone(ctx context.Context, namespace, pod, zone, path, primaryIP string, serial int64) error {
|
|
origin := dot(zone)
|
|
ns := "ns1." + origin
|
|
content := fmt.Sprintf(`$TTL 3600
|
|
@ IN SOA %s hostmaster.%s (
|
|
%d ; serial
|
|
3600 ; refresh
|
|
900 ; retry
|
|
1209600 ; expire
|
|
300 ) ; minimum
|
|
@ IN NS %s
|
|
ns1 IN A %s
|
|
`, ns, origin, serial, ns, primaryIP)
|
|
|
|
cmd := []string{"sh", "-c", fmt.Sprintf("mkdir -p \"$(dirname '%s')\" && cat > '%s'", path, path)}
|
|
if out, err := e.Exec(ctx, namespace, pod, cmd, content); err != nil {
|
|
return fmt.Errorf("seed zone %s: %w (out: %s)", zone, err, out)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AddCatalogMember registers a member zone in a catalog zone by adding the
|
|
// catalog PTR record, so secondaries auto-provision it.
|
|
func (e *Executor) AddCatalogMember(ctx context.Context, namespace, pod, catalogZone, memberZone string, creds TSIGCreds) error {
|
|
hash := catalogHash(memberZone)
|
|
owner := fmt.Sprintf("%s.zones.%s", hash, dot(catalogZone))
|
|
updates := []RecordUpdate{{
|
|
FQDN: owner,
|
|
Type: "PTR",
|
|
TTL: 3600,
|
|
Values: []string{dot(memberZone)},
|
|
}}
|
|
return e.NSUpdate(ctx, namespace, pod, catalogZone, creds, updates)
|
|
}
|
|
|
|
// RemoveCatalogMember deregisters a member zone from a catalog zone.
|
|
func (e *Executor) RemoveCatalogMember(ctx context.Context, namespace, pod, catalogZone, memberZone string, creds TSIGCreds) error {
|
|
hash := catalogHash(memberZone)
|
|
owner := fmt.Sprintf("%s.zones.%s", hash, dot(catalogZone))
|
|
updates := []RecordUpdate{{FQDN: owner, Type: "PTR", Delete: true}}
|
|
return e.NSUpdate(ctx, namespace, pod, catalogZone, creds, updates)
|
|
}
|