Retire the seat node-build-agent for one build seat per language, so a node's builds are pulled and paused at its build modules and a claim on the old seat names its replacement (issue 1887 A9a, ADR 0525 §2, §10)
This commit is contained in:
@@ -170,10 +170,10 @@ func TestAMembershipForTheNewBusIsComposedAsASealedFile(t *testing.T) {
|
||||
func TestAStoreRowWithoutAProtocolKeepsTheCompiledOne(t *testing.T) {
|
||||
was := Seats()
|
||||
t.Cleanup(func() { UseSeats(was) })
|
||||
UseSeats([]Seat{{Name: "mesh-build-machine", Scope: ScopeMesh, Decision: "row"}})
|
||||
got, ok := SeatNamed("mesh-build-machine")
|
||||
if !ok || len(got.Accepts) == 0 {
|
||||
t.Fatalf("the build machine's seat lost what it accepts when loaded from the store: %+v", got)
|
||||
UseSeats([]Seat{{Name: "node-build-go", Scope: ScopeNode, Decision: "row"}})
|
||||
got, ok := SeatNamed("node-build-go")
|
||||
if !ok || len(got.Accepts) == 0 || !got.PulledByKind {
|
||||
t.Fatalf("the Go build seat lost what it accepts when loaded from the store: %+v", got)
|
||||
}
|
||||
if got.Decision != "row" {
|
||||
t.Fatalf("the store's own columns were not kept: %+v", got)
|
||||
|
||||
+41
-38
@@ -45,6 +45,10 @@ type Seat struct {
|
||||
// today — which the bus refuses, because a namespace belongs to who it is named for.
|
||||
Accepts []string
|
||||
Emits []string
|
||||
// PulledByKind says each accepted kind is a worker of its own on the seat's stream, which its holders pull in the
|
||||
// order Accepts lists them (novox/hq ADR 0525 §2, ADR 0536 §4): the build seats'. Compiled, never stored, like
|
||||
// Receives.
|
||||
PulledByKind bool
|
||||
// Serves carries each verb in full — name, description, schema — because a role's tools are the
|
||||
// mesh's to define and an agent's to call (novox/hq ADR 0132, design 33 §2).
|
||||
Serves []Verb
|
||||
@@ -238,31 +242,19 @@ var defaultSeats = append([]Seat{
|
||||
// They keep their names until that migration is done deliberately, apart from the node-* pass.
|
||||
{Name: "npm-package-registry", Scope: ScopeMesh, Delivers: "npm-package-registry", Decision: "novox/hq ADR 0109"},
|
||||
{Name: "git", Scope: ScopeMesh, Delivers: "git", Decision: "novox/hq ADR 0111"},
|
||||
// A build is work submitted to this role and its outcome is the role's own event (ADR 0129).
|
||||
// One publish reaches whoever asked, the controller that records it, and the catalogue that
|
||||
// places it in the graph — what the old bus's shared exchange did for free.
|
||||
// A build says what it does as it does it (novox/hq ADR 0157): `started` when work is taken,
|
||||
// `log.<build id>` for every line, `built` for the outcome. The log's tail token is the build's
|
||||
// id, so a reader follows one build by subject alone.
|
||||
// **Node-scoped, and every holder takes from one queue** (novox/hq ADR 0190): a build is asked of
|
||||
// the role, and whichever machine holding the seat is idle pulls it. One holder per machine is
|
||||
// what the scope says; sharing the work is what a seat's queue has always done.
|
||||
//
|
||||
// **And its holder answers for the build it is running** (novox/hq ADR 0219): what it is
|
||||
// building, kill it, take nothing new, take again. The queue as a whole is the controller's to
|
||||
// show and change (`queue`, `cancel`, `clear`); what one machine does with an ask it already
|
||||
// took only that machine can do. `paused.<node>` is each holder saying whether it takes work, so
|
||||
// the controller can tell a plan waiting on a paused seat from one that is late.
|
||||
{Name: "node-build-agent", Scope: ScopeNode,
|
||||
Accepts: []string{"build"}, Emits: []string{"started", "built", "log.*", "paused.*"},
|
||||
Serves: buildAgentVerbs(),
|
||||
Decision: "novox/hq ADR 0190, ADR 0219"},
|
||||
// **Retired by ADR 0190, kept while a manifest still claims it.** The one build machine's seat.
|
||||
// A claim to a seat the mesh no longer defines is refused, and the module holding this one is
|
||||
// assigned on a live machine until build-agent replaces it — removing the row first would make
|
||||
// that machine unresolvable in the meantime. Deleted once no registered manifest claims it.
|
||||
{Name: "mesh-build-machine", Scope: ScopeMesh,
|
||||
Accepts: []string{"build"}, Emits: []string{"started", "built", "log.*"}, Decision: "novox/hq ADR 0190"},
|
||||
// **A build node's work, one seat per build language** (novox/hq ADR 0525 §2, §10; ADR 0536 §4; issue 1887). The
|
||||
// build module of each language claims its seat on every node it runs on; the builder and the checker put their
|
||||
// asks on the seat's accepted kinds, which the broker derives as one work queue with a shared worker per kind, and
|
||||
// each holder pulls delivery first, then check, then recheck, at most its node's build capacity at once. Its verbs
|
||||
// say what the node runs and pause or resume it there: every holder pulls from the same worker, so a node's pause is
|
||||
// its holder taking nothing, kept on the module's own pause key (issue 1887's design limit 2). They replace
|
||||
// node-build-agent, retired with build-agent (ADR 0524 §5).
|
||||
{Name: "node-build-go", Scope: ScopeNode, Accepts: buildAskKinds(), PulledByKind: true, Serves: buildSeatVerbs("Go"),
|
||||
Decision: "novox/hq ADR 0525, ADR 0536"},
|
||||
{Name: "node-build-node", Scope: ScopeNode, Accepts: buildAskKinds(), PulledByKind: true, Serves: buildSeatVerbs("Node"),
|
||||
Decision: "novox/hq ADR 0525, ADR 0536"},
|
||||
{Name: "node-build-image", Scope: ScopeNode, Accepts: buildAskKinds(), PulledByKind: true, Serves: buildSeatVerbs("image"),
|
||||
Decision: "novox/hq ADR 0525, ADR 0536"},
|
||||
// **The mesh's resolvers** (novox/hq ADR 0194, 0196, 0223): every node's internal domain, and
|
||||
// every node and container asks them and nothing else. Replicated since ADR 0223: held on more
|
||||
// than one machine, each answering the same names from the same roster, and every machine's
|
||||
@@ -498,6 +490,7 @@ func UseSeats(s []Seat) {
|
||||
row.Receives = d.Receives
|
||||
row.Paths = d.Paths
|
||||
row.Replicated = d.Replicated
|
||||
row.PulledByKind = d.PulledByKind
|
||||
row.Serves = optionalAsCompiled(row.Serves, d.Serves)
|
||||
}
|
||||
merged = append(merged, row)
|
||||
@@ -623,6 +616,10 @@ func claimProblems(m Manifest) []string {
|
||||
// after UseSeats has replaced the set with the store's.
|
||||
continue
|
||||
}
|
||||
if replaced, retired := RetiredSeats[c.Name]; retired {
|
||||
problems = append(problems, fmt.Sprintf("%s claims the seat %q, which is retired: its work is %s", m.Module, c.Name, replaced))
|
||||
continue
|
||||
}
|
||||
if isSystemSeatName(c.Name) {
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%s claims %q, which is a seat in the mesh's own namespace (mesh-*/node-*) that it "+
|
||||
@@ -903,22 +900,28 @@ func loginShellVerbs() []Verb {
|
||||
}
|
||||
}
|
||||
|
||||
// buildAgentVerbs are what a holder of node-build-agent answers on its own machine (novox/hq ADR
|
||||
// 0219). One build at a time per holder (ADR 0190), so "the build running here" is one or none.
|
||||
func buildAgentVerbs() []Verb {
|
||||
// buildAskKinds are a build seat's accepted kinds, in the order a holder pulls them (ADR 0536 §4; mesh-sdk
|
||||
// delivery.AskKinds).
|
||||
func buildAskKinds() []string { return []string{"delivery", "check", "recheck"} }
|
||||
|
||||
// RetiredSeats are seats the mesh no longer defines, each with what replaced it: a claim or a use of one is refused
|
||||
// naming its replacement, not as a seat nobody declares.
|
||||
var RetiredSeats = map[string]string{
|
||||
"node-build-agent": "node-build-go, node-build-node and node-build-image, held by the build modules (novox/hq ADR 0525 §10)",
|
||||
"mesh-build-machine": "node-build-go, node-build-node and node-build-image, held by the build modules (novox/hq ADR 0190, ADR 0525 §10)",
|
||||
}
|
||||
|
||||
// buildSeatVerbs are what a build module answers on its own node for its seat (ADR 0525 §10).
|
||||
func buildSeatVerbs(language string) []Verb {
|
||||
return []Verb{
|
||||
{Name: "current", Description: "The build this machine is running — its id, repository, path, " +
|
||||
"the step it is at, when it started and for how long — or none; and whether this machine is " +
|
||||
"paused, taking no new build.",
|
||||
{Name: "current", Description: "The " + language + " asks this node runs now (each ask's id, kind, module and when it " +
|
||||
"started), its build capacity, and whether it is paused, taking no new ask.",
|
||||
Input: schema(map[string]string{}, nil)},
|
||||
{Name: "kill", Description: "End the build with this id, running here: its commands and the " +
|
||||
"containers it started are stopped, and its outcome is announced as failed, killed by hand — " +
|
||||
"settled, so it is not handed to another machine.",
|
||||
Input: schema(map[string]string{"id": "the build's id, as `queue` or `current` says it"}, []string{"id"})},
|
||||
{Name: "pause", Description: "Take no new build on this machine until resumed; a build running " +
|
||||
"here finishes. Kept across a restart of the holder.",
|
||||
{Name: "pause", Description: "Take no new " + language + " ask on this node until resumed; an ask running here " +
|
||||
"ends in its place. Kept across a restart of the build module (its pause key). A capacity of 0 is the " +
|
||||
"lasting way to keep a node from building.",
|
||||
Input: schema(map[string]string{}, nil)},
|
||||
{Name: "resume", Description: "Take builds again on this machine.",
|
||||
{Name: "resume", Description: "Take " + language + " asks again on this node.",
|
||||
Input: schema(map[string]string{}, nil)},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,6 +364,10 @@ func CatalogueProblems(shelf Shelf) []string {
|
||||
// A `uses` naming nothing is where ADR 0110's guarantee lands under a derived set: the
|
||||
// same refusal, at the same moment, from a set nobody maintains by hand.
|
||||
for _, u := range m.Uses {
|
||||
if replaced, retired := RetiredSeats[u]; retired && !exists(u) {
|
||||
problems = append(problems, fmt.Sprintf("%s uses the seat %q, which is retired: its work is %s", module, u, replaced))
|
||||
continue
|
||||
}
|
||||
if !exists(u) {
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%s uses the seat %q, "+UndeclaredSeat,
|
||||
@@ -372,6 +376,10 @@ func CatalogueProblems(shelf Shelf) []string {
|
||||
}
|
||||
|
||||
for _, c := range m.Claims {
|
||||
if replaced, retired := RetiredSeats[c.Name]; retired && !exists(c.Name) {
|
||||
problems = append(problems, fmt.Sprintf("%s claims the seat %q, which is retired: its work is %s", module, c.Name, replaced))
|
||||
continue
|
||||
}
|
||||
if !exists(c.Name) {
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%s claims the seat %q, "+UndeclaredSeat,
|
||||
|
||||
@@ -43,7 +43,8 @@ func TestTheSeatsAreAClosedSetAndEachNamesItsDecision(t *testing.T) {
|
||||
delivered[s.Delivers] = s.Name
|
||||
}
|
||||
}
|
||||
// Forty-five with node-files (novox/hq ADR 0394); forty-four again once node-reads goes.
|
||||
// Forty-six with the three build seats replacing node-build-agent and the retired mesh-build-machine (novox/hq
|
||||
// ADR 0525 §2, §10); forty-five with node-files (novox/hq ADR 0394); forty-four again once node-reads goes.
|
||||
// Forty-four with node-provisioner (novox/hq ADR 0349 clause 3); forty-three with mesh-provisioner (novox/hq ADR 0349 clause 3); forty-two with mesh-config (novox/hq ADR 0348 §9); forty-one with node-reads (novox/hq ADR 0310); forty with node-nfs-server and node-mounts (novox/hq ADR 0263); thirty-eight with node-resolver (novox/hq ADR 0247); thirty-seven with mesh-delivery (novox/hq ADR 0239); thirty-six since node-resolver-config retired
|
||||
// into node-uplink (novox/hq ADR 0223); thirty-seven
|
||||
// since the retired node-dns-resolver went (novox/hq ADR 0220); thirty-eight with
|
||||
@@ -55,8 +56,8 @@ func TestTheSeatsAreAClosedSetAndEachNamesItsDecision(t *testing.T) {
|
||||
// node-container-runtime (ADR 0207); nineteen with node-environment and node-login-shell (ADR 0203,
|
||||
// ADR 0204); seventeen with node-build-agent (ADR 0190). One fewer once the retired
|
||||
// mesh-build-machine row goes, when no registered manifest claims it.
|
||||
if len(Seats()) != 45 {
|
||||
t.Errorf("the mesh defines %d seats rather than 45; the set is closed, so a change here is "+
|
||||
if len(Seats()) != 46 {
|
||||
t.Errorf("the mesh defines %d seats rather than 46; the set is closed, so a change here is "+
|
||||
"a decision (novox/hq ADR 0110): %s", len(Seats()), seatNames())
|
||||
}
|
||||
}
|
||||
@@ -300,3 +301,35 @@ func TestAFormerNameResolvesAfterARename(t *testing.T) {
|
||||
t.Fatalf("the holder recorded under the former name was not matched: %+v found=%v", holder, found)
|
||||
}
|
||||
}
|
||||
|
||||
// 1887/A9a: the catalogue defines no seat node-build-agent, and a manifest claiming it is refused naming its
|
||||
// replacement; the build modules' seats accept the three kinds in their pull order and serve current, pause, resume.
|
||||
func TestNodeBuildAgentIsRetiredForTheBuildSeats(t *testing.T) {
|
||||
for _, gone := range []string{"node-build-agent", "mesh-build-machine"} {
|
||||
if _, ok := SeatNamed(gone); ok {
|
||||
t.Errorf("the mesh still defines %s", gone)
|
||||
}
|
||||
}
|
||||
_, err := ParseManifest([]byte(`{"module":"build-agent","version":"1","claims":[{"name":"node-build-agent","scope":"node"}]}`))
|
||||
if err == nil || !strings.Contains(err.Error(), `"node-build-agent", which is retired`) || !strings.Contains(err.Error(), "node-build-go") {
|
||||
t.Fatalf("the claim is not refused naming its replacement: %v", err)
|
||||
}
|
||||
// And over a catalogue whose store still holds no row for it, a use of it is refused the same way.
|
||||
user := Manifest{Module: "watcher", Uses: []string{"node-build-agent"}}
|
||||
if joined := strings.Join(CatalogueProblems(Shelf{user.Module: user}), "; "); !strings.Contains(joined, "which is retired: its work is node-build-go") {
|
||||
t.Fatalf("a use is not refused naming the replacement: %s", joined)
|
||||
}
|
||||
for _, name := range []string{"node-build-go", "node-build-node", "node-build-image"} {
|
||||
s, ok := SeatNamed(name)
|
||||
if !ok || s.Scope != ScopeNode || !s.PulledByKind || strings.Join(s.Accepts, ",") != "delivery,check,recheck" {
|
||||
t.Fatalf("%s = %+v", name, s)
|
||||
}
|
||||
var verbs []string
|
||||
for _, v := range s.Serves {
|
||||
verbs = append(verbs, v.Name)
|
||||
}
|
||||
if strings.Join(verbs, ",") != "current,pause,resume" {
|
||||
t.Errorf("%s serves %v", name, verbs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user