Files
mesh-host/internal/apply/health_check_test.go
T
jochen bdd44154cc Judge how a module says it is ready, beside whether it stays up (hq ADR 0240, to-be 48 Phase B)
Liveness alone could not see a web application whose port was open and whose
program ran while every request hung for eleven hours (issue 145). A resource
now carries the `health` its module declared: the engine makes http and tcp
looks itself from the machine to the endpoint's published port, reads a unit's
readiness from the show it already makes, hands an exec command or the image's
own check to the runtime as the container's check with the declared timing and
reads its state from the inspect it already makes, and asks a module's tool on
its own node tools. Starting until the check passed, unhealthy once its looks
after the grace fail the declared number of times; never more looks than the
measured budget; nothing restarted. The statement says contract 2, which tells
the controller this engine may be sent the field.
2026-10-07 14:08:32 +02:00

85 lines
3.5 KiB
Go

package apply
import (
"context"
"slices"
"strings"
"testing"
"github.com/novox/mesh-host/internal/declaration"
"github.com/novox/mesh-host/internal/store"
)
// The node-engine owns every verdict, and nothing else sets a container's check (novox/hq ADR 0240 rule 3,
// "how it is checked"): a declared command becomes the container's check with the declared timing, an
// adopted image check keeps the image's command under the declared timing, and a check the engine makes
// itself — http, tcp — sets nothing on the container and recreates nothing.
func TestADeclaredCommandBecomesTheContainersCheckAndNothingElseSetsOne(t *testing.T) {
pinned := "postgres@sha256:" + strings.Repeat("a", 64)
ranWith := func(health string) []string {
t.Helper()
var ran []string
run := func(_ context.Context, cmd string, args ...string) (string, error) {
if cmd == "docker" && len(args) > 0 && args[0] == "run" {
ran = args
return "deadbeef\n", nil
}
return "", nil
}
field := ""
if health != "" {
field = `,"health":` + health
}
d := parseTrusted(t, `{"declaration":1,"resources":[
{"id":"db","type":"container","name":"db","image":"`+pinned+`","ports":["31001:5432"]`+field+`}
]}`)
_, _, _ = Apply(context.Background(), archHost(t), d, store.State{}, store.OriginCarried, run, nil, nil)
if ran == nil {
t.Fatal("the container was not run")
}
return ran
}
healthFlags := func(args []string) []string {
var out []string
for i, a := range args {
if strings.HasPrefix(a, "--health") || a == "--no-healthcheck" {
out = append(out, a)
if i+1 < len(args) {
out = append(out, args[i+1])
}
}
}
return out
}
exec := healthFlags(ranWith(`{"kind":"exec","command":"pg_isready -q","interval":"30s","timeout":"5s","looks":3,"grace":"1m0s"}`))
want := []string{"--health-cmd", "pg_isready -q", "--health-interval", "30s", "--health-timeout", "5s",
"--health-retries", "3", "--health-start-period", "1m0s"}
if !slices.Equal(exec, want) {
t.Errorf("a declared command was run as %v, want %v", exec, want)
}
adopted := healthFlags(ranWith(`{"kind":"runtime","interval":"20s","timeout":"3s","looks":2,"grace":"30s"}`))
if slices.Contains(adopted, "--health-cmd") || !slices.Contains(adopted, "20s") || !slices.Contains(adopted, "2") {
t.Errorf("an adopted image check was run as %v: the image's command under the declared timing", adopted)
}
for _, h := range []string{"", `{"kind":"http","endpoint":"web","port":31001,"path":"/","interval":"30s","timeout":"5s","looks":3,"grace":"1m0s"}`,
`{"kind":"tcp","port":31001,"interval":"30s","timeout":"5s","looks":3,"grace":"1m0s"}`} {
if got := healthFlags(ranWith(h)); len(got) > 0 {
t.Errorf("a container whose check the engine makes itself (%q) was given the runtime's: %v", h, got)
}
}
// Declaring an http check recreates nothing; declaring a command does.
plain := &declaration.Container{ID: "db", Name: "db", Image: pinned}
withHTTP := *plain
withHTTP.Health = &declaration.Health{Kind: "http", Port: 31001, Path: "/", Interval: "30s", Timeout: "5s", Looks: 3, Grace: "1m0s"}
withExec := *plain
withExec.Health = &declaration.Health{Kind: "exec", Command: "true", Interval: "30s", Timeout: "5s", Looks: 3, Grace: "1m0s"}
if containerSpec(plain, inputs{}) != containerSpec(&withHTTP, inputs{}) {
t.Error("declaring an http check would recreate the container")
}
if containerSpec(plain, inputs{}) == containerSpec(&withExec, inputs{}) {
t.Error("declaring a command would not reach a running container: it is fixed when the container is made")
}
}