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.
145 lines
8.1 KiB
Go
145 lines
8.1 KiB
Go
package main
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
)
|
|
|
|
// A walk stopped by hand while its tier waited — modules asked on the builder's keys, two built and never sent, the next
|
|
// tier not yet asked — is resumed by `plans retry` (issue 1886): set building again, its stop cleared, the asks it holds
|
|
// kept, so the next look carries it on as any walk. Nothing in it failed, so the retry of failed builds had nothing to do
|
|
// and refused it, leaving a stopped R4e with no verb to carry its 48 modules on.
|
|
func TestRetryResumesAWalkStoppedByHand(t *testing.T) {
|
|
at := time.Date(2026, 10, 23, 0, 1, 53, 0, time.UTC)
|
|
sent := at.Add(-time.Hour)
|
|
p := inventory.Plan{ID: "plan-r4e", Repository: "novox/mesh-sdk", Branch: "main", Commit: "7aacbc16",
|
|
Created: at.Add(-2 * time.Hour), State: inventory.PlanFailed, Tier: 1,
|
|
Note: "stopped by a person at tier 1: issue 1886",
|
|
Delivery: &inventory.PlanDelivery{Stopped: "a person (fix-1886@shanks)", StoppedWhy: "issue 1886",
|
|
Kept: []inventory.PlanKept{{Module: "mesh-controller"}}},
|
|
Modules: map[string]*inventory.PlanModule{}}
|
|
tier0 := []string{"mesh-controller", "mesh-sdk", "pacman", "route-proxy"}
|
|
for _, m := range tier0 {
|
|
p.Modules[m] = &inventory.PlanModule{State: "built", BuiltAt: &sent, SentAt: &sent}
|
|
}
|
|
var tier1 []string
|
|
for _, m := range []string{"mesh-tools-go", "mesh-tools"} {
|
|
tier1 = append(tier1, m)
|
|
p.Modules[m] = &inventory.PlanModule{State: "built", AskedAt: &sent, BuiltAt: &sent, Build: "build-" + m}
|
|
}
|
|
onKeys := []string{"mesh-build-go", "mesh-build-image", "mesh-build-node", "mesh-delivery-checker",
|
|
"mesh-delivery-forge-bridge", "mesh-delivery-gate", "mesh-delivery-indexer", "mesh-delivery-view"}
|
|
for _, m := range onKeys {
|
|
tier1 = append(tier1, m)
|
|
p.Modules[m] = &inventory.PlanModule{State: "asked", AskedAt: &sent, Key: true, Build: "build-" + m}
|
|
}
|
|
tier2 := []string{"gitea", "mesh-catalog", "mongodb"}
|
|
for _, m := range tier2 {
|
|
p.Modules[m] = &inventory.PlanModule{}
|
|
}
|
|
p.Tiers = [][]string{tier0, tier1, tier2}
|
|
|
|
if err := retryRefusal(p, []inventory.Plan{p}, ownPathOf(nil)); err != nil {
|
|
t.Fatalf("a walk stopped by hand with its tier waiting was refused a retry: %v", err)
|
|
}
|
|
said := resumeStopped(&p)
|
|
if p.State != inventory.PlanBuilding {
|
|
t.Errorf("resumed walk is %s, wanted building", p.State)
|
|
}
|
|
if p.Delivery.Stopped != "" || p.Delivery.StoppedWhy != "" || len(p.Delivery.Kept) > 0 {
|
|
t.Errorf("resumed walk still says it stopped: %+v", p.Delivery)
|
|
}
|
|
if !strings.Contains(p.Note, "resumed by hand") || !strings.Contains(p.Note, "a person (fix-1886@shanks)") {
|
|
t.Errorf("the note does not record the hand resume and the stop it undid: %q", p.Note)
|
|
}
|
|
for _, m := range onKeys {
|
|
s := p.Modules[m]
|
|
if s.State != "asked" || !s.Key || s.Build != "build-"+m {
|
|
t.Errorf("%s's ask was not kept for the look to hand on: %+v", m, s)
|
|
}
|
|
}
|
|
for _, w := range []string{"plan-r4e", "tier 2 of 3", "8 asked", "2 built and not sent", "0 not yet asked in it",
|
|
"3 not yet asked in the tiers after"} {
|
|
if !strings.Contains(said, w) {
|
|
t.Errorf("the answer %q does not say %q", said, w)
|
|
}
|
|
}
|
|
|
|
// What it still refuses: a walk stopped with nothing left in its tier, and one a related open walk runs beside.
|
|
done := p
|
|
done.State, done.Delivery = inventory.PlanFailed, &inventory.PlanDelivery{Stopped: "a person"}
|
|
done.Modules = map[string]*inventory.PlanModule{}
|
|
for _, tier := range done.Tiers {
|
|
for _, m := range tier {
|
|
done.Modules[m] = &inventory.PlanModule{State: "built", BuiltAt: &sent, SentAt: &sent}
|
|
}
|
|
}
|
|
if err := retryRefusal(done, nil, ownPathOf(nil)); err == nil || !strings.Contains(err.Error(), "nothing in tier 1") {
|
|
t.Errorf("a stopped walk with nothing left in its tier was retried: %v", err)
|
|
}
|
|
stopped := p
|
|
stopped.State, stopped.Delivery = inventory.PlanFailed, &inventory.PlanDelivery{Stopped: "a person"}
|
|
beside := inventory.Plan{ID: "plan-442", Repository: "novox/mesh-sdk", Branch: "release", Commit: "bbadbb2a",
|
|
Created: at, State: inventory.PlanBuilding, Tiers: [][]string{{"mesh-sdk"}},
|
|
Modules: map[string]*inventory.PlanModule{"mesh-sdk": {State: "asked"}}}
|
|
if err := retryRefusal(stopped, []inventory.Plan{beside}, ownPathOf(nil)); err == nil || !strings.Contains(err.Error(), "one walk at a time") {
|
|
t.Errorf("a stopped walk was resumed beside a related open walk: %v", err)
|
|
}
|
|
// A newer open plan of its repository and branch supersedes it: resuming would build what that one replaced.
|
|
newerOpen := inventory.Plan{ID: "plan-newer", Repository: "novox/mesh-sdk", Branch: "main", Commit: "d00dfeed",
|
|
Created: at, State: inventory.PlanBuilding, Tiers: [][]string{{"zsh"}},
|
|
Modules: map[string]*inventory.PlanModule{"zsh": {State: "asked"}}}
|
|
if err := retryRefusal(stopped, []inventory.Plan{stopped, newerOpen}, ownPathOf(nil)); err == nil ||
|
|
!strings.Contains(err.Error(), "plan-newer supersedes it") {
|
|
t.Errorf("a stopped walk was resumed under a newer open plan of its branch: %v", err)
|
|
}
|
|
// A newer plan holding a build of its tier not yet sent refuses it: resuming would send the older build over the newer.
|
|
newerBuilt := inventory.Plan{ID: "plan-agent", Repository: "novox/mesh-controller", Branch: "main", Commit: "d00dfeed",
|
|
Created: at, State: inventory.PlanDone, Tiers: [][]string{{"mesh-tools"}},
|
|
Modules: map[string]*inventory.PlanModule{"mesh-tools": {State: "built"}}}
|
|
if err := retryRefusal(stopped, []inventory.Plan{stopped, newerBuilt}, ownPathOf(nil)); err == nil ||
|
|
!strings.Contains(err.Error(), "older build") {
|
|
t.Errorf("a stopped walk was resumed over a newer plan of a build it has not sent: %v", err)
|
|
}
|
|
}
|
|
|
|
// A newer plan holding a module the stopped walk has not yet asked does not refuse its resume (review of issue 1886): that
|
|
// module is asked at its followed branch's head when the walk reaches it, so no older build goes over the newer. Open and
|
|
// related, the newer walk refuses it under one walk at a time; done, it does not. One holding a build the walk has not sent
|
|
// still refuses it.
|
|
func TestResumeIsNotRefusedByANewerPlanOfAModuleNotYetAsked(t *testing.T) {
|
|
at := time.Date(2026, 10, 23, 0, 1, 53, 0, time.UTC)
|
|
sent := at.Add(-time.Hour)
|
|
p := inventory.Plan{ID: "plan-r4e", Repository: "novox/mesh-sdk", Branch: "main", Commit: "7aacbc16",
|
|
Created: at.Add(-2 * time.Hour), State: inventory.PlanFailed, Tier: 1,
|
|
Delivery: &inventory.PlanDelivery{Stopped: "a person", StoppedWhy: "issue 1886"},
|
|
Tiers: [][]string{{"mesh-sdk"}, {"build-agent", "mesh-delivery-gate"}, {"mesh-delivery-builder", "gitea"}},
|
|
Modules: map[string]*inventory.PlanModule{
|
|
"mesh-sdk": {State: "built", BuiltAt: &sent, SentAt: &sent},
|
|
"build-agent": {State: "built", BuiltAt: &sent},
|
|
"mesh-delivery-gate": {State: "asked", AskedAt: &sent, Key: true},
|
|
"mesh-delivery-builder": {}, "gitea": {}}}
|
|
newer := func(state string, holds string) inventory.Plan {
|
|
return inventory.Plan{ID: "plan-1792715180015327197", Repository: "novox/mesh-delivery", Branch: "main",
|
|
Commit: "d00dfeed", Created: at, State: state, Tiers: [][]string{{holds}},
|
|
Modules: map[string]*inventory.PlanModule{holds: {State: "asked"}}}
|
|
}
|
|
if err := retryRefusal(p, []inventory.Plan{p, newer(inventory.PlanDone, "mesh-delivery-builder")}, ownPathOf(nil)); err != nil {
|
|
t.Errorf("a done newer plan of a module not yet asked refused the resume: %v", err)
|
|
}
|
|
if err := retryRefusal(p, []inventory.Plan{p, newer(inventory.PlanDone, "mesh-delivery-gate")}, ownPathOf(nil)); err != nil {
|
|
t.Errorf("a done newer plan of a module on the builder's key refused the resume: %v", err)
|
|
}
|
|
if err := retryRefusal(p, []inventory.Plan{p, newer(inventory.PlanBuilding, "mesh-delivery-builder")}, ownPathOf(nil)); err == nil ||
|
|
!strings.Contains(err.Error(), "one walk at a time") {
|
|
t.Errorf("an open newer walk related to it did not refuse the resume under one walk at a time: %v", err)
|
|
}
|
|
if err := retryRefusal(p, []inventory.Plan{p, newer(inventory.PlanDone, "build-agent")}, ownPathOf(nil)); err == nil ||
|
|
!strings.Contains(err.Error(), "older build") {
|
|
t.Errorf("a newer plan of a built module not yet sent did not refuse the resume: %v", err)
|
|
}
|
|
}
|