diff --git a/internal/kea/agent.go b/internal/kea/agent.go index 273f1c3..e5b848c 100644 --- a/internal/kea/agent.go +++ b/internal/kea/agent.go @@ -37,6 +37,15 @@ ORD="${HOSTNAME##*-}" mkdir -p %[1]s chmod 0750 %[1]s sed "s/%[2]s/server${ORD}/g" %[3]s/kea-dhcp4.conf > %[4]s +# The HA hook resolves peer URL hostnames once at load; on a cold container +# start the StatefulSet peer DNS records may not resolve yet, and kea exits +# hard instead of retrying. Wait for the config to validate before starting. +i=0 +until %[5]s -t %[4]s >/dev/null 2>&1; do + i=$((i+1)) + if [ "$i" -ge 60 ]; then break; fi + sleep 2 +done exec %[5]s -c %[4]s `, RunDir, ThisServerPlaceholder, ConfigDir, DHCP4ConfPath, DHCP4Bin) } diff --git a/internal/kea/config_test.go b/internal/kea/config_test.go index 47bc01a..396296d 100644 --- a/internal/kea/config_test.go +++ b/internal/kea/config_test.go @@ -249,6 +249,17 @@ func TestEntrypointsHardenSocketDir(t *testing.T) { } } +func TestEntrypointWaitsForConfigToValidate(t *testing.T) { + // The dhcp4 entrypoint must gate startup on `kea-dhcp4 -t` so a cold-start + // HA peer DNS resolution failure retries instead of crash-looping. + ep := EntrypointDHCP4() + for _, want := range []string{"until " + DHCP4Bin + " -t " + DHCP4ConfPath, "exec " + DHCP4Bin + " -c " + DHCP4ConfPath} { + if !strings.Contains(ep, want) { + t.Errorf("dhcp4 entrypoint must contain %q, got:\n%s", want, ep) + } + } +} + func TestControlSocketPathAllowedByKea(t *testing.T) { if !strings.HasPrefix(CtrlSocketPath, "/var/run/kea/") { t.Errorf("CtrlSocketPath %q must live under /var/run/kea (kea 2.6+ restriction)", CtrlSocketPath)