The controller asks no build of anybody (ADR 0501 §5, ADR 0525 clause 6, ADR 0503 §3). Every module of a walk is taken from the builder's done key; a key not done yet waits, named in the plan, and never fails it. - C2b/C2c: internal/builder and cmd/mesh-controller's asks go (askABuild, onlyTheBuildersOwn, buildOneAsked, takeIn from an outcome, buildAgentsOwn, the builder's say and its fallback, the daemon hearing an outcome). The server follows the retired seats' built subjects only to let go of them, because the installer's carried user list names them. - C2d: replay, queue, cancel, clear, kill, pause, resume, delivery-check, check-here and merge-gate are refused by name. - C3: the check queues (CHECKS_READY, CHECKS_OTHER) and their workers, the checks-running and plans-building buckets and their grants, the build queue and its hold (plans_building.go, queue.go, link/holds.go, builds_nats.go, checks_nats.go). - C4: merge_gate*.go, the pull request's repo-check path, the Checked event and its layers; a pull request asks the controller nothing but to tell mesh-config what its head declares. - C5: the builds bucket is written only by key_takein. - C10 (help and refusals): the terminal's help lists only what is served. Integration tests of the plan's tiers, sends, stops and retries now run on a stand-in builder with a done key for every module (walk_keys_scaffold_integration_test.go); the tests of the asks, the queue, the merge gate and the replays are deleted.
333 lines
14 KiB
Go
333 lines
14 KiB
Go
//go:build integration
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/asks"
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
"github.com/novox/mesh-controller/internal/link"
|
|
)
|
|
|
|
func theCatalogue(t *testing.T) *stores {
|
|
t.Helper()
|
|
open := aMesh(t)
|
|
ctx := t.Context()
|
|
for _, b := range []link.BuildResult{
|
|
onTrunk("build-sudo", "novox/mesh-catalog", "git", "modules/sudo", map[string]any{"module": "sudo", "version": "1"}),
|
|
onTrunk("build-host", "novox/mesh-host", "git", "", map[string]any{"module": "mesh-host", "version": "1"}),
|
|
} {
|
|
keptAsked(t, open.inventory, b.ID, b.Source.Repository, b.Path, true)
|
|
if _, _, err := takeIn(ctx, open.inventory, b); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
return open
|
|
}
|
|
|
|
func TestABuildFromAnAgentsRepositoryIsNotRegisteredAsAModuleTheMeshHolds(t *testing.T) {
|
|
open := theCatalogue(t)
|
|
ctx := t.Context()
|
|
for _, c := range []struct {
|
|
name, repository, path, module string
|
|
}{
|
|
{"its own repository naming sudo", "agent/sudo", "modules/sudo", "sudo"},
|
|
{"a fork of the catalogue", "agent/mesh-catalog", "modules/sudo", "sudo"},
|
|
{"a fork of the node-engine", "agent/mesh-host", "", "mesh-host"},
|
|
} {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
id := "build-" + strings.ReplaceAll(c.repository, "/", "-")
|
|
keptAsked(t, open.inventory, id, c.repository, c.path, false) // through the build verb
|
|
evil := onTrunk(id, c.repository, "git", c.path, map[string]any{"module": c.module, "version": "evil"})
|
|
_, _, err := takeIn(ctx, open.inventory, evil)
|
|
if !errors.Is(err, errNotItsSource) {
|
|
t.Fatalf("a build of %s was taken in as %s: %v", c.repository, c.module, err)
|
|
}
|
|
shelf, err := open.inventory.Catalogue(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := shelf[c.module].Version; got != "1" {
|
|
t.Fatalf("%s is now %q, from %s", c.module, got, c.repository)
|
|
}
|
|
if _, found, _ := open.inventory.BuildByID(ctx, id); !found {
|
|
t.Errorf("the refused build %s is not recorded", id)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestANewModuleFromARepositoryTheCatalogueDoesNotBuildFromIsNotRegistered(t *testing.T) {
|
|
open := theCatalogue(t)
|
|
ctx := t.Context()
|
|
keptAsked(t, open.inventory, "build-new", "agent/tools", "", false)
|
|
_, _, err := takeIn(ctx, open.inventory, onTrunk("build-new", "agent/tools", "git", "",
|
|
map[string]any{"module": "agent-tools", "version": "1"}))
|
|
if !errors.Is(err, errNotItsSource) {
|
|
t.Fatalf("a new module from an agent's repository was taken in: %v", err)
|
|
}
|
|
// And one asked of nobody here — an outcome on the bus no request was kept for — the same.
|
|
_, _, err = takeIn(ctx, open.inventory, onTrunk("build-unasked", "agent/tools", "git", "",
|
|
map[string]any{"module": "agent-tools", "version": "1"}))
|
|
if !errors.Is(err, errNotItsSource) {
|
|
t.Fatalf("an outcome nobody asked for was taken in: %v", err)
|
|
}
|
|
if shelf, _ := open.inventory.Catalogue(ctx); shelf["agent-tools"].Module != "" {
|
|
t.Fatal("the refused module is in the catalogue")
|
|
}
|
|
}
|
|
|
|
func TestAtTheTerminalAnotherRepositoryIsRegistered(t *testing.T) {
|
|
open := theCatalogue(t)
|
|
ctx := t.Context()
|
|
keptAsked(t, open.inventory, "build-moved", "novox/sudo", "", true)
|
|
if _, _, err := takeIn(ctx, open.inventory, onTrunk("build-moved", "novox/sudo", "git", "",
|
|
map[string]any{"module": "sudo", "version": "2"})); err != nil {
|
|
t.Fatalf("a move the operator asked for at the terminal was refused: %v", err)
|
|
}
|
|
if src, _ := open.inventory.SourceOf(ctx, "sudo"); src.Repository != "novox/sudo" {
|
|
t.Fatalf("sudo is built from %q", src.Repository)
|
|
}
|
|
if err := open.inventory.RecordBuildRequest(ctx, inventory.BuildRequest{ID: "build-external",
|
|
Repository: "http://forge.internal:20000/someone/app.git", For: "build", AtTerminal: true}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, _, err := takeIn(ctx, open.inventory, onTrunk("build-external", "someone/app", "", "",
|
|
map[string]any{"module": "app", "version": "1"})); err != nil {
|
|
t.Fatalf("a new module the operator asked for at the terminal was refused: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestADeliveryFromTheRegisteredRepositoryIsRegistered(t *testing.T) {
|
|
open := theCatalogue(t)
|
|
ctx := t.Context()
|
|
rebuilt := onTrunk("build-plan", "novox/mesh-catalog", "git", "modules/sudo", map[string]any{"module": "sudo", "version": "2"})
|
|
if err := open.inventory.RecordBuildRequest(ctx, inventory.BuildRequest{ID: rebuilt.ID,
|
|
Repository: "novox/mesh-catalog", Seat: "git", Path: "modules/sudo", For: "plan"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, _, err := takeIn(ctx, open.inventory, rebuilt); err != nil {
|
|
t.Fatalf("a plan's build of the module's own repository was refused: %v", err)
|
|
}
|
|
added := onTrunk("build-merge", "novox/mesh-catalog", "git", "modules/zram", map[string]any{"module": "zram", "version": "1"})
|
|
if _, _, err := takeIn(ctx, open.inventory, added); err != nil {
|
|
t.Fatalf("a module a merge added to the catalogue repository was refused: %v", err)
|
|
}
|
|
shelf, _ := open.inventory.Catalogue(ctx)
|
|
if shelf["sudo"].Version != "2" || shelf["zram"].Module == "" {
|
|
t.Fatalf("not registered: sudo %q, zram %q", shelf["sudo"].Version, shelf["zram"].Module)
|
|
}
|
|
}
|
|
|
|
func TestARollbackNeverPutsBackABuildFromAnotherRepository(t *testing.T) {
|
|
open := theCatalogue(t)
|
|
ctx := t.Context()
|
|
inv := open.inventory
|
|
fork := onTrunk("build-1791500000000000000", "agent/mesh-catalog", "git", "modules/sudo",
|
|
map[string]any{"module": "sudo", "version": "evil"})
|
|
fork.Commit = "c0ffee0123456789" // the commit sudo was registered at
|
|
keptAsked(t, inv, fork.ID, "agent/mesh-catalog", "modules/sudo", false)
|
|
if _, _, err := takeIn(ctx, inv, fork); !errors.Is(err, errNotItsSource) {
|
|
t.Fatalf("the fork's build was taken in: %v", err)
|
|
}
|
|
// Asked after the registered build, whenever the test runs: an id naming a fixed moment read as older
|
|
// than the registered build once the clock passed it (2026-10-10 02:40 UTC), and the test failed on main.
|
|
failed := onTrunk(fmt.Sprintf("build-%d", time.Now().Add(time.Hour).UnixNano()), "novox/mesh-catalog", "git", "modules/sudo",
|
|
map[string]any{"module": "sudo", "version": "2"})
|
|
failed.Commit = "badbadbad0123456"
|
|
if _, _, err := takeIn(ctx, inv, failed); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
record, _, err := inv.BuildByID(ctx, failed.ID)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
previous, found, err := inv.PreviousBuild(ctx, "sudo", "c0ffee0123456789", record)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if found && previous.ID == fork.ID {
|
|
t.Fatalf("a rollback would put back the fork's build %s", previous.ID)
|
|
}
|
|
if !found || previous.ID != "build-sudo" {
|
|
t.Fatalf("a rollback puts back %q (found %v), want the registered build-sudo", previous.ID, found)
|
|
}
|
|
}
|
|
|
|
func TestATrunkTheForgeDoesNotProtectRegistersNothing(t *testing.T) {
|
|
open := theCatalogue(t)
|
|
ctx := t.Context()
|
|
theForge.Store("novox/unguarded", forgeFacts{ID: 7, Why: "the rule main lets a person push to main directly"})
|
|
t.Cleanup(func() { theForge.Delete("novox/unguarded") })
|
|
first := onTrunk("build-unguarded-1", "novox/unguarded", "git", "", map[string]any{"module": "unguarded", "version": "1"})
|
|
keptAsked(t, open.inventory, first.ID, "novox/unguarded", "", true)
|
|
if _, _, err := takeIn(ctx, open.inventory, first); err != nil {
|
|
t.Fatalf("at the terminal: %v", err)
|
|
}
|
|
again := onTrunk("build-unguarded-2", "novox/unguarded", "git", "", map[string]any{"module": "unguarded", "version": "2"})
|
|
if _, _, err := takeIn(ctx, open.inventory, again); !errors.Is(err, errNotItsSource) ||
|
|
!strings.Contains(err.Error(), "push to main directly") {
|
|
t.Fatalf("a rebuild from an unprotected trunk was taken in: %v", err)
|
|
}
|
|
theForge.Store("novox/unguarded", errors.New("nothing serves gitea.gitea_api"))
|
|
if _, _, err := takeIn(ctx, open.inventory, onTrunk("build-unguarded-3", "novox/unguarded", "git", "",
|
|
map[string]any{"module": "unguarded", "version": "3"})); !errors.Is(err, errNotItsSource) {
|
|
t.Fatalf("a forge that could not say was read as a protected trunk: %v", err)
|
|
}
|
|
if shelf, _ := open.inventory.Catalogue(ctx); shelf["unguarded"].Version != "1" {
|
|
t.Fatalf("unguarded is %q", shelf["unguarded"].Version)
|
|
}
|
|
}
|
|
|
|
func TestARepositoryMadeAgainUnderItsNameIsNotTheModulesSource(t *testing.T) {
|
|
open := theCatalogue(t)
|
|
ctx := t.Context()
|
|
theForge.Store("novox/remade", forgeFacts{ID: 100, Guarded: true})
|
|
t.Cleanup(func() { theForge.Delete("novox/remade") })
|
|
first := onTrunk("build-remade-1", "novox/remade", "git", "", map[string]any{"module": "remade", "version": "1"})
|
|
keptAsked(t, open.inventory, first.ID, "novox/remade", "", true)
|
|
if _, _, err := takeIn(ctx, open.inventory, first); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if id, _ := open.inventory.SourceIdentity(ctx, "remade"); id != 100 {
|
|
t.Fatalf("the forge's id was not recorded: %d", id)
|
|
}
|
|
theForge.Store("novox/remade", forgeFacts{ID: 101, Guarded: true}) // deleted, and made again by an agent
|
|
if _, _, err := takeIn(ctx, open.inventory, onTrunk("build-remade-2", "novox/remade", "git", "",
|
|
map[string]any{"module": "remade", "version": "evil"})); !errors.Is(err, errNotItsSource) ||
|
|
!strings.Contains(err.Error(), "made again") {
|
|
t.Fatalf("a repository made again under the name was taken in: %v", err)
|
|
}
|
|
// And a new module from it, beside the one registered from the first, the same.
|
|
if _, _, err := takeIn(ctx, open.inventory, onTrunk("build-remade-3", "novox/remade", "git", "modules/other",
|
|
map[string]any{"module": "other", "version": "1"})); !errors.Is(err, errNotItsSource) {
|
|
t.Fatalf("a new module from a repository made again was taken in: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestATerminalRequestCoversOnlyWhatItAsked(t *testing.T) {
|
|
open := theCatalogue(t)
|
|
ctx := t.Context()
|
|
if err := open.inventory.RecordBuildRequest(ctx, inventory.BuildRequest{ID: "build-asked", Repository: "novox/app",
|
|
Seat: "git", Path: "modules/app", For: "build", AtTerminal: true}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
other := onTrunk("build-asked", "agent/sudo", "git", "modules/app", map[string]any{"module": "sudo", "version": "evil"})
|
|
if _, _, err := takeIn(ctx, open.inventory, other); !errors.Is(err, errNotItsSource) {
|
|
t.Fatalf("an outcome of another repository under a terminal request's id was taken in: %v", err)
|
|
}
|
|
elsewhere := onTrunk("build-asked", "novox/app", "git", "modules/sudo", map[string]any{"module": "sudo", "version": "evil"})
|
|
if _, _, err := takeIn(ctx, open.inventory, elsewhere); !errors.Is(err, errNotItsSource) {
|
|
t.Fatalf("an outcome of another path under a terminal request's id was taken in: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestTheServingControllerIsNeverTheTerminal(t *testing.T) {
|
|
t.Setenv(verbVar, "")
|
|
t.Setenv(link.CallerVar, "")
|
|
t.Setenv(servedVar, "")
|
|
if !startedAtTheTerminal() {
|
|
t.Fatal("a process started by hand is not the terminal")
|
|
}
|
|
markServed()
|
|
if startedAtTheTerminal() {
|
|
t.Fatal("the serving controller reads as the terminal")
|
|
}
|
|
child := exec.Command(os.Args[0], "-test.run=^$")
|
|
child.Env = os.Environ()
|
|
if !slices.Contains(child.Env, servedVar+"=1") {
|
|
t.Fatal("what the serving controller starts does not carry its mark")
|
|
}
|
|
}
|
|
|
|
func TestAnUnknownIdentityKeepsTheRecordedOne(t *testing.T) {
|
|
open := theCatalogue(t)
|
|
ctx := t.Context()
|
|
if err := open.inventory.SetSourceIdentity(ctx, "sudo", 100); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := open.inventory.SetSourceIdentity(ctx, "sudo", 0); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if id, _ := open.inventory.SourceIdentity(ctx, "sudo"); id != 100 {
|
|
t.Fatalf("the recorded id became %d", id)
|
|
}
|
|
}
|
|
|
|
func TestAModuleMovesOnTheOperatorsApproveOfTheMergeThatMovedIt(t *testing.T) {
|
|
const commit = "c0ffee0123456789"
|
|
for _, c := range []struct {
|
|
what string
|
|
change func(*anApprovedMerge)
|
|
unkept bool
|
|
request string // who asked: "merge", or "build" through a verb
|
|
moved bool
|
|
}{
|
|
{what: "the operator's Approve of the merged head", request: "merge", moved: true},
|
|
{what: "no merge mesh-delivery announced", unkept: true, request: "merge"},
|
|
{what: "a warrant for other heads", request: "merge", change: func(a *anApprovedMerge) {
|
|
a.merge.Heads = map[string]string{"novox/mesh-host@aaaaaaaaaaaa": "e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5"}
|
|
}},
|
|
{what: "a Decline", request: "merge", change: func(a *anApprovedMerge) {
|
|
a.warrant.Option, a.warrant.Label, a.merge.Option = "decline", "Decline", "decline"
|
|
}},
|
|
{what: "a person who is not the operator", request: "merge", change: func(a *anApprovedMerge) { a.warrant.By.Who = "agent" }},
|
|
{what: "a build asked through a verb", request: "build"},
|
|
} {
|
|
open := theCatalogue(t)
|
|
ctx := t.Context()
|
|
inv := open.inventory
|
|
a := approvedMerge(t)
|
|
a.merge.Owner, a.merge.Repo, a.merge.Number, a.merge.HeadSHA = "novox", "mesh-host", 7, "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1"
|
|
if c.change != nil {
|
|
c.change(&a)
|
|
}
|
|
wasKept, wasAsk := keptDeliveryMerge, routerAskOf
|
|
keptDeliveryMerge = func(_ context.Context, owner, repo string, number int, head string) (deliveryMerge, bool, error) {
|
|
return a.merge, !c.unkept && owner == "novox" && repo == "mesh-host" && number == 7 && head == a.merge.HeadSHA, nil
|
|
}
|
|
routerAskOf = func(_ context.Context, _ catalogueReader, _, id string) (*asks.Ask, *asks.Warrant, error) {
|
|
if id != a.merge.AskID {
|
|
t.Errorf("%s: the router was asked for %q", c.what, id)
|
|
}
|
|
return &a.ask, &a.warrant, nil
|
|
}
|
|
t.Cleanup(func() { keptDeliveryMerge, routerAskOf = wasKept, wasAsk })
|
|
event, _ := json.Marshal(map[string]any{"owner": "novox", "repo": "mesh-host", "base": "main", "number": 7,
|
|
"head_sha": a.merge.HeadSHA, "merge_commit_sha": commit})
|
|
if _, err := inv.AddMerge(ctx, inventory.BatchedMerge{Repository: "novox/mesh-host", Branch: "main", Commit: commit,
|
|
Merged: time.Now(), Heard: time.Now(), Event: event}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
id := fmt.Sprintf("build-%d", time.Now().Add(time.Hour).UnixNano())
|
|
if err := inv.RecordBuildRequest(ctx, inventory.BuildRequest{ID: id, Repository: "novox/mesh-host", Seat: "git",
|
|
Path: "modules/sudo", Ref: "main", Commit: commit, For: c.request}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
moved := onTrunk(id, "novox/mesh-host", "git", "modules/sudo", map[string]any{"module": "sudo", "version": "2"})
|
|
moved.Commit = commit
|
|
_, _, err := takeIn(ctx, inv, moved)
|
|
src, _ := inv.SourceOf(ctx, "sudo")
|
|
switch {
|
|
case c.moved && err != nil:
|
|
t.Errorf("%s: the move was refused: %v", c.what, err)
|
|
case c.moved && src.Repository != "novox/mesh-host":
|
|
t.Errorf("%s: sudo is built from %q", c.what, src.Repository)
|
|
case !c.moved && !errors.Is(err, errNotItsSource):
|
|
t.Errorf("%s: the move was not refused as ADR 0266 has it: %v", c.what, err)
|
|
case !c.moved && src.Repository != "novox/mesh-catalog":
|
|
t.Errorf("%s: a refused move changed sudo's source to %q", c.what, src.Repository)
|
|
}
|
|
}
|
|
}
|