Every check the mesh had ran after a merge, on a machine: a manifest the node-engine refused (236), an identity a real machine's name made too long (263). merge-gate raises the mesh as the facts snapshot says it is and the mesh with the change, each in a throwaway store through the controller's own records, composes every machine twice and validates it with the node-engine's validator, and fails what the change breaks, naming the machine's roles and the module - plus a manifest the judging controller cannot read, a consumer left out of its grant, a module removed while a machine runs it, a new module the node-engine would refuse; it warns on a wide rebuild. The forge's new head of a pull request becomes a check the controller asks of the build seat: the head and, beside it, the controller the mesh runs, the catalogue, the host and the lab; a throwaway store and bus of the versions the mesh runs; the repository's merge-check.sh in the mesh's Go toolchain with no container runtime socket; then mesh-lab's replays. The verdict is said as checked, an error never a pass, and nothing is recorded or registered.
153 lines
6.3 KiB
Go
153 lines
6.3 KiB
Go
package builder
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/novox/mesh-controller/internal/artifacts"
|
|
"github.com/novox/mesh-controller/internal/facts"
|
|
)
|
|
|
|
// A merge check on the build seat (novox/hq to-be 45 §9), against a real container runtime and a real
|
|
// registry: the repository's own merge-check.sh runs with the facts the controller keeps, beside a
|
|
// throwaway store and bus of **the versions the mesh runs**, and everything raised is removed.
|
|
//
|
|
// MESH_TEST_DOCKER=1 MESH_TEST_REGISTRY=127.0.0.1:15000 go test ./internal/builder -run Check
|
|
|
|
func TestTheStoreAndBusAChecksStandsOnAreTheOnesTheMeshRuns(t *testing.T) {
|
|
if got := StoreImage("17.11"); got != "postgres:17-alpine" {
|
|
t.Errorf("a store at 17.11 is checked against %s", got)
|
|
}
|
|
if got := StoreImage("16.4 (Debian 16.4-1.pgdg120+1)"); got != "postgres:16-alpine" {
|
|
t.Errorf("a store at 16.4 is checked against %s", got)
|
|
}
|
|
if got := BusImage("2.11.17"); got != "nats:2.11.17-alpine" {
|
|
t.Errorf("a bus at 2.11.17 is checked against %s", got)
|
|
}
|
|
}
|
|
|
|
// aRepository is a git repository holding these files, committed, and its head.
|
|
func aCheckedRepository(t *testing.T, files map[string]string) (string, string) {
|
|
t.Helper()
|
|
dir := t.TempDir()
|
|
git := func(args ...string) string {
|
|
cmd := exec.Command("git", args...)
|
|
cmd.Dir = dir
|
|
cmd.Env = append(os.Environ(), "GIT_AUTHOR_NAME=t", "GIT_AUTHOR_EMAIL=t@example.org",
|
|
"GIT_COMMITTER_NAME=t", "GIT_COMMITTER_EMAIL=t@example.org")
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("git %v: %v\n%s", args, err, out)
|
|
}
|
|
return strings.TrimSpace(string(out))
|
|
}
|
|
git("init", "--quiet", "-b", "main")
|
|
for name, body := range files {
|
|
if err := os.WriteFile(filepath.Join(dir, name), []byte(body), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
git("add", "-A")
|
|
git("commit", "--quiet", "-m", "x")
|
|
return dir, git("rev-parse", "HEAD")
|
|
}
|
|
|
|
func checkEnvironment(t *testing.T) string {
|
|
t.Helper()
|
|
if os.Getenv("MESH_TEST_DOCKER") != "1" || os.Getenv("MESH_TEST_REGISTRY") == "" {
|
|
t.Skip("MESH_TEST_DOCKER=1 and MESH_TEST_REGISTRY: a check raises containers and reads the registry")
|
|
}
|
|
registry := os.Getenv("MESH_TEST_REGISTRY")
|
|
body, err := json.Marshal(facts.Facts{Format: facts.Format, Taken: time.Now().UTC(),
|
|
Versions: facts.Versions{Bus: "2.11.17", Store: "17.11"},
|
|
Machines: []facts.Machine{{Name: "abcdef", Length: 6}}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := (artifacts.Store{Address: registry}).PutTagged(t.Context(), facts.Repository, facts.Tag,
|
|
facts.MediaType, body); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return registry
|
|
}
|
|
|
|
// goToolchain is the Go image a check's script runs in here: the base the controller's own image is built on.
|
|
const goToolchain = "golang@sha256:8ac98ca534ac3f51e1f420a1dd2c15e74c75cfa0f23f3ad27eb5d7236c349a0c"
|
|
|
|
func labelled(id string) []string {
|
|
out, _ := exec.Command("docker", "ps", "-aq", "--filter", "label="+BuildLabel+"="+id).Output()
|
|
return strings.Fields(string(out))
|
|
}
|
|
|
|
func TestACheckRunsTheRepositorysOwnScriptBesideTheMeshsVersionsAndLeavesNothing(t *testing.T) {
|
|
registry := checkEnvironment(t)
|
|
// The script proves what it was given: the facts, a store that answers, a bus that answers, and
|
|
// writes the gate's verdict where it is told to.
|
|
script := `set -e
|
|
test -s "$MESH_FACTS"
|
|
grep -q '"bus": "2.11.17"' "$MESH_FACTS" || grep -q '"bus":"2.11.17"' "$MESH_FACTS"
|
|
case "$MESH_TEST_POSTGRES" in postgres://*127.0.0.1:*) ;; *) echo "no store: $MESH_TEST_POSTGRES"; exit 1;; esac
|
|
case "$MESH_TEST_NATS" in nats://127.0.0.1:*) ;; *) echo "no bus: $MESH_TEST_NATS"; exit 1;; esac
|
|
test "$MESH_CHECK_REPOSITORY" = "novox/mesh-controller"
|
|
test "$MESH_CHECK_CHANGED" = "a.go,b.go"
|
|
test ! -S /var/run/docker.sock || { echo "the check holds the container runtime's socket"; exit 1; }
|
|
echo '{"verdict":"warning","summary":"a merge rebuilds 14 module(s)"}' > "$MESH_CHECK_VERDICT"
|
|
echo checked
|
|
`
|
|
repo, head := aCheckedRepository(t, map[string]string{CheckScript: script})
|
|
id := fmt.Sprintf("check-test-%d", time.Now().UnixNano())
|
|
v, err := Check(t.Context(), Command, CheckSpec{ID: id, Repository: repo, Ref: head, Owner: "novox",
|
|
Repo: "mesh-controller", Number: 7, Paths: []string{"a.go", "b.go"}, Toolchain: goToolchain}, t.TempDir(), registry, GitCredential{}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if v.Verdict != "warning" || v.Summary != "a merge rebuilds 14 module(s)" || !strings.Contains(v.Report, "checked") {
|
|
t.Fatalf("the check answered %+v", v)
|
|
}
|
|
if left := labelled(id); len(left) > 0 {
|
|
t.Errorf("the check left %d container(s) behind", len(left))
|
|
}
|
|
}
|
|
|
|
func TestAFailingCheckFailsAndOneThatCannotRunIsNeverAPass(t *testing.T) {
|
|
registry := checkEnvironment(t)
|
|
repo, head := aCheckedRepository(t, map[string]string{CheckScript: "echo 'resource \"x.service\": refused'; exit 3\n"})
|
|
v, err := Check(t.Context(), Command, CheckSpec{ID: fmt.Sprintf("check-fail-%d", time.Now().UnixNano()),
|
|
Repository: repo, Ref: head, Owner: "novox", Repo: "mesh-controller", Toolchain: goToolchain}, t.TempDir(), registry, GitCredential{}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if v.Verdict != "fail" || !strings.Contains(v.Summary, "refused") {
|
|
t.Fatalf("a failing script answered %+v", v)
|
|
}
|
|
|
|
// Past its bound: an error, not a pass.
|
|
was := CheckTimeout
|
|
CheckTimeout = 25 * time.Second
|
|
t.Cleanup(func() { CheckTimeout = was })
|
|
repo, head = aCheckedRepository(t, map[string]string{CheckScript: "sleep 120\n"})
|
|
v, err = Check(context.Background(), Command, CheckSpec{ID: fmt.Sprintf("check-slow-%d", time.Now().UnixNano()),
|
|
Repository: repo, Ref: head, Owner: "novox", Repo: "mesh-controller", Toolchain: goToolchain}, t.TempDir(), registry, GitCredential{}, nil)
|
|
if err == nil && v.Verdict == "pass" {
|
|
t.Fatalf("a check past its bound passed: %+v", v)
|
|
}
|
|
if err == nil && v.Verdict != "error" {
|
|
t.Fatalf("a check past its bound answered %+v", v)
|
|
}
|
|
|
|
// No facts: it cannot run, and says so.
|
|
repo, head = aCheckedRepository(t, map[string]string{CheckScript: "exit 0\n"})
|
|
_, err = Check(t.Context(), Command, CheckSpec{ID: "check-nofacts", Repository: repo, Ref: head, Owner: "novox",
|
|
Repo: "mesh-controller", Toolchain: goToolchain}, t.TempDir(), "127.0.0.1:1", GitCredential{}, nil)
|
|
if err == nil || !strings.Contains(err.Error(), "facts snapshot") {
|
|
t.Fatalf("a check with no facts said %v", err)
|
|
}
|
|
}
|