diff --git a/replays/bed_studio_test.go b/replays/bed_studio_test.go new file mode 100644 index 0000000..ff689eb --- /dev/null +++ b/replays/bed_studio_test.go @@ -0,0 +1,67 @@ +//go:build integration + +package replays + +import ( + "context" + "fmt" + "strings" + "testing" + "time" +) + +// **R-studio — a check that asks an address the program does not bind fails the bed, not a machine** +// (novox/hq ADR 0240 Phase D, done when). The hosted database suite's studio binds the address the runtime +// puts in HOSTNAME, so it answered only on its network address while its image's own check asked localhost: +// unhealthy for ever while working, until the module set HOSTNAME=0.0.0.0. Adopted by name without proof, it +// would have put back a good build. +// +// The replay is that image in miniature: a web server that binds $HOSTNAME's address, and an image check that +// asks localhost. Adopted as the module declared it before the fix, the bed fails it; with the fix, it proves +// it. The image is built here and removed after, with everything the bed made. +func TestBedFailsTheStudiosFalseUnhealthy(t *testing.T) { + docker, ok := DockerFromEnv() + if !ok { + t.Skip("no container runtime: the studio is a container") + } + ctx, cancel := context.WithTimeout(t.Context(), 5*time.Minute) + defer cancel() + if err := docker.Pull(ctx, "busybox:1.36"); err != nil { + t.Fatal(err) + } + tag := fmt.Sprintf("mesh-replay-studio:%d", time.Now().UnixNano()) + if err := docker.Build(ctx, tag, StudioDockerfile); err != nil { + t.Fatal(err) + } + defer docker.RemoveImage(context.Background(), tag) + was := BedFloor + BedFloor = 15 * time.Second + defer func() { BedFloor = was }() + + studio := func(env map[string]any) BedResource { + r := map[string]any{"id": "studio", "type": "container", "name": "supabase-studio", "image": tag, + "health": map[string]any{"kind": "runtime", "grace": "10s"}} + if env != nil { + r["env"] = env + } + return BedResource{Module: "supabase", ID: "studio", Resource: r, Listens: map[string]int{}} + } + before := docker.Prove(ctx, studio(nil), nil) + if !before.Failed || !strings.Contains(before.Said, "does not see it") { + t.Fatalf("the studio's false unhealthy was not failed on the bed: %+v", before) + } + t.Logf("before the fix: %s", before.Said) + after := docker.Prove(ctx, studio(map[string]any{"HOSTNAME": "0.0.0.0"}), nil) + if !after.Proved { + t.Fatalf("the studio with HOSTNAME=0.0.0.0 was not proved: %+v", after) + } + t.Logf("with the fix: %s", after.Said) +} + +// StudioDockerfile is the studio in miniature: it serves on the address HOSTNAME names, and its own check +// asks localhost, as the studio's image does. +const StudioDockerfile = `FROM busybox:1.36 +RUN mkdir -p /www && echo studio > /www/index.html +HEALTHCHECK --interval=5s --timeout=2s --retries=2 CMD wget -q -O /dev/null http://localhost:3000/ || exit 1 +CMD addr=$(grep -w "$HOSTNAME" /etc/hosts | head -n 1 | cut -f 1); exec httpd -f -p "${addr:-$HOSTNAME}:3000" -h /www +` diff --git a/replays/bed_test.go b/replays/bed_test.go index 3f90df6..9a4510e 100644 --- a/replays/bed_test.go +++ b/replays/bed_test.go @@ -2,7 +2,6 @@ package replays import ( "context" - "fmt" "os" "os/exec" "path/filepath" @@ -58,62 +57,6 @@ func TestBedProvesEveryChangedHealthCheck(t *testing.T) { } } -// **R-studio — a check that asks an address the program does not bind fails the bed, not a machine** -// (novox/hq ADR 0240 Phase D, done when). The hosted database suite's studio binds the address the runtime -// puts in HOSTNAME, so it answered only on its network address while its image's own check asked localhost: -// unhealthy for ever while working, until the module set HOSTNAME=0.0.0.0. Adopted by name without proof, it -// would have put back a good build. -// -// The replay is that image in miniature: a web server that binds $HOSTNAME's address, and an image check that -// asks localhost. Adopted as the module declared it before the fix, the bed fails it; with the fix, it proves -// it. The image is built here and removed after, with everything the bed made. -func TestBedFailsTheStudiosFalseUnhealthy(t *testing.T) { - docker, ok := DockerFromEnv() - if !ok { - t.Skip("no container runtime: the studio is a container") - } - ctx, cancel := context.WithTimeout(t.Context(), 5*time.Minute) - defer cancel() - if err := docker.Pull(ctx, "busybox:1.36"); err != nil { - t.Fatal(err) - } - tag := fmt.Sprintf("mesh-replay-studio:%d", time.Now().UnixNano()) - if err := docker.Build(ctx, tag, StudioDockerfile); err != nil { - t.Fatal(err) - } - defer docker.RemoveImage(context.Background(), tag) - was := BedFloor - BedFloor = 15 * time.Second - defer func() { BedFloor = was }() - - studio := func(env map[string]any) BedResource { - r := map[string]any{"id": "studio", "type": "container", "name": "supabase-studio", "image": tag, - "health": map[string]any{"kind": "runtime", "grace": "10s"}} - if env != nil { - r["env"] = env - } - return BedResource{Module: "supabase", ID: "studio", Resource: r, Listens: map[string]int{}} - } - before := docker.Prove(ctx, studio(nil), nil) - if !before.Failed || !strings.Contains(before.Said, "does not see it") { - t.Fatalf("the studio's false unhealthy was not failed on the bed: %+v", before) - } - t.Logf("before the fix: %s", before.Said) - after := docker.Prove(ctx, studio(map[string]any{"HOSTNAME": "0.0.0.0"}), nil) - if !after.Proved { - t.Fatalf("the studio with HOSTNAME=0.0.0.0 was not proved: %+v", after) - } - t.Logf("with the fix: %s", after.Said) -} - -// StudioDockerfile is the studio in miniature: it serves on the address HOSTNAME names, and its own check -// asks localhost, as the studio's image does. -const StudioDockerfile = `FROM busybox:1.36 -RUN mkdir -p /www && echo studio > /www/index.html -HEALTHCHECK --interval=5s --timeout=2s --retries=2 CMD wget -q -O /dev/null http://localhost:3000/ || exit 1 -CMD addr=$(grep -w "$HOSTNAME" /etc/hosts | head -n 1 | cut -f 1); exec httpd -f -p "${addr:-$HOSTNAME}:3000" -h /www -` - // What the bed proves is what the change touches: a long-running container whose declared check or image // changed, or that is new; never a step, never one left as it was. func TestTheBedProvesWhatTheChangeTouches(t *testing.T) { @@ -159,3 +102,10 @@ func TestTheBedProvesWhatTheChangeTouches(t *testing.T) { t.Fatalf("the bed would prove %v; want the new module's checked container and the one whose image moved", got) } } + +func orDefault(s, fallback string) string { + if s == "" { + return fallback + } + return s +} diff --git a/replays/bus_test.go b/replays/bus_test.go index 2d3b69b..befd01b 100644 --- a/replays/bus_test.go +++ b/replays/bus_test.go @@ -1,3 +1,5 @@ +//go:build integration + package replays import ( diff --git a/replays/crashloop_test.go b/replays/crashloop_test.go index db79457..029d863 100644 --- a/replays/crashloop_test.go +++ b/replays/crashloop_test.go @@ -1,3 +1,5 @@ +//go:build integration + package replays import ( @@ -111,13 +113,6 @@ func statementOf(path string) string { return string(raw) } -func orDefault(s, fallback string) string { - if s == "" { - return fallback - } - return s -} - func firstLineOf(s string) string { line, _, _ := strings.Cut(strings.TrimSpace(s), "\n") if len(line) > 400 { diff --git a/replays/merge-check.sh b/replays/merge-check.sh deleted file mode 100644 index fe63253..0000000 --- a/replays/merge-check.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh -# mesh-check-toolchain: go -# -# The Go part of the lab's own check (novox/hq issue 302): the replays register and its prover, run by the -# build seat in the mesh's Go toolchain, its own container, because the lab's merge-check.sh declares it -# (`# mesh-check-also: go replays/merge-check.sh`) — and by hand, from anywhere: -# `sh replays/merge-check.sh`. -# -# 1. formatted and vetted — the register, the replays and the prover compile, tests included; -# 2. the tests that need nothing but Go and git: every replay registered whole (TestEveryReplayIsWhole), -# and the bed's reading of what a change touches. -# -# **Said, never passed silently**: the replays themselves raise containers and a bus of the release the mesh -# runs; this container holds no container runtime, so they are not run here. They run from the lab's main, -# reviewed, in the gate of every core change, and a change to one is proven by `go run ./cmd/prove` before -# it merges. -set -eu -cd "$(dirname "$0")" - -unformatted=$(gofmt -l .) -if [ -n "$unformatted" ]; then - echo "not gofmt'd:" - echo "$unformatted" - exit 1 -fi -go vet ./... -go test -count=1 -run '^(TestEveryReplayIsWhole|TestTheBedProvesWhatTheChangeTouches)$' . -echo "NOT RUN HERE: the replays themselves — they raise containers, and this container holds no container runtime" diff --git a/replays/resolver_test.go b/replays/resolver_test.go index 1583c3b..6afcf73 100644 --- a/replays/resolver_test.go +++ b/replays/resolver_test.go @@ -1,3 +1,5 @@ +//go:build integration + package replays import ( diff --git a/replays/silentweb_test.go b/replays/silentweb_test.go index 60cdb30..7bfcda7 100644 --- a/replays/silentweb_test.go +++ b/replays/silentweb_test.go @@ -1,3 +1,5 @@ +//go:build integration + package replays import ( diff --git a/replays/store_test.go b/replays/store_test.go index 5c8eb2b..fdce789 100644 --- a/replays/store_test.go +++ b/replays/store_test.go @@ -1,3 +1,5 @@ +//go:build integration + package replays import (