From d93f1e4eab73fde93a83b036c9b98005e17c769a Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 7 Oct 2026 14:41:54 +0200 Subject: [PATCH] Replay the silent web application: its declared HTTP check raises it within two looks (hq ADR 0240 Phase B, issue 145) For eleven hours a web application's port was open and its program ran while every request hung; liveness and a TCP check both say it fine. R145 raises a web server whose application never answers, has the node-engine at its commit look at it with the module's declared HTTP check, and the controller at its commit raise the module's condition on the second look. Proved: it fails on the trunk before Phase B and passes on it. --- replays/register.go | 10 +++++ replays/silentweb_test.go | 93 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 replays/silentweb_test.go diff --git a/replays/register.go b/replays/register.go index 99baafc..5679aa4 100644 --- a/replays/register.go +++ b/replays/register.go @@ -105,6 +105,16 @@ var Register = []Replay{ Asserts: "a container whose program exits at start is said unhealthy by the node-engine, and its build fails " + "its gate on the first machine and is put back there", Package: ".", Test: "TestReplayCrashLoop", Files: []string{"cmd/mesh-controller/replays_test.go"}}, + // A module's again: the silent web application, which no liveness and no TCP check sees, raised by the + // HTTP check its module declares (ADR 0240 Phase B). + {ID: "R145", Issue: 145, Kind: Liveness, Repository: "mesh-controller", + Fix: "feat/health-the-field", Before: "origin/main", + With: []Also{{Repository: "mesh-host", Fix: "feat/health-the-field", Before: "origin/main"}}, + What: "a web application accepted TCP and answered nothing for eleven hours while the mesh said its machine " + + "healthy: its port was open and its program ran", + Asserts: "a web server whose application never answers is said unhealthy by its declared HTTP check within two " + + "looks, and the controller raises the module's condition on the second", + Package: ".", Test: "TestReplaySilentWebApp", Files: []string{"cmd/mesh-controller/replays_test.go"}}, } // Find is the replay of that id, or of that issue. diff --git a/replays/silentweb_test.go b/replays/silentweb_test.go new file mode 100644 index 0000000..60cdb30 --- /dev/null +++ b/replays/silentweb_test.go @@ -0,0 +1,93 @@ +package replays + +import ( + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + "time" +) + +// **R145 — a web application that accepts TCP and answers nothing is raised within two looks** (novox/hq +// ADR 0240 Phase B, "how it is checked", rule 4; issue 145). +// +// For eleven hours a web application's port was open and its program ran while every request hung, and the +// mesh said its machine was healthy; a person found it. Liveness cannot see it and a TCP check cannot either. +// The replay is the whole chain, each half at its commit: +// +// 1. a web server whose application never answers is raised here, as the node-engine raises a module's; +// 2. the node-engine at MESH_REPLAY_HOST looks at it with the HTTP check its module declares, two looks, +// and writes what it states (its TestReplaySilentWebAppIsSaidUnhealthy) — an engine older than the +// declared check states it alive and nothing more; +// 3. the controller at MESH_REPLAY_CONTROLLER hears that statement on two looks in a row and raises the +// module's condition on the second (its TestReplaySilentWebAppIsRaisedWithinTwoLooks). +// +// Needs a container runtime, Go, and MESH_TEST_POSTGRES for the controller's store; the container is removed +// after, whatever happened. +func TestReplaySilentWebApp(t *testing.T) { + host := orDefault(os.Getenv("MESH_REPLAY_HOST"), filepath.Join("..", "..", "mesh-host")) + controller := orDefault(os.Getenv("MESH_REPLAY_CONTROLLER"), filepath.Join("..", "..", "mesh-controller")) + for _, dir := range []string{host, controller} { + if _, err := os.Stat(filepath.Join(dir, "go.mod")); err != nil { + t.Skipf("no checkout at %s (MESH_REPLAY_HOST and MESH_REPLAY_CONTROLLER name them)", dir) + } + } + if os.Getenv("MESH_TEST_POSTGRES") == "" { + t.Skip("no MESH_TEST_POSTGRES: the controller's half raises its condition in a store") + } + docker, ok := DockerFromEnv() + if !ok { + t.Skip("no container runtime: the web application is a container") + } + ctx, cancel := context.WithTimeout(t.Context(), 10*time.Minute) + defer cancel() + + // 1. The silent web application (mesh-host internal/liveness SilentWebImage, SilentWebProgram). + const image = "busybox:1.36" + if err := docker.Pull(ctx, image); err != nil { + t.Fatal(err) + } + name := fmt.Sprintf("mesh-replay-silent-web-%d", time.Now().UnixNano()) + id, address, err := docker.Start(ctx, Run{Image: image, Name: name, Restart: "unless-stopped", + Labels: map[string]string{"mesh-host.id": "app.server"}, + Cmd: []string{"sh", "-c", "mkdir -p /www/cgi-bin && printf '#!/bin/sh\\nsleep 3600\\n' > /www/cgi-bin/app && " + + "chmod +x /www/cgi-bin/app && exec httpd -f -p 8080 -h /www"}}) + if err != nil { + t.Fatal(err) + } + defer docker.Remove(context.Background(), id) + + // 2. The node-engine at its commit looks at it, and says what it states. + statement := filepath.Join(t.TempDir(), "statement.json") + engine := exec.CommandContext(ctx, "go", "test", "-count=1", "-run", "^TestReplaySilentWebAppIsSaidUnhealthy$", + "./internal/liveness/") + engine.Dir = host + engine.Env = append(os.Environ(), "MESH_REPLAY_CONTAINER="+name, "MESH_REPLAY_ADDRESS="+address, + "MESH_REPLAY_STATEMENT="+statement, "GOFLAGS=") + out, err := engine.CombinedOutput() + switch { + case err == nil && !strings.Contains(string(out), "no tests to run"): + t.Logf("the node-engine said: %s", firstLineOf(statementOf(statement))) + case judgesNothing(string(out)) || strings.Contains(string(out), "no tests to run"): + // An engine older than the declared check: it states the application alive, and nothing more. + t.Fatalf("the node-engine at this commit does not look at a declared HTTP check") + default: + t.Fatalf("the node-engine did not say the silent web application unhealthy: %v\n%s", err, lastOf(string(out), 12)) + } + + // 3. The controller at its commit raises the module's condition from what the engine said. + raise := exec.CommandContext(ctx, "go", "test", "-count=1", "-run", "^TestReplaySilentWebAppIsRaisedWithinTwoLooks$", + "./cmd/mesh-controller/") + raise.Dir = controller + raise.Env = append(os.Environ(), "MESH_REPLAY_STATEMENT="+statement, "GOFLAGS=-mod=vendor", "GOPROXY=off") + out, err = raise.CombinedOutput() + if err != nil { + t.Fatalf("the controller did not raise the silent web application within two looks: %v\n%s", err, lastOf(string(out), 12)) + } + if strings.Contains(string(out), "no tests to run") { + t.Fatal("the controller at this commit has no half of the replay to run") + } +}