Seats are data the controller owns, loaded from its store (ADR 0122, phase 1)
The seat set was a Go slice compiled into the controller and referenced by name everywhere, so changing it meant a rebuild and a freeze-prone deploy. It is now a table: catalogue keeps the shipped set as defaultSeats (the seed and the fallback) and a loadable working set; inventory adds the seat table (migration 0034), Seats to read it, and SeedSeats to fill it idempotently without overwriting an operator's edit; migrate seeds it; openInventory loads it, and an empty or unreadable table leaves the compiled defaults in force so it can never brick the control plane's boot. Behaviour-neutral: the seeded table equals the defaults. Phase 2 (reference by a stable id so a rename touches no manifest or code, and the builder reads the set from the mesh) follows.
This commit is contained in:
@@ -31,8 +31,12 @@ type Seat struct {
|
||||
Decision string
|
||||
}
|
||||
|
||||
// seats is the whole set, in the order a person reads it: the mesh's own, then a node's.
|
||||
var seats = []Seat{
|
||||
// defaultSeats is the set the mesh ships with — the seed for the control plane's seat table and the
|
||||
// fallback when it has none (novox/hq ADR 0122). It is the one place the closed set 0110 defines is
|
||||
// written; the store's table is seeded from it and thereafter is the live, editable copy.
|
||||
//
|
||||
// In the order a person reads it: the mesh's own, then a node's.
|
||||
var defaultSeats = []Seat{
|
||||
{Name: "mesh-controller", Scope: ScopeMesh, Decision: "novox/hq ADR 0079"},
|
||||
{Name: "mesh-store", Scope: ScopeMesh, Delivers: "postgres-database", Decision: "novox/hq ADR 0079"},
|
||||
{Name: "mesh-broker", Scope: ScopeMesh, Delivers: "amqp", Decision: "novox/hq ADR 0079"},
|
||||
@@ -69,6 +73,27 @@ func isSystemSeatName(name string) bool {
|
||||
return strings.HasPrefix(name, "mesh-") || strings.HasPrefix(name, "node-")
|
||||
}
|
||||
|
||||
// seats is the working set the lookups read. It starts as the compiled defaults and is replaced by
|
||||
// what the control plane loaded from its store (novox/hq ADR 0122), so a change to the set is a
|
||||
// change to data, not to this code.
|
||||
var seats = defaultSeats
|
||||
|
||||
// DefaultSeats is the set the mesh ships with, for seeding the store's seat table.
|
||||
func DefaultSeats() []Seat { return append([]Seat(nil), defaultSeats...) }
|
||||
|
||||
// UseSeats replaces the working set with the one the control plane read from its store.
|
||||
//
|
||||
// **Empty is ignored on purpose.** A store that has not been seeded yet — or one that could not be
|
||||
// read — must leave the compiled defaults in force rather than emptying the set: an empty set would
|
||||
// refuse every claim and could stop the control plane composing at all, which is a far worse failure
|
||||
// than running on the set the binary shipped with. So the store can only ever *replace* the set with
|
||||
// a non-empty one, never erase it.
|
||||
func UseSeats(s []Seat) {
|
||||
if len(s) > 0 {
|
||||
seats = s
|
||||
}
|
||||
}
|
||||
|
||||
// Seats is every seat the mesh defines, in reading order.
|
||||
func Seats() []Seat {
|
||||
return append([]Seat(nil), seats...)
|
||||
|
||||
@@ -246,3 +246,23 @@ func TestTheHolderIsTheModuleNotTheMachine(t *testing.T) {
|
||||
t.Fatalf("the holder was not told apart from a neighbour: %+v", holder)
|
||||
}
|
||||
}
|
||||
|
||||
// The working set is loaded from the store, and an empty load never erases it (novox/hq ADR 0122).
|
||||
func TestUseSeatsReplacesTheSetButNeverEmptiesIt(t *testing.T) {
|
||||
before := Seats()
|
||||
defer UseSeats(DefaultSeats()) // restore for other tests, whatever this leaves it as
|
||||
|
||||
// An empty load (store not seeded, or unreadable) leaves the compiled defaults in force.
|
||||
UseSeats(nil)
|
||||
if len(Seats()) != len(before) {
|
||||
t.Fatalf("an empty load changed the set from %d to %d seats", len(before), len(Seats()))
|
||||
}
|
||||
// A non-empty load replaces it — this is how a rename in the store reaches the lookups.
|
||||
UseSeats([]Seat{{Name: "node-firewall", Scope: ScopeNode, Decision: "novox/hq ADR 0122"}})
|
||||
if _, known := SeatNamed("node-firewall"); !known {
|
||||
t.Fatal("the loaded set did not replace the working set")
|
||||
}
|
||||
if len(Seats()) != 1 {
|
||||
t.Fatalf("the working set is %d seats, not the one that was loaded", len(Seats()))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user