Issues 203 and 206: an assignment issues its credential; the controller owns a worker's shape; the build seat's holder follows the controller #233
@@ -36,17 +36,29 @@ func tiersOf(set []string, edges []inventory.Edge) [][]string {
|
||||
for _, m := range set {
|
||||
deps[m] = map[string]bool{}
|
||||
}
|
||||
// The build seat's holders follow the controller that defines their worker (EdgeWorkerOf,
|
||||
// novox/hq issue 206), so the built-by edge from that controller to such a holder yields: the
|
||||
// controller is built by whichever build machine is running, as the runtime image always was.
|
||||
worker := map[string]map[string]bool{}
|
||||
for _, e := range edges {
|
||||
if e.Kind == inventory.EdgeWorkerOf && in[e.From] && in[e.To] {
|
||||
if worker[e.To] == nil {
|
||||
worker[e.To] = map[string]bool{}
|
||||
}
|
||||
worker[e.To][e.From] = true
|
||||
}
|
||||
}
|
||||
for _, e := range edges {
|
||||
// A code dependency — B packages A's source — rebuilds B with A, in the same tier: B's
|
||||
// build needs nothing of A's first. The other kinds order: stands-on and declared after
|
||||
// the base is built, built-by after the build machine is built and running — except for
|
||||
// what the build machine itself stands on. The runtime image is built by the builder and
|
||||
// the builder is built on the runtime image; the image comes first, built by the builder
|
||||
// that is running, which is the only one there could be.
|
||||
// what the build machine itself stands on, and for the controller whose worker the build
|
||||
// machine binds. The runtime image is built by the builder and the builder is built on the
|
||||
// runtime image; the image comes first, built by the builder that is running.
|
||||
if !in[e.From] || !in[e.To] || e.From == e.To || e.Kind == inventory.EdgePackages {
|
||||
continue
|
||||
}
|
||||
if e.Kind == inventory.EdgeBuiltBy && isBaseOf(e.From, e.To, edges, in) {
|
||||
if e.Kind == inventory.EdgeBuiltBy && (isBaseOf(e.From, e.To, edges, in) || worker[e.From][e.To]) {
|
||||
continue
|
||||
}
|
||||
deps[e.From][e.To] = true
|
||||
@@ -122,7 +134,10 @@ func reachableFrom(moved []string, edges []inventory.Edge) []string {
|
||||
for grew := true; grew; {
|
||||
grew = false
|
||||
for _, e := range edges {
|
||||
if e.Kind == inventory.EdgeBuiltBy {
|
||||
// Built-by and worker-of order a plan; neither widens it. A new build machine changes
|
||||
// nothing it builds, and a new controller changes nothing about the holder it orders —
|
||||
// what packages the controller's source is already a code edge.
|
||||
if e.Kind == inventory.EdgeBuiltBy || e.Kind == inventory.EdgeWorkerOf {
|
||||
continue
|
||||
}
|
||||
if in[e.To] && !in[e.From] {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/novox/mesh-controller/internal/inventory"
|
||||
)
|
||||
|
||||
// The holder of the build seat follows the controller that defines its worker (novox/hq issue 206).
|
||||
// On 2026-10-03 a plan put the build machine in tier 0 and the controller in tier 1; the new build
|
||||
// machine could not bind the worker the old controller had defined, and nothing could build the
|
||||
// controller that would have redefined it. The built-by edge from the controller to its build
|
||||
// machine yields to that order: the controller is built by whichever build machine is running.
|
||||
func TestTheBuildSeatsHolderFollowsTheControllerThatDefinesItsWorker(t *testing.T) {
|
||||
edges := []inventory.Edge{
|
||||
{From: "build-agent", To: "mesh-controller", Kind: inventory.EdgePackages},
|
||||
{From: "build-agent", To: "mesh-controller", Kind: inventory.EdgeWorkerOf},
|
||||
{From: "mesh-controller", To: "build-agent", Kind: inventory.EdgeBuiltBy},
|
||||
{From: "route-proxy", To: "mesh-controller", Kind: inventory.EdgePackages},
|
||||
{From: "route-proxy", To: "build-agent", Kind: inventory.EdgeBuiltBy},
|
||||
}
|
||||
set := reachableFrom([]string{"mesh-controller"}, edges)
|
||||
if len(set) != 3 {
|
||||
t.Fatalf("the controller, what packages it, and nothing more: %v", set)
|
||||
}
|
||||
tiers := tiersOf(set, edges)
|
||||
pos := map[string]int{}
|
||||
for i, tier := range tiers {
|
||||
for _, m := range tier {
|
||||
pos[m] = i
|
||||
}
|
||||
}
|
||||
if pos["mesh-controller"] != 0 {
|
||||
t.Fatalf("the controller first, built by the build machine that is running: %v", tiers)
|
||||
}
|
||||
if pos["build-agent"] <= pos["mesh-controller"] {
|
||||
t.Fatalf("the build machine after the controller that defines its worker: %v", tiers)
|
||||
}
|
||||
if pos["route-proxy"] <= pos["build-agent"] {
|
||||
t.Fatalf("what the build machine builds comes after it: %v", tiers)
|
||||
}
|
||||
if hasCycle(tiers, edges) {
|
||||
t.Fatalf("no cycle here: %v", tiers)
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,17 @@ const (
|
||||
EdgeBuiltBy = "built-by"
|
||||
// EdgeDeclared: the manifest's own `build.on`.
|
||||
EdgeDeclared = "declared"
|
||||
// EdgeWorkerOf: the module holds the build seat, whose worker the control plane defines
|
||||
// (novox/hq issue 206). The one place a *running* order enters the graph: a build machine rolled
|
||||
// before the controller that redefines its worker cannot bind it, and nothing can then build the
|
||||
// controller that would end that — so the holder of the build seat follows the controller, and
|
||||
// the controller is built by whichever build machine is running, as it always was.
|
||||
EdgeWorkerOf = "worker-of"
|
||||
)
|
||||
|
||||
// TheControlPlane is the module that defines every seat's worker on the bus.
|
||||
const TheControlPlane = "mesh-controller"
|
||||
|
||||
// Edge is one dependency: From depends on To, in the way Kind says.
|
||||
type Edge struct {
|
||||
From string `json:"from"`
|
||||
@@ -106,6 +115,13 @@ func dependenciesOf(entries []Entry, against map[string][]string, read map[strin
|
||||
}
|
||||
}
|
||||
}
|
||||
if known[TheControlPlane] {
|
||||
for _, b := range builders {
|
||||
if b != TheControlPlane {
|
||||
add(b, TheControlPlane, EdgeWorkerOf)
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Slice(out, func(a, b int) bool {
|
||||
if out[a].From != out[b].From {
|
||||
return out[a].From < out[b].From
|
||||
|
||||
Reference in New Issue
Block a user