Files
at-container-registry/deploy/upcloud/health_test.go
T
Evan JarrettandClaude Fable 5.1 db6f37a070 deploy: never install a template config during update, and poll every restarted service's health
On 2026-09-09 the labeler's config was replaced with the rendered template,
which has empty identity fields (owner DID, DID, rotation key). The running
process kept its in-memory config, so nothing failed until the next restart
on 2026-09-12, when the labeler crash-looped on "labeler.owner_did is
required" and nobody was told: the deploy tool never probed the labeler at
all. The sync's missing-file branch is the only code that writes a whole
template, so an update now refuses when the file is gone and says to restore
it from the predeploy backup; provision keeps the first-install behaviour.

The health check was a single curl two seconds after restart. The hold takes
longer than that to open its listener, so tonight's deploy printed
HEALTH_FAIL for a hold that answered seconds later. Worse, the verdict was a
substring match on HEALTH_OK, which the scanner's SCANNER_HEALTH_OK line also
satisfies, so a failed hold next to a healthy scanner was reported healthy.
Each restarted service (hold, scanner, appview, labeler) is now polled every
two seconds for up to thirty, and reports on its own whole-label line.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EvFJr4Dwz8p2NDAeXmgmBt
2026-09-11 19:53:06 -05:00

33 lines
1.1 KiB
Go

package main
import (
"strings"
"testing"
)
func TestRenderHealthProbe(t *testing.T) {
s := renderHealthProbe("hold", "http://localhost:8080/xrpc/_health", 15)
for _, want := range []string{"seq 1 15", `curl -sf "http://localhost:8080/xrpc/_health"`, `echo "PROBE hold OK`, `echo "PROBE hold FAIL`, "sleep 2"} {
if !strings.Contains(s, want) {
t.Errorf("probe script missing %q:\n%s", want, s)
}
}
}
func TestParseProbesDoesNotConfuseServices(t *testing.T) {
out := "systemctl output\nPROBE hold FAIL 30\nPROBE scanner OK 4\nnoise PROBE labeler OK 2\n"
got := parseProbes(out)
if got["hold"].Status != "FAIL" || got["hold"].Seconds != 30 {
t.Errorf("hold = %+v, want FAIL after 30s", got["hold"])
}
if got["scanner"].Status != "OK" || got["scanner"].Seconds != 4 {
t.Errorf("scanner = %+v, want OK after 4s", got["scanner"])
}
if _, ok := got["labeler"]; ok {
t.Errorf("a PROBE token that is not at the start of a line must not count: %+v", got["labeler"])
}
if !probeMarkersPresent(out) {
t.Error("expected probe markers to be detected")
}
}