diff --git a/format/seat-definition.schema.json b/format/seat-definition.schema.json index 9dbc5ce..46a1557 100644 --- a/format/seat-definition.schema.json +++ b/format/seat-definition.schema.json @@ -39,7 +39,6 @@ "Module", "Config", "Core", - "Placement", "Provisioning", "Identity and access", "Delivery", diff --git a/placement/go/clients/meshmoduleassignerclient/client_test.go b/go/clients/meshmoduleassignerclient/client_test.go similarity index 97% rename from placement/go/clients/meshmoduleassignerclient/client_test.go rename to go/clients/meshmoduleassignerclient/client_test.go index 06453e0..8248650 100644 --- a/placement/go/clients/meshmoduleassignerclient/client_test.go +++ b/go/clients/meshmoduleassignerclient/client_test.go @@ -7,7 +7,7 @@ import ( "strings" "testing" - seat "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + seat "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-sdk/go/catalogue" ) @@ -66,7 +66,7 @@ func TestPlacementClient(t *testing.T) { Holds: []seat.Holding{{Claim: "node-shell", Scope: "node", Module: "zsh"}}, } novox := seat.Placement{Node: "novox", Decided: seat.NodeDecided{Hub: true, Address: "10.77.0.1"}, Modules: []seat.PlacedModule{{Module: "postgres", Ports: map[string]int64{"5432": 5432}}}, - Holds: []seat.Holding{{Claim: "mesh-store", Scope: "mesh", Module: "postgres"}}} + Holds: []seat.Holding{{Claim: "mesh-example", Scope: "mesh", Module: "postgres"}}} t.Run("value", func(t *testing.T) { raw, err := EncodePlacement(g14) @@ -230,7 +230,7 @@ func TestPlacementClient(t *testing.T) { t.Errorf("an unchanged placement writes %+v", again) } // Keys that do not agree keep what the node had, and say so. - stray, _ := EncodeModule(seat.PlacementByNodeModuleValue{Node: "g14", Placed: seat.PlacedModule{Module: "app"}}) + stray, _ := EncodeModule(seat.AssignmentsByNodeModuleValue{Node: "g14", Placed: seat.PlacedModule{Module: "app"}}) if _, err := keys.Put("g14.app", stray, 99, false); err == nil { t.Error("a module key the node key does not name the value of was taken silently") } diff --git a/go/clients/meshmoduleassignerclient/go.mod b/go/clients/meshmoduleassignerclient/go.mod new file mode 100644 index 0000000..5ba273a --- /dev/null +++ b/go/clients/meshmoduleassignerclient/go.mod @@ -0,0 +1,8 @@ +module git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient + +go 1.26.0 + +require ( + git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-sdk/go v0.0.0-00010101000000-000000000000 +) diff --git a/placement/go/clients/meshmoduleassignerclient/holders.go b/go/clients/meshmoduleassignerclient/holders.go similarity index 94% rename from placement/go/clients/meshmoduleassignerclient/holders.go rename to go/clients/meshmoduleassignerclient/holders.go index 763d2be..8e0f845 100644 --- a/placement/go/clients/meshmoduleassignerclient/holders.go +++ b/go/clients/meshmoduleassignerclient/holders.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - seat "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + seat "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" ) // SeatHoldersKey is a seat's key in the state `seat-holders`: `` for a seat held once for the mesh (node empty), diff --git a/placement/go/clients/meshmoduleassignerclient/holders_test.go b/go/clients/meshmoduleassignerclient/holders_test.go similarity index 81% rename from placement/go/clients/meshmoduleassignerclient/holders_test.go rename to go/clients/meshmoduleassignerclient/holders_test.go index 88d520c..6d51a54 100644 --- a/placement/go/clients/meshmoduleassignerclient/holders_test.go +++ b/go/clients/meshmoduleassignerclient/holders_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - seat "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + seat "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" ) // The digest a node key names is kept as the first writer made it, so a node key written before this client reads. @@ -18,14 +18,14 @@ func TestDigestIsTheFirstWritersAndHoldersAndStandingRead(t *testing.T) { keys := map[string][]byte{ "mesh-module-assigner": []byte(`{"v":1,"seat":"mesh-module-assigner","scope":"mesh","holders":[{"module":"mesh-delivery-assigner","node":"novox"}]}`), - "node-files.ace": []byte(`{"v":1,"seat":"node-files","scope":"node","node":"ace","holders":[{"module":"node-files","node":"ace"}]}`), + "node-example.ace": []byte(`{"v":1,"seat":"node-example","scope":"node","node":"ace","holders":[{"module":"node-example","node":"ace"}]}`), "mesh-vault": []byte(`{"v":2,"seat":"mesh-vault","scope":"mesh","holders":[]}`), } for _, c := range []struct { seat, node string want int bad bool - }{{"mesh-module-assigner", "", 1, false}, {"node-files", "ace", 1, false}, {"node-files", "g14", 0, false}, {"mesh-vault", "", 0, true}} { + }{{"mesh-module-assigner", "", 1, false}, {"node-example", "ace", 1, false}, {"node-example", "g14", 0, false}, {"mesh-vault", "", 0, true}} { got, err := HoldersOf(keys, c.seat, c.node) if (err != nil) != c.bad || len(got) != c.want { t.Errorf("HoldersOf(%s, %q) = %v, %v", c.seat, c.node, got, err) @@ -37,9 +37,9 @@ func TestDigestIsTheFirstWritersAndHoldersAndStandingRead(t *testing.T) { p := NewPlaced() t0 := time.Date(2026, 11, 5, 12, 0, 0, 0, time.UTC) node := func(modules ...string) []byte { - n := seat.PlacementByNodeNodeValue{Node: "ace"} + n := seat.AssignmentsByNodeNodeValue{Node: "ace"} for _, name := range modules { - n.Modules = append(n.Modules, seat.PlacementByNodeNodeValueModulesItem{Module: name, Digest: "d"}) + n.Modules = append(n.Modules, seat.AssignmentsByNodeNodeValueModulesItem{Module: name, Digest: "d"}) } raw, err := EncodeNode(n) if err != nil { diff --git a/placement/go/clients/meshmoduleassignerclient/keys.go b/go/clients/meshmoduleassignerclient/keys.go similarity index 91% rename from placement/go/clients/meshmoduleassignerclient/keys.go rename to go/clients/meshmoduleassignerclient/keys.go index 27822a8..ac2dfe1 100644 --- a/placement/go/clients/meshmoduleassignerclient/keys.go +++ b/go/clients/meshmoduleassignerclient/keys.go @@ -8,7 +8,7 @@ import ( "strings" "sync" - seat "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + seat "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" ) // **One key per module and node** (research 072 row 37; issue 774). The seat's state `placement` keeps a node's placement @@ -104,7 +104,7 @@ func checkPlacementModule(module string) error { } // EncodeNode is n as JSON at Version. -func EncodeNode(n seat.PlacementByNodeNodeValue) ([]byte, error) { +func EncodeNode(n seat.AssignmentsByNodeNodeValue) ([]byte, error) { if err := checkPlacementNode(n.Node); err != nil { return nil, err } @@ -114,24 +114,24 @@ func EncodeNode(n seat.PlacementByNodeNodeValue) ([]byte, error) { // DecodeNode reads a node key's value; it refuses an unknown version, a node or module that is not a name, and a module // named twice or without a digest. -func DecodeNode(data []byte) (seat.PlacementByNodeNodeValue, error) { +func DecodeNode(data []byte) (seat.AssignmentsByNodeNodeValue, error) { if err := checkVersion(data); err != nil { - return seat.PlacementByNodeNodeValue{}, err + return seat.AssignmentsByNodeNodeValue{}, err } - n, err := seat.DecodePlacementByNodeNodeValue(data) + n, err := seat.DecodeAssignmentsByNodeNodeValue(data) if err != nil { return n, fmt.Errorf("meshmoduleassignerclient: value does not fit its shape: %w", err) } if err := checkPlacementNode(n.Node); err != nil { - return seat.PlacementByNodeNodeValue{}, err + return seat.AssignmentsByNodeNodeValue{}, err } seen := map[string]bool{} for _, r := range n.Modules { if err := checkPlacementModule(r.Module); err != nil { - return seat.PlacementByNodeNodeValue{}, err + return seat.AssignmentsByNodeNodeValue{}, err } if seen[r.Module] || r.Digest == "" { - return seat.PlacementByNodeNodeValue{}, fmt.Errorf("meshmoduleassignerclient: the node key of %s names %s twice or without a digest", n.Node, r.Module) + return seat.AssignmentsByNodeNodeValue{}, fmt.Errorf("meshmoduleassignerclient: the node key of %s names %s twice or without a digest", n.Node, r.Module) } seen[r.Module] = true } @@ -139,7 +139,7 @@ func DecodeNode(data []byte) (seat.PlacementByNodeNodeValue, error) { } // EncodeModule is m as JSON at Version. -func EncodeModule(m seat.PlacementByNodeModuleValue) ([]byte, error) { +func EncodeModule(m seat.AssignmentsByNodeModuleValue) ([]byte, error) { if err := checkPlacementNode(m.Node); err != nil { return nil, err } @@ -154,19 +154,19 @@ func EncodeModule(m seat.PlacementByNodeModuleValue) ([]byte, error) { } // DecodeModule reads a module key's value. -func DecodeModule(data []byte) (seat.PlacementByNodeModuleValue, error) { +func DecodeModule(data []byte) (seat.AssignmentsByNodeModuleValue, error) { if err := checkVersion(data); err != nil { - return seat.PlacementByNodeModuleValue{}, err + return seat.AssignmentsByNodeModuleValue{}, err } - m, err := seat.DecodePlacementByNodeModuleValue(data) + m, err := seat.DecodeAssignmentsByNodeModuleValue(data) if err != nil { return m, fmt.Errorf("meshmoduleassignerclient: value does not fit its shape: %w", err) } if err := checkPlacementNode(m.Node); err != nil { - return seat.PlacementByNodeModuleValue{}, err + return seat.AssignmentsByNodeModuleValue{}, err } if err := checkPlacementModule(m.Placed.Module); err != nil { - return seat.PlacementByNodeModuleValue{}, err + return seat.AssignmentsByNodeModuleValue{}, err } return m, nil } @@ -185,9 +185,9 @@ type Keys struct { // keysOf is what is held of one node's keys. type keysOf struct { - node *seat.PlacementByNodeNodeValue + node *seat.AssignmentsByNodeNodeValue nodeRev uint64 - modules map[string]seat.PlacementByNodeModuleValue + modules map[string]seat.AssignmentsByNodeModuleValue moduleRevs map[string]uint64 legacy *seat.Placement legacyRev uint64 @@ -245,7 +245,7 @@ func (k *Keys) put(key string, value []byte, revision uint64, deleted bool) (nod defer k.mu.Unlock() of := k.nodes[node] if of == nil { - of = &keysOf{modules: map[string]seat.PlacementByNodeModuleValue{}, moduleRevs: map[string]uint64{}} + of = &keysOf{modules: map[string]seat.AssignmentsByNodeModuleValue{}, moduleRevs: map[string]uint64{}} k.nodes[node] = of } switch kind { @@ -405,7 +405,7 @@ func (k *Keys) Writes(node string, after *seat.Placement) ([]KeyWrite, error) { defer k.mu.Unlock() of := k.nodes[node] if of == nil { - of = &keysOf{modules: map[string]seat.PlacementByNodeModuleValue{}, moduleRevs: map[string]uint64{}} + of = &keysOf{modules: map[string]seat.AssignmentsByNodeModuleValue{}, moduleRevs: map[string]uint64{}} } var out []KeyWrite deleteModules := func(keep map[string]bool) { @@ -440,21 +440,21 @@ func (k *Keys) Writes(node string, after *seat.Placement) ([]KeyWrite, error) { committed[m.Module] = m } } - n := seat.PlacementByNodeNodeValue{Node: node, Decided: after.Decided, Pins: after.Pins, Holds: after.Holds, Modules: make([]seat.PlacementByNodeNodeValueModulesItem, 0, len(after.Modules))} + n := seat.AssignmentsByNodeNodeValue{Node: node, Decided: after.Decided, Pins: after.Pins, Holds: after.Holds, Modules: make([]seat.AssignmentsByNodeNodeValueModulesItem, 0, len(after.Modules))} keep := map[string]bool{} for _, m := range after.Modules { if err := checkPlacementModule(m.Module); err != nil { return nil, err } digest := Digest(m) - n.Modules = append(n.Modules, seat.PlacementByNodeNodeValueModulesItem{Module: m.Module, Digest: digest}) + n.Modules = append(n.Modules, seat.AssignmentsByNodeNodeValueModulesItem{Module: m.Module, Digest: digest}) keep[m.Module] = true was, wasPlaced := committed[m.Module] if held, ok := of.modules[m.Module]; ok && Digest(held.Placed) == digest && (held.Previous == nil || wasPlaced && Digest(was) == digest) { continue // the key holds it, and no previous value a reader could take in its place } - v := seat.PlacementByNodeModuleValue{Node: node, Placed: m} + v := seat.AssignmentsByNodeModuleValue{Node: node, Placed: m} if wasPlaced && Digest(was) != digest { v.Previous = &was } diff --git a/placement/go/clients/meshmoduleassignerclient/ops.go b/go/clients/meshmoduleassignerclient/ops.go similarity index 100% rename from placement/go/clients/meshmoduleassignerclient/ops.go rename to go/clients/meshmoduleassignerclient/ops.go diff --git a/placement/go/clients/meshmoduleassignerclient/ops_test.go b/go/clients/meshmoduleassignerclient/ops_test.go similarity index 100% rename from placement/go/clients/meshmoduleassignerclient/ops_test.go rename to go/clients/meshmoduleassignerclient/ops_test.go diff --git a/placement/go/clients/meshmoduleassignerclient/placed.go b/go/clients/meshmoduleassignerclient/placed.go similarity index 97% rename from placement/go/clients/meshmoduleassignerclient/placed.go rename to go/clients/meshmoduleassignerclient/placed.go index bf9fe53..1bae32a 100644 --- a/placement/go/clients/meshmoduleassignerclient/placed.go +++ b/go/clients/meshmoduleassignerclient/placed.go @@ -7,7 +7,7 @@ import ( "sync" "time" - seat "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + seat "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" ) // Placed is where the placement (the seat mesh-placement's state `placement`, held by the assigner; ADR 0567, ADR 0549 §3) places each module, and since when: @@ -65,7 +65,7 @@ func (p *Placed) Put(key string, value []byte, deleted bool, at time.Time) (node var derr error var named string if kind == NodeKind { - var n seat.PlacementByNodeNodeValue + var n seat.AssignmentsByNodeNodeValue n, derr = DecodeNode(value) named = n.Node for _, r := range n.Modules { diff --git a/placement/go/clients/meshmoduleassignerclient/placement.go b/go/clients/meshmoduleassignerclient/placement.go similarity index 99% rename from placement/go/clients/meshmoduleassignerclient/placement.go rename to go/clients/meshmoduleassignerclient/placement.go index 823287e..efe9612 100644 --- a/placement/go/clients/meshmoduleassignerclient/placement.go +++ b/go/clients/meshmoduleassignerclient/placement.go @@ -15,7 +15,7 @@ import ( "strconv" "strings" - seat "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + seat "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-sdk/go/catalogue" "git.novox.be/novox/mesh-sdk/go/keys" ) diff --git a/placement/go/clients/meshmoduleassignerclient/proposal.go b/go/clients/meshmoduleassignerclient/proposal.go similarity index 100% rename from placement/go/clients/meshmoduleassignerclient/proposal.go rename to go/clients/meshmoduleassignerclient/proposal.go diff --git a/placement/go/clients/meshmoduleassignerclient/reads.go b/go/clients/meshmoduleassignerclient/reads.go similarity index 100% rename from placement/go/clients/meshmoduleassignerclient/reads.go rename to go/clients/meshmoduleassignerclient/reads.go diff --git a/placement/go/clients/meshmoduleassignerclient/reads_test.go b/go/clients/meshmoduleassignerclient/reads_test.go similarity index 100% rename from placement/go/clients/meshmoduleassignerclient/reads_test.go rename to go/clients/meshmoduleassignerclient/reads_test.go diff --git a/placement/go/seats/meshmoduleassigner/contract.go b/go/seats/meshmoduleassigner/contract.go similarity index 88% rename from placement/go/seats/meshmoduleassigner/contract.go rename to go/seats/meshmoduleassigner/contract.go index 10de4c1..fd50676 100644 --- a/placement/go/seats/meshmoduleassigner/contract.go +++ b/go/seats/meshmoduleassigner/contract.go @@ -15,11 +15,11 @@ import ( const Seat = "mesh-module-assigner" const ( - VerbPlacement = "placement" - VerbProposals = "proposals" - VerbPropose = "propose" - VerbWithdraw = "withdraw" - VerbAgain = "again" + VerbAssignments = "assignments" + VerbProposals = "proposals" + VerbPropose = "propose" + VerbWithdraw = "withdraw" + VerbAgain = "again" ) const ( @@ -31,7 +31,7 @@ const ( // Verbs is every verb the seat serves, in its definition's order: a new copy each call. func Verbs() []string { - return []string{VerbPlacement, VerbProposals, VerbPropose, VerbWithdraw, VerbAgain} + return []string{VerbAssignments, VerbProposals, VerbPropose, VerbWithdraw, VerbAgain} } // Events is every event published on the seat's subject: a new copy each call. @@ -58,18 +58,18 @@ const SubjectPlacementJudged = "mesh.seat.mesh-module-assigner.event.placement-j const SubjectPlacementChanged = "mesh.seat.mesh-module-assigner.event.placement-changed" const ( - StatePlacement = "placement" + StateAssignments = "assignments" StateProposals = "proposals" StateCandidates = "candidates" StatePulses = "pulses" StateSeatHolders = "seat-holders" ) -// PlacementByNodeNodeKey is the key of one value of the state placement under the form ".node". -func PlacementByNodeNodeKey(node string) string { return node + ".node" } +// AssignmentsByNodeNodeKey is the key of one value of the state assignments under the form ".node". +func AssignmentsByNodeNodeKey(node string) string { return node + ".node" } -// ParsePlacementByNodeNodeKey reads the parts of a key of the form ".node", in the order of the form; the last result is false for a key of another form. -func ParsePlacementByNodeNodeKey(key string) (string, bool) { +// ParseAssignmentsByNodeNodeKey reads the parts of a key of the form ".node", in the order of the form; the last result is false for a key of another form. +func ParseAssignmentsByNodeNodeKey(key string) (string, bool) { rest := key v0, after0, found0 := strings.Cut(rest, ".node") if !found0 || v0 == "" { @@ -82,9 +82,9 @@ func ParsePlacementByNodeNodeKey(key string) (string, bool) { return v0, true } -// DecodePlacementByNodeNodeValue reads one value of the state placement under the form ".node", refusing a field PlacementByNodeNodeValue does not know and any text after the value. -func DecodePlacementByNodeNodeValue(raw []byte) (PlacementByNodeNodeValue, error) { - var v PlacementByNodeNodeValue +// DecodeAssignmentsByNodeNodeValue reads one value of the state assignments under the form ".node", refusing a field AssignmentsByNodeNodeValue does not know and any text after the value. +func DecodeAssignmentsByNodeNodeValue(raw []byte) (AssignmentsByNodeNodeValue, error) { + var v AssignmentsByNodeNodeValue dec := json.NewDecoder(bytes.NewReader(raw)) dec.DisallowUnknownFields() if err := dec.Decode(&v); err != nil { @@ -96,11 +96,11 @@ func DecodePlacementByNodeNodeValue(raw []byte) (PlacementByNodeNodeValue, error return v, nil } -// PlacementByNodeModuleKey is the key of one value of the state placement under the form ".". -func PlacementByNodeModuleKey(node, module string) string { return node + "." + module } +// AssignmentsByNodeModuleKey is the key of one value of the state assignments under the form ".". +func AssignmentsByNodeModuleKey(node, module string) string { return node + "." + module } -// ParsePlacementByNodeModuleKey reads the parts of a key of the form ".", in the order of the form; the last result is false for a key of another form. -func ParsePlacementByNodeModuleKey(key string) (string, string, bool) { +// ParseAssignmentsByNodeModuleKey reads the parts of a key of the form ".", in the order of the form; the last result is false for a key of another form. +func ParseAssignmentsByNodeModuleKey(key string) (string, string, bool) { rest := key v0, after0, found0 := strings.Cut(rest, ".") if !found0 || v0 == "" { @@ -118,9 +118,9 @@ func ParsePlacementByNodeModuleKey(key string) (string, string, bool) { return v0, v2, true } -// DecodePlacementByNodeModuleValue reads one value of the state placement under the form ".", refusing a field PlacementByNodeModuleValue does not know and any text after the value. -func DecodePlacementByNodeModuleValue(raw []byte) (PlacementByNodeModuleValue, error) { - var v PlacementByNodeModuleValue +// DecodeAssignmentsByNodeModuleValue reads one value of the state assignments under the form ".", refusing a field AssignmentsByNodeModuleValue does not know and any text after the value. +func DecodeAssignmentsByNodeModuleValue(raw []byte) (AssignmentsByNodeModuleValue, error) { + var v AssignmentsByNodeModuleValue dec := json.NewDecoder(bytes.NewReader(raw)) dec.DisallowUnknownFields() if err := dec.Decode(&v); err != nil { @@ -547,8 +547,8 @@ type Warrant struct { Warrant string `json:"warrant"` } -// PlacementInput is what placement takes. -type PlacementInput struct { +// AssignmentsInput is what assignments takes. +type AssignmentsInput struct { // "true": the changes in order, from the stream and the state's revisions History string `json:"history,omitempty"` // one module: the nodes it is placed on @@ -559,20 +559,20 @@ type PlacementInput struct { Seats string `json:"seats,omitempty"` } -// The values of PlacementInput's history. +// The values of AssignmentsInput's history. const ( - PlacementInputHistoryTrue = "true" - PlacementInputHistoryFalse = "false" + AssignmentsInputHistoryTrue = "true" + AssignmentsInputHistoryFalse = "false" ) -// The values of PlacementInput's seats. +// The values of AssignmentsInput's seats. const ( - PlacementInputSeatsTrue = "true" - PlacementInputSeatsFalse = "false" + AssignmentsInputSeatsTrue = "true" + AssignmentsInputSeatsFalse = "false" ) -// PlacementAnswer is what placement answers. -type PlacementAnswer = map[string]any +// AssignmentsAnswer is what assignments answers. +type AssignmentsAnswer = map[string]any // ProposalsInput is what proposals takes. type ProposalsInput struct { @@ -628,26 +628,26 @@ type PlacementJudgedEvent = PlacementEvent // PlacementChangedEvent is the body of the event placement-changed. type PlacementChangedEvent = PlacementEvent -// PlacementByNodeNodeValueModulesItem is a type of the contract. -type PlacementByNodeNodeValueModulesItem struct { +// AssignmentsByNodeNodeValueModulesItem is a type of the contract. +type AssignmentsByNodeNodeValueModulesItem struct { // the digest of the module key's value it stands for Digest string `json:"digest"` Module string `json:"module"` } -// PlacementByNodeNodeValue is one value of the state placement under the key .node. -type PlacementByNodeNodeValue struct { +// AssignmentsByNodeNodeValue is one value of the state assignments under the key .node. +type AssignmentsByNodeNodeValue struct { Decided NodeDecided `json:"decided"` Holds []Holding `json:"holds,omitempty"` // the modules placed, in the order the composition reads them - Modules []PlacementByNodeNodeValueModulesItem `json:"modules"` - Node string `json:"node"` - Pins map[string]Provider `json:"pins,omitempty"` - V int64 `json:"v,omitempty"` + Modules []AssignmentsByNodeNodeValueModulesItem `json:"modules"` + Node string `json:"node"` + Pins map[string]Provider `json:"pins,omitempty"` + V int64 `json:"v,omitempty"` } -// PlacementByNodeModuleValue is one value of the state placement under the key .. -type PlacementByNodeModuleValue struct { +// AssignmentsByNodeModuleValue is one value of the state assignments under the key .. +type AssignmentsByNodeModuleValue struct { Node string `json:"node"` Placed PlacedModule `json:"placed"` // the value it replaces while a write is under way diff --git a/placement/go/seats/meshmoduleassigner/contract_test.go b/go/seats/meshmoduleassigner/contract_test.go similarity index 98% rename from placement/go/seats/meshmoduleassigner/contract_test.go rename to go/seats/meshmoduleassigner/contract_test.go index a007587..2fad606 100644 --- a/placement/go/seats/meshmoduleassigner/contract_test.go +++ b/go/seats/meshmoduleassigner/contract_test.go @@ -21,19 +21,19 @@ func TestFixtures(t *testing.T) { decode: func(b []byte) (any, error) { var v PlacementProposedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-module-assigner\\.event\\.placement-proposed$")}, {name: "a node's key", subject: "", given: "{\"decided\":{\"address\":\"10.0.0.2\"},\"modules\":[{\"digest\":\"0123456789abcdef\",\"module\":\"plex\"}],\"node\":\"ace\",\"v\":1}", wire: "{\"decided\":{\"address\":\"10.0.0.2\"},\"modules\":[{\"digest\":\"0123456789abcdef\",\"module\":\"plex\"}],\"node\":\"ace\",\"v\":1}", decode: func(b []byte) (any, error) { - var v PlacementByNodeNodeValue + var v AssignmentsByNodeNodeValue err := json.Unmarshal(b, &v) return v, err }, subjectIs: nil}, {name: "a node that came with a tunnel", subject: "", given: "{\"decided\":{\"tunnel\":{\"interface\":\"wg0\",\"peers\":[{\"address\":\"10.77.0.9/32\",\"public-key\":\"k2\"}],\"port\":51820,\"public-key\":\"k1\"}},\"modules\":[],\"node\":\"ace\",\"v\":1}", wire: "{\"decided\":{\"tunnel\":{\"interface\":\"wg0\",\"peers\":[{\"address\":\"10.77.0.9/32\",\"public-key\":\"k2\"}],\"port\":51820,\"public-key\":\"k1\"}},\"modules\":[],\"node\":\"ace\",\"v\":1}", decode: func(b []byte) (any, error) { - var v PlacementByNodeNodeValue + var v AssignmentsByNodeNodeValue err := json.Unmarshal(b, &v) return v, err }, subjectIs: nil}, {name: "a module's key", subject: "", given: "{\"node\":\"ace\",\"placed\":{\"bindings\":{\"database\":{\"module\":\"postgres\",\"node\":\"ace\"}},\"module\":\"plex\",\"ports\":{\"32400\":32400}},\"v\":1}", wire: "{\"node\":\"ace\",\"placed\":{\"bindings\":{\"database\":{\"module\":\"postgres\",\"node\":\"ace\"}},\"module\":\"plex\",\"ports\":{\"32400\":32400}},\"v\":1}", decode: func(b []byte) (any, error) { - var v PlacementByNodeModuleValue + var v AssignmentsByNodeModuleValue err := json.Unmarshal(b, &v) return v, err }, subjectIs: nil}, diff --git a/go/seats/meshmoduleassigner/go.mod b/go/seats/meshmoduleassigner/go.mod new file mode 100644 index 0000000..2d1f766 --- /dev/null +++ b/go/seats/meshmoduleassigner/go.mod @@ -0,0 +1,3 @@ +module git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner + +go 1.26.0 diff --git a/module.json b/module.json index 1a93c8e..1d7d5c0 100644 --- a/module.json +++ b/module.json @@ -81,6 +81,35 @@ ], "series": "0.1", "generated-from": "." + }, + { + "name": "go-meshmoduleassigner", + "kind": "package", + "language": "go", + "paths": [ + "go/seats/meshmoduleassigner" + ], + "series": "0.1", + "generated-from": "." + }, + { + "name": "go-meshmoduleassignerclient", + "kind": "package", + "language": "go", + "paths": [ + "go/clients/meshmoduleassignerclient" + ], + "series": "0.1" + }, + { + "name": "ts-meshmoduleassigner", + "kind": "package", + "language": "typescript", + "paths": [ + "ts/seats/meshmoduleassigner" + ], + "series": "0.1", + "generated-from": "." } ] }, diff --git a/placement/go/clients/meshmoduleassignerclient/go.mod b/placement/go/clients/meshmoduleassignerclient/go.mod deleted file mode 100644 index 45deaaf..0000000 --- a/placement/go/clients/meshmoduleassignerclient/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient - -go 1.26.0 - -require ( - git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-sdk/go v0.0.0-00010101000000-000000000000 -) diff --git a/placement/go/clients/meshwantsclient/go.mod b/placement/go/clients/meshwantsclient/go.mod deleted file mode 100644 index 26ce723..0000000 --- a/placement/go/clients/meshwantsclient/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient - -go 1.26.0 - -require ( - git.novox.be/novox/mesh-contracts/placement/go/seats/meshwants v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-sdk/go v0.0.0-00010101000000-000000000000 -) diff --git a/placement/go/clients/meshwantsclient/wants.go b/placement/go/clients/meshwantsclient/wants.go deleted file mode 100644 index 9f04734..0000000 --- a/placement/go/clients/meshwantsclient/wants.go +++ /dev/null @@ -1,326 +0,0 @@ -// Package meshwantsclient is the client of the seat mesh-wants: the keys of its state wants, and the check and the codec -// of a want, a node's own want and a hold, which every module that reads or writes wants shares (the assigner that writes -// them, the node-engine, the provisioner and the artifact store that read them). The layouts are the seat's contract; this -// file writes the version and refuses what the earlier codec refused (novox/hq ADR 0528, ADR 0539, ADR 0546, ADR 0634 §4). -package meshwantsclient - -import ( - "encoding/json" - "fmt" - "regexp" - "strings" - - seat "git.novox.be/novox/mesh-contracts/placement/go/seats/meshwants" - "git.novox.be/novox/mesh-sdk/go/keys" -) - -// The three values of the state wants, in the seat's types. -type ( - // Want is a node's want for a module: the fingerprint it should run and the digest of the one composition it allows, - // or Removed, or Self when the node composes it itself; Newest and Rule name what holds the node back. - Want = seat.WantsByWantNodeModuleValue - // NodeWant is a node's own want: the digest of the node's own composition it may apply. - NodeWant = seat.WantsByWantNodeNodeValue - // Held is a fingerprint the assigner holds: the node whose health check failed on it, and the check's words. - Held = seat.WantsByHeldModuleFingerprintValue -) - -// The rules of ADR 0429 §6 and ADR 0539 clause 6, by number, as a want or a difference names the one that holds a node back. -const ( - RuleBuildDone = 1 // the build of F is done - RuleNotHeld = 2 // F is not held - RuleCanary = 3 // the node is the module's canary, or the canary runs F healthy - RuleStep = 4 // at most step other nodes are ahead (step 0: no limit, ADR 0528 §3) - RuleProviders = 5 // every provider the module requires runs healthy on that node, or at its canary - RuleGrant = 6 // the module's credential for F exists (grants..) - RuleComposed = 7 // the module's composition for that node at F exists (ADR 0539 §6) - // RuleProvisions: every provision the module requires on that node is provided (novox/hq ADR 0570 §4). - RuleProvisions = 8 -) - -// ruleRead is the highest rule a reader takes (novox/hq ADR 0614 §2, §4): 9 and 10 are kept for the verb wait and the -// capability hold of a later release. -const ruleRead = 10 - -// The answers a want carries to a failed backup before an apply (ADR 0539 clause 16). -const ( - // BackupAgain is the answer to back the module up again before the apply. - BackupAgain = seat.WantsByWantNodeModuleValueBackupAgain - // BackupSkip is the answer to go on without a backup. - BackupSkip = seat.WantsByWantNodeModuleValueBackupSkip -) - -// NodeWord is the last part of a node's own key. -const NodeWord = "node" - -const ( - wantWord = "want" - heldWord = "held" - srcWord = "src2:" -) - -var ( - moduleNameOK = regexp.MustCompile(`^[-/_a-zA-Z0-9]+(\.[-/_a-zA-Z0-9]+)*$`) - digestForm = regexp.MustCompile(`^sha256:[0-9a-f]{64}$`) -) - -func moduleKeyPart(module string) (string, error) { - if module == "" { - return "", fmt.Errorf("wants: the module name is empty") - } - if !moduleNameOK.MatchString(module) { - return "", fmt.Errorf("wants: the module name %q holds a wildcard, whitespace, '=', an empty part between dots or a character a key refuses", module) - } - return keys.EscapeName(module), nil -} - -func parseModuleKeyPart(part string) (string, error) { - if err := keys.CheckName("module", part); err != nil { - return "", err - } - module, err := keys.UnescapeName(part) - if err != nil { - return "", err - } - if back, err := moduleKeyPart(module); err != nil || back != part { - return "", fmt.Errorf("wants: the module part %q is not a module name as a key writes it", part) - } - return module, nil -} - -func checkFingerprint(fp, of string) error { - if part, ok := strings.CutPrefix(fp, srcWord); !ok || !keys.IsHex64(part) { - return fmt.Errorf("wants: %s: %q is not a fingerprint of the new form, %s and 64 lowercase hex digits", of, fp, srcWord) - } - return nil -} - -func checkDigest(digest, of string) error { - if !digestForm.MatchString(digest) { - return fmt.Errorf("wants: %s %q is not sha256:<64 lowercase hex digits>", of, digest) - } - return nil -} - -func checkRule(rule int64, of string) error { - if rule < RuleBuildDone || rule > ruleRead { - return fmt.Errorf("wants: %s names the rule %d, which is not one of 1 to %d", of, rule, ruleRead) - } - return nil -} - -// WantKey is the key, in the state wants, of a node's want for a module: `want..`. A module named `node` is -// refused, since `want..node` is the node's own key. -func WantKey(node, module string) (string, error) { - if err := keys.CheckName("node", node); err != nil { - return "", err - } - part, err := moduleKeyPart(module) - if err != nil { - return "", err - } - if module == NodeWord { - return "", fmt.Errorf(`wants: the module name "node" is refused: want.%s.node is the node's own key`, node) - } - return seat.WantsByWantNodeModuleKey(node, part), nil -} - -// WantNodeKey is the key of a node's own want: `want..node`. -func WantNodeKey(node string) (string, error) { - if err := keys.CheckName("node", node); err != nil { - return "", err - } - return seat.WantsByWantNodeNodeKey(node), nil -} - -// ParseWantNodeKey is the inverse of WantNodeKey; it refuses a module's want key and any key of another form. -func ParseWantNodeKey(key string) (node string, err error) { - node, ok := seat.ParseWantsByWantNodeNodeKey(key) - if !ok || keys.CheckName("node", node) != nil { - return "", fmt.Errorf("wants: %q is not a node's own want key, want..node", key) - } - return node, nil -} - -// ParseWantKey is the inverse of WantKey; it refuses a held key, the node's own want key and any key of another form. -func ParseWantKey(key string) (node, module string, err error) { - refused := fmt.Errorf("wants: %q is not a want key, want..", key) - node, part, ok := seat.ParseWantsByWantNodeModuleKey(key) - if !ok || part == NodeWord || keys.CheckName("node", node) != nil { - return "", "", refused - } - if module, err = parseModuleKeyPart(part); err != nil { - return "", "", refused - } - return node, module, nil -} - -// HeldKey is the key of a module's fingerprint the assigner holds: `held..`, the hex being the -// fingerprint's key part. -func HeldKey(module, fingerprint string) (string, error) { - if err := keys.CheckName("module", module); err != nil { - return "", err - } - if err := checkFingerprint(fingerprint, "a held key's fingerprint"); err != nil { - return "", err - } - return seat.WantsByHeldModuleFingerprintKey(module, strings.TrimPrefix(fingerprint, srcWord)), nil -} - -// ParseHeldKey is the inverse of HeldKey: the module and the fingerprint (`src2:` and the hex). -func ParseHeldKey(key string) (module, fingerprint string, err error) { - module, part, ok := seat.ParseWantsByHeldModuleFingerprintKey(key) - if !ok || keys.CheckName("module", module) != nil || !keys.IsHex64(part) { - return "", "", fmt.Errorf("wants: %q is not a held key, held..", key) - } - return module, srcWord + part, nil -} - -func checkWant(w Want) error { - if w.Removed { - if w.Fp != "" || w.Composed != "" || w.Newest != "" || w.Rule != 0 || w.Backup != "" || w.Put || w.Hold || w.Self || w.Yes != "" || w.Release != "" { - return fmt.Errorf("wants: a removed want names no fingerprint, composition, rule, backup answer or put") - } - return nil - } - if w.Hold && !w.Put { - return fmt.Errorf("wants: only a put holds a want") - } - if w.Put && (w.Newest != "" || w.Rule != 0) { - return fmt.Errorf("wants: a put's want is held back by no rule") - } - if err := checkFingerprint(w.Fp, "a want's fingerprint"); err != nil { - return err - } - // A node the catalogue places composes its own modules (ADR 0547 §2): its want is Self and names the build alone. Any - // other want names the one composition it allows (ADR 0539 clause 1). - switch { - case w.Self && w.Composed != "": - return fmt.Errorf("wants: the want of %s says the node composes it, and names a composition", w.Fp) - case !w.Self && w.Composed == "": - return fmt.Errorf("wants: the want of %s names no composition: a want names the digest of the one composition it allows (ADR 0539 clause 1), unless the node composes it itself", w.Fp) - case w.Composed != "": - if err := checkDigest(w.Composed, "a want's composition digest"); err != nil { - return err - } - } - if w.Yes != "" { - if err := checkDigest(w.Yes, "the digest a want's yes allows"); err != nil { - return err - } - } else if w.Release != "" { - return fmt.Errorf("wants: the want of %s names the release %s and no yes it gave", w.Fp, w.Release) - } - switch w.Backup { - case "", BackupAgain, BackupSkip: - default: - return fmt.Errorf("wants: the backup answer %q is not %s or %s", w.Backup, BackupAgain, BackupSkip) - } - if w.Newest == "" && w.Rule == 0 { - return nil - } - if err := checkFingerprint(w.Newest, "a want's newest fingerprint"); err != nil { - return err - } - if w.Newest == w.Fp { - return fmt.Errorf("wants: a want at its newest fingerprint names no rule holding it back") - } - return checkRule(w.Rule, "a want held back") -} - -// CheckWant refuses a want the codec refuses. -func CheckWant(w Want) error { return checkWant(w) } - -// EncodeWant is w as JSON at the kept values' version; it refuses a fingerprint not of the new form, a removed want naming -// one, and a want that names no composition unless the node composes it itself. -func EncodeWant(w Want) ([]byte, error) { - if err := checkWant(w); err != nil { - return nil, err - } - w.V = keys.Version - return json.Marshal(w) -} - -// DecodeWant reads a want; it refuses an unknown version (keys.ErrUnknownVersion), a field the contract does not know and -// what EncodeWant refuses. -func DecodeWant(data []byte) (Want, error) { - if err := keys.CheckVersion(data); err != nil { - return Want{}, err - } - w, err := seat.DecodeWantsByWantNodeModuleValue(data) - if err != nil { - return Want{}, fmt.Errorf("wants: value does not fit its shape: %w", err) - } - if err := checkWant(w); err != nil { - return Want{}, err - } - return w, nil -} - -// SameWant says whether two wants are the same value, so the assigner writes only when they differ. -func SameWant(a, b Want) bool { return a == b } - -func checkNodeWant(w NodeWant) error { - switch { - case w.Self && w.Composed != "": - return fmt.Errorf("wants: the node's own want says the node composes it, and names a composition") - case w.Self: - return nil - } - return checkDigest(w.Composed, "the node's own want's composition digest") -} - -// EncodeNodeWant is w as JSON; it refuses a digest not of the sha256 form, and a want that names no composition unless it -// says the node composes its own (Self, ADR 0547 §2). -func EncodeNodeWant(w NodeWant) ([]byte, error) { - if err := checkNodeWant(w); err != nil { - return nil, err - } - w.V = keys.Version - return json.Marshal(w) -} - -// DecodeNodeWant reads a node's own want; it refuses an unknown version, a field a node's own want does not have (a -// fingerprint among them), and what EncodeNodeWant refuses. -func DecodeNodeWant(data []byte) (NodeWant, error) { - if err := keys.CheckVersion(data); err != nil { - return NodeWant{}, err - } - w, err := seat.DecodeWantsByWantNodeNodeValue(data) - if err != nil { - return NodeWant{}, fmt.Errorf("wants: a node's own want does not fit its shape: %w", err) - } - if err := checkNodeWant(w); err != nil { - return NodeWant{}, err - } - return w, nil -} - -func checkHeld(h Held) error { return keys.CheckName("node", h.Node) } - -// EncodeHeld is h as JSON; it refuses a node name a key refuses. -func EncodeHeld(h Held) ([]byte, error) { - if err := checkHeld(h); err != nil { - return nil, err - } - h.V = keys.Version - return json.Marshal(h) -} - -// DecodeHeld reads a hold; it refuses an unknown version and what EncodeHeld refuses. -func DecodeHeld(data []byte) (Held, error) { - if err := keys.CheckVersion(data); err != nil { - return Held{}, err - } - h, err := seat.DecodeWantsByHeldModuleFingerprintValue(data) - if err != nil { - return Held{}, fmt.Errorf("wants: value does not fit its shape: %w", err) - } - if err := checkHeld(h); err != nil { - return Held{}, err - } - return h, nil -} - -// SameHeld says whether two holds are the same value. -func SameHeld(a, b Held) bool { return a == b } diff --git a/placement/go/clients/meshwantsclient/wants_test.go b/placement/go/clients/meshwantsclient/wants_test.go deleted file mode 100644 index 5e2ecad..0000000 --- a/placement/go/clients/meshwantsclient/wants_test.go +++ /dev/null @@ -1,162 +0,0 @@ -package meshwantsclient - -import ( - "encoding/json" - "errors" - "reflect" - "strings" - "testing" - - "git.novox.be/novox/mesh-sdk/go/keys" -) - -func rep(c string) string { return strings.Repeat(c, 64) } - -// TestWantsAreWhatTheEarlierCodecWrote holds the values and keys equal to what go/delivery's codec wrote for the same -// values (the golden texts are its output; the field order is the contract's now, so values are compared decoded), and the -// refusals it made. -func TestWantsAreWhatTheEarlierCodecWrote(t *testing.T) { - a, b, c, d := "src2:"+rep("a"), "src2:"+rep("b"), "sha256:"+rep("c"), "sha256:"+rep("d") - sameJSON := func(t *testing.T, got []byte, want string) { - t.Helper() - var g, w any - if err := json.Unmarshal(got, &g); err != nil { - t.Fatal(err) - } - if err := json.Unmarshal([]byte(want), &w); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(g, w) { - t.Fatalf("wrote %s, the earlier codec wrote %s", got, want) - } - } - t.Run("wants", func(t *testing.T) { - for name, row := range map[string]struct { - w Want - golden string - }{ - "composed": {Want{Fp: a, Composed: c, Words: "w"}, `{"v":1,"fp":"` + a + `","composed":"` + c + `","words":"w"}`}, - "held back": {Want{Fp: a, Self: true, Newest: b, Rule: RuleGrant, Backup: BackupAgain, Words: "held"}, `{"v":1,"fp":"` + a + `","newest":"` + b + `","rule":6,"backup":"again","words":"held","self":true}`}, - "put": {Want{Fp: a, Self: true, Put: true, Hold: true, Yes: d, Release: "r1"}, `{"v":1,"fp":"` + a + `","put":true,"hold":true,"yes":"` + d + `","release":"r1","self":true}`}, - "removed": {Want{Removed: true, Words: "gone"}, `{"v":1,"removed":true,"words":"gone"}`}, - } { - t.Run(name, func(t *testing.T) { - raw, err := EncodeWant(row.w) - if err != nil { - t.Fatal(err) - } - sameJSON(t, raw, row.golden) - back, err := DecodeWant(raw) - if err != nil || !SameWant(back, func() Want { w := row.w; w.V = keys.Version; return w }()) { - t.Fatalf("read back %+v, %v", back, err) - } - }) - } - }) - t.Run("refused", func(t *testing.T) { - for name, w := range map[string]Want{ - "old fingerprint": {Fp: "src1:abc", Composed: c}, - "no composition": {Fp: a}, - "self and composition": {Fp: a, Self: true, Composed: c}, - "hold without put": {Fp: a, Self: true, Hold: true}, - "put held back": {Fp: a, Self: true, Put: true, Newest: b, Rule: RuleStep}, - "removed names a rule": {Removed: true, Rule: 1}, - "release without yes": {Fp: a, Self: true, Release: "r"}, - "backup answer unknown": {Fp: a, Self: true, Backup: "later"}, - "newest equals fp": {Fp: a, Self: true, Newest: a, Rule: RuleStep}, - "rule 11": {Fp: a, Self: true, Newest: b, Rule: 11}, - "newest without a rule": {Fp: a, Self: true, Newest: b}, - "composition not sha256": {Fp: a, Composed: "sha256:xyz"}, - } { - if _, err := EncodeWant(w); err == nil { - t.Errorf("%s: written", name) - } - } - if _, err := EncodeWant(Want{Fp: a, Self: true, Newest: b, Rule: 10}); err != nil { - t.Errorf("rule 10 is read: %v", err) - } - if _, err := DecodeWant([]byte(`{"fp":"` + a + `","self":true}`)); !errors.Is(err, keys.ErrUnknownVersion) { - t.Errorf("no version: %v", err) - } - if _, err := DecodeWant([]byte(`{"v":1,"fp":"` + a + `","self":true,"extra":1}`)); err == nil { - t.Error("an unknown field is read") - } - }) - t.Run("node want and hold", func(t *testing.T) { - raw, err := EncodeNodeWant(NodeWant{Composed: c, Words: "n"}) - if err != nil { - t.Fatal(err) - } - sameJSON(t, raw, `{"v":1,"composed":"`+c+`","words":"n"}`) - raw, err = EncodeNodeWant(NodeWant{Self: true}) - if err != nil { - t.Fatal(err) - } - sameJSON(t, raw, `{"v":1,"composed":"","self":true}`) - if back, err := DecodeNodeWant(raw); err != nil || !back.Self { - t.Fatalf("%+v %v", back, err) - } - for name, w := range map[string]NodeWant{"none": {}, "both": {Self: true, Composed: c}, "bad": {Composed: "x"}} { - if _, err := EncodeNodeWant(w); err == nil { - t.Errorf("%s: written", name) - } - } - if _, err := DecodeNodeWant([]byte(`{"v":1,"composed":"` + c + `","fp":"` + a + `"}`)); err == nil { - t.Error("a fingerprint in a node's own want is read") - } - raw, err = EncodeHeld(Held{Node: "ace", Words: "unhealthy"}) - if err != nil { - t.Fatal(err) - } - sameJSON(t, raw, `{"v":1,"node":"ace","words":"unhealthy"}`) - if h, err := DecodeHeld(raw); err != nil || !SameHeld(h, Held{V: 1, Node: "ace", Words: "unhealthy"}) { - t.Fatalf("%+v %v", h, err) - } - if _, err := EncodeHeld(Held{Node: "a.b"}); err == nil { - t.Error("a node with a dot is held") - } - }) - t.Run("keys", func(t *testing.T) { - for _, row := range []struct{ module, want string }{ - {"mesh-delivery-assigner", "want.ace.mesh-delivery-assigner"}, - {"novox.be", "want.ace.novox=2ebe"}, - } { - k, err := WantKey("ace", row.module) - if err != nil || k != row.want { - t.Fatalf("WantKey %s = %q, %v", row.module, k, err) - } - if n, m, err := ParseWantKey(k); err != nil || n != "ace" || m != row.module { - t.Fatalf("ParseWantKey %q = %q %q %v", k, n, m, err) - } - } - if k, err := HeldKey("mesh-delivery-assigner", a); err != nil || k != "held.mesh-delivery-assigner."+rep("a") { - t.Fatalf("HeldKey = %q %v", k, err) - } - if m, fp, err := ParseHeldKey("held.mesh-delivery-assigner." + rep("a")); err != nil || m != "mesh-delivery-assigner" || fp != a { - t.Fatalf("ParseHeldKey = %q %q %v", m, fp, err) - } - if k, err := WantNodeKey("ace"); err != nil || k != "want.ace.node" { - t.Fatalf("WantNodeKey = %q %v", k, err) - } - if n, err := ParseWantNodeKey("want.ace.node"); err != nil || n != "ace" { - t.Fatalf("ParseWantNodeKey = %q %v", n, err) - } - if _, err := WantKey("ace", "node"); err == nil { - t.Error("a module named node has a want key") - } - if _, err := HeldKey("novox.be", a); err == nil { - t.Error("a module with a dot has a held key") - } - for _, key := range []string{"want.ace.node", "held.m." + rep("a"), "ace.m", "want.ace", "want..m"} { - if _, _, err := ParseWantKey(key); err == nil { - t.Errorf("%q is a want key", key) - } - } - if _, err := ParseWantNodeKey("want.ace.m"); err == nil { - t.Error("a module's want is a node's own") - } - if _, _, err := ParseHeldKey("want.ace.m"); err == nil { - t.Error("a want key is a held key") - } - }) -} diff --git a/placement/go/seats/meshmoduleassigner/go.mod b/placement/go/seats/meshmoduleassigner/go.mod deleted file mode 100644 index cda234d..0000000 --- a/placement/go/seats/meshmoduleassigner/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner - -go 1.26.0 diff --git a/placement/go/seats/meshwants/contract.go b/placement/go/seats/meshwants/contract.go deleted file mode 100644 index 74860fa..0000000 --- a/placement/go/seats/meshwants/contract.go +++ /dev/null @@ -1,337 +0,0 @@ -// Code generated by seatgen from the seat definitions; DO NOT EDIT. - -// Package meshwants is the contract of the seat "mesh-wants", defined whole (seats/mesh-wants/seat.json): The seat that says what each node should run and whether each module's new builds wait for a person (ADR 0565, ADR 0539). -package meshwants - -import ( - "bytes" - "encoding/json" - "errors" - "strings" -) - -// Seat is the seat's name. -const Seat = "mesh-wants" - -const ( - VerbUpgrade = "upgrade" - VerbBus = "bus" -) - -const ( - EventPolicyChanged = "policy-changed" -) - -// Verbs is every verb the seat serves, in its definition's order: a new copy each call. -func Verbs() []string { - return []string{VerbUpgrade, VerbBus} -} - -// Events is every event published on the seat's subject: a new copy each call. -func Events() []string { - return []string{EventPolicyChanged} -} - -// Family is where the seat's events are kept: the shared events stream, filtered to the seat's subjects. -const ( - Family = "mesh-wants.events" - FamilyMaxAgeDays = 7 -) - -// SubjectPolicyChanged is where policy-changed is published. -const SubjectPolicyChanged = "mesh.seat.mesh-wants.event.policy-changed" - -const ( - StateWants = "wants" - StateDifferences = "differences" - StateCompositions = "compositions" -) - -// WantsByWantNodeModuleKey is the key of one value of the state wants under the form "want..". -func WantsByWantNodeModuleKey(node, module string) string { return "want." + node + "." + module } - -// ParseWantsByWantNodeModuleKey reads the parts of a key of the form "want..", in the order of the form; the last result is false for a key of another form. -func ParseWantsByWantNodeModuleKey(key string) (string, string, bool) { - rest := key - if !strings.HasPrefix(rest, "want.") { - return "", "", false - } - rest = rest[5:] - v1, after1, found1 := strings.Cut(rest, ".") - if !found1 || v1 == "" { - return "", "", false - } - rest = after1 - if rest == "" { - return "", "", false - } - v3 := rest - rest = "" - if rest != "" { - return "", "", false - } - return v1, v3, true -} - -// DecodeWantsByWantNodeModuleValue reads one value of the state wants under the form "want..", refusing a field WantsByWantNodeModuleValue does not know and any text after the value. -func DecodeWantsByWantNodeModuleValue(raw []byte) (WantsByWantNodeModuleValue, error) { - var v WantsByWantNodeModuleValue - dec := json.NewDecoder(bytes.NewReader(raw)) - dec.DisallowUnknownFields() - if err := dec.Decode(&v); err != nil { - return v, err - } - if dec.More() { - return v, errors.New("text after the value") - } - return v, nil -} - -// WantsByWantNodeNodeKey is the key of one value of the state wants under the form "want..node". -func WantsByWantNodeNodeKey(node string) string { return "want." + node + ".node" } - -// ParseWantsByWantNodeNodeKey reads the parts of a key of the form "want..node", in the order of the form; the last result is false for a key of another form. -func ParseWantsByWantNodeNodeKey(key string) (string, bool) { - rest := key - if !strings.HasPrefix(rest, "want.") { - return "", false - } - rest = rest[5:] - v1, after1, found1 := strings.Cut(rest, ".node") - if !found1 || v1 == "" { - return "", false - } - rest = after1 - if rest != "" { - return "", false - } - return v1, true -} - -// DecodeWantsByWantNodeNodeValue reads one value of the state wants under the form "want..node", refusing a field WantsByWantNodeNodeValue does not know and any text after the value. -func DecodeWantsByWantNodeNodeValue(raw []byte) (WantsByWantNodeNodeValue, error) { - var v WantsByWantNodeNodeValue - dec := json.NewDecoder(bytes.NewReader(raw)) - dec.DisallowUnknownFields() - if err := dec.Decode(&v); err != nil { - return v, err - } - if dec.More() { - return v, errors.New("text after the value") - } - return v, nil -} - -// WantsByHeldModuleFingerprintKey is the key of one value of the state wants under the form "held..". -func WantsByHeldModuleFingerprintKey(module, fingerprint string) string { - return "held." + module + "." + fingerprint -} - -// ParseWantsByHeldModuleFingerprintKey reads the parts of a key of the form "held..", in the order of the form; the last result is false for a key of another form. -func ParseWantsByHeldModuleFingerprintKey(key string) (string, string, bool) { - rest := key - if !strings.HasPrefix(rest, "held.") { - return "", "", false - } - rest = rest[5:] - v1, after1, found1 := strings.Cut(rest, ".") - if !found1 || v1 == "" { - return "", "", false - } - rest = after1 - if rest == "" { - return "", "", false - } - v3 := rest - rest = "" - if rest != "" { - return "", "", false - } - return v1, v3, true -} - -// DecodeWantsByHeldModuleFingerprintValue reads one value of the state wants under the form "held..", refusing a field WantsByHeldModuleFingerprintValue does not know and any text after the value. -func DecodeWantsByHeldModuleFingerprintValue(raw []byte) (WantsByHeldModuleFingerprintValue, error) { - var v WantsByHeldModuleFingerprintValue - dec := json.NewDecoder(bytes.NewReader(raw)) - dec.DisallowUnknownFields() - if err := dec.Decode(&v); err != nil { - return v, err - } - if dec.More() { - return v, errors.New("text after the value") - } - return v, nil -} - -// DifferencesByNodeModuleKey is the key of one value of the state differences under the form ".". -func DifferencesByNodeModuleKey(node, module string) string { return node + "." + module } - -// ParseDifferencesByNodeModuleKey reads the parts of a key of the form ".", in the order of the form; the last result is false for a key of another form. -func ParseDifferencesByNodeModuleKey(key string) (string, string, bool) { - rest := key - v0, after0, found0 := strings.Cut(rest, ".") - if !found0 || v0 == "" { - return "", "", false - } - rest = after0 - if rest == "" { - return "", "", false - } - v2 := rest - rest = "" - if rest != "" { - return "", "", false - } - return v0, v2, true -} - -// DecodeDifferencesByNodeModuleValue reads one value of the state differences under the form ".", refusing a field DifferencesByNodeModuleValue does not know and any text after the value. -func DecodeDifferencesByNodeModuleValue(raw []byte) (DifferencesByNodeModuleValue, error) { - var v DifferencesByNodeModuleValue - dec := json.NewDecoder(bytes.NewReader(raw)) - dec.DisallowUnknownFields() - if err := dec.Decode(&v); err != nil { - return v, err - } - if dec.More() { - return v, errors.New("text after the value") - } - return v, nil -} - -// CompositionsByGroupHeadsKey is the key of one value of the state compositions under the form ".". -func CompositionsByGroupHeadsKey(group, heads string) string { return group + "." + heads } - -// ParseCompositionsByGroupHeadsKey reads the parts of a key of the form ".", in the order of the form; the last result is false for a key of another form. -func ParseCompositionsByGroupHeadsKey(key string) (string, string, bool) { - rest := key - v0, after0, found0 := strings.Cut(rest, ".") - if !found0 || v0 == "" { - return "", "", false - } - rest = after0 - if rest == "" { - return "", "", false - } - v2 := rest - rest = "" - if rest != "" { - return "", "", false - } - return v0, v2, true -} - -// DecodeCompositionsByGroupHeadsValue reads one value of the state compositions under the form ".", refusing a field CompositionsByGroupHeadsValue does not know and any text after the value. -func DecodeCompositionsByGroupHeadsValue(raw []byte) (CompositionsByGroupHeadsValue, error) { - var v CompositionsByGroupHeadsValue - dec := json.NewDecoder(bytes.NewReader(raw)) - dec.DisallowUnknownFields() - if err := dec.Decode(&v); err != nil { - return v, err - } - if dec.More() { - return v, errors.New("text after the value") - } - return v, nil -} - -// UpgradeInput is what upgrade takes. -type UpgradeInput struct { - // "true": each node's own composition, by its digest, and the assigner's findings - Compare string `json:"compare,omitempty"` - // one module - Module string `json:"module,omitempty"` - // with module: roll, together, record, or default to take a person's choice back - Policy string `json:"policy,omitempty"` - // with policy: why, required and kept - Why string `json:"why,omitempty"` -} - -// The values of UpgradeInput's compare. -const ( - UpgradeInputCompareTrue = "true" - UpgradeInputCompareFalse = "false" -) - -// UpgradeAnswer is what upgrade answers. -type UpgradeAnswer = map[string]any - -// BusInput is what bus takes. -type BusInput struct { -} - -// BusAnswer is what bus answers. -type BusAnswer = map[string]any - -// PolicyChangedEvent is the body of the event policy-changed. -type PolicyChangedEvent = map[string]any - -// WantsByWantNodeModuleValue is one value of the state wants under the key want... -type WantsByWantNodeModuleValue struct { - // again or skip - Backup string `json:"backup,omitempty"` - Composed string `json:"composed,omitempty"` - Fp string `json:"fp,omitempty"` - Hold bool `json:"hold,omitempty"` - Newest string `json:"newest,omitempty"` - Put bool `json:"put,omitempty"` - Release string `json:"release,omitempty"` - Removed bool `json:"removed,omitempty"` - // the rule that holds the node back, 1 to 10 - Rule int64 `json:"rule,omitempty"` - Self bool `json:"self,omitempty"` - V int64 `json:"v,omitempty"` - Words string `json:"words,omitempty"` - Yes string `json:"yes,omitempty"` -} - -// The values of WantsByWantNodeModuleValue's backup. -const ( - WantsByWantNodeModuleValueBackupAgain = "again" - WantsByWantNodeModuleValueBackupSkip = "skip" -) - -// WantsByWantNodeNodeValue is one value of the state wants under the key want..node. -type WantsByWantNodeNodeValue struct { - Composed string `json:"composed"` - Self bool `json:"self,omitempty"` - V int64 `json:"v,omitempty"` - Words string `json:"words,omitempty"` -} - -// WantsByHeldModuleFingerprintValue is one value of the state wants under the key held... -type WantsByHeldModuleFingerprintValue struct { - Node string `json:"node"` - V int64 `json:"v,omitempty"` - Words string `json:"words,omitempty"` -} - -// DifferencesByNodeModuleValue is one value of the state differences under the key .. -type DifferencesByNodeModuleValue struct { - Rule int64 `json:"rule,omitempty"` - Sent string `json:"sent,omitempty"` - SentBuild string `json:"sent-build,omitempty"` - Unexplained bool `json:"unexplained,omitempty"` - V int64 `json:"v,omitempty"` - Want string `json:"want,omitempty"` - WantRemoved bool `json:"want-removed,omitempty"` - Words string `json:"words,omitempty"` -} - -// CompositionsByGroupHeadsValue is one value of the state compositions under the key .. -type CompositionsByGroupHeadsValue struct { - Group string `json:"group"` - // 12 lowercase hex digits - Heads string `json:"heads"` - Machine string `json:"machine,omitempty"` - Machines []string `json:"machines,omitempty"` - Sdk string `json:"sdk,omitempty"` - // pass, fail or waits - State string `json:"state"` - // milliseconds - Took int64 `json:"took,omitempty"` - V int64 `json:"v,omitempty"` - Warnings []string `json:"warnings,omitempty"` - Words string `json:"words,omitempty"` -} diff --git a/placement/go/seats/meshwants/contract_test.go b/placement/go/seats/meshwants/contract_test.go deleted file mode 100644 index 1355a9b..0000000 --- a/placement/go/seats/meshwants/contract_test.go +++ /dev/null @@ -1,76 +0,0 @@ -// Code generated by seatgen from the seat definitions; DO NOT EDIT. - -package meshwants - -import ( - "encoding/json" - "reflect" - "regexp" - "testing" -) - -// TestFixtures reads every fixture of seats/mesh-wants/fixtures: each decodes into its type, a field the type does not -// know is dropped (ADR 0608 §6), and it reads back as its wire says; each subject named is the contract's. -func TestFixtures(t *testing.T) { - cases := []struct { - name, subject, given, wire string - decode func([]byte) (any, error) - subjectIs *regexp.Regexp - }{ - {name: "a want", subject: "", given: "{\"fp\":\"ab12\",\"rule\":3,\"v\":1}", wire: "{\"fp\":\"ab12\",\"rule\":3,\"v\":1}", - decode: func(b []byte) (any, error) { - var v WantsByWantNodeModuleValue - err := json.Unmarshal(b, &v) - return v, err - }, subjectIs: nil}, - {name: "a node's own want", subject: "", given: "{\"composed\":\"sha256:aa\",\"self\":true,\"v\":1}", wire: "{\"composed\":\"sha256:aa\",\"self\":true,\"v\":1}", - decode: func(b []byte) (any, error) { - var v WantsByWantNodeNodeValue - err := json.Unmarshal(b, &v) - return v, err - }, subjectIs: nil}, - {name: "a held fingerprint", subject: "", given: "{\"node\":\"ace\",\"v\":1}", wire: "{\"node\":\"ace\",\"v\":1}", - decode: func(b []byte) (any, error) { - var v WantsByHeldModuleFingerprintValue - err := json.Unmarshal(b, &v) - return v, err - }, subjectIs: nil}, - {name: "a difference", subject: "", given: "{\"unexplained\":true,\"v\":1,\"want\":\"ab12\"}", wire: "{\"unexplained\":true,\"v\":1,\"want\":\"ab12\"}", - decode: func(b []byte) (any, error) { - var v DifferencesByNodeModuleValue - err := json.Unmarshal(b, &v) - return v, err - }, subjectIs: nil}, - {name: "a composition", subject: "", given: "{\"group\":\"g1\",\"heads\":\"0123456789ab\",\"state\":\"pass\",\"v\":1}", wire: "{\"group\":\"g1\",\"heads\":\"0123456789ab\",\"state\":\"pass\",\"v\":1}", - decode: func(b []byte) (any, error) { - var v CompositionsByGroupHeadsValue - err := json.Unmarshal(b, &v) - return v, err - }, subjectIs: nil}, - } - for _, c := range cases { - v, err := c.decode([]byte(c.given)) - if err != nil { - t.Errorf("%s: does not decode: %v", c.name, err) - continue - } - back, err := json.Marshal(v) - if err != nil { - t.Errorf("%s: does not encode: %v", c.name, err) - continue - } - var got, want any - if err := json.Unmarshal(back, &got); err != nil { - t.Fatal(err) - } - if err := json.Unmarshal([]byte(c.wire), &want); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(got, want) { - t.Errorf("%s: reads back as %s, want %s", c.name, back, c.wire) - } - if c.subjectIs != nil && !c.subjectIs.MatchString(c.subject) { - t.Errorf("%s: subject %q is not the contract's %s", c.name, c.subject, c.subjectIs) - } - } -} diff --git a/placement/go/seats/meshwants/go.mod b/placement/go/seats/meshwants/go.mod deleted file mode 100644 index a075868..0000000 --- a/placement/go/seats/meshwants/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module git.novox.be/novox/mesh-contracts/placement/go/seats/meshwants - -go 1.26.0 diff --git a/placement/module.json b/placement/module.json deleted file mode 100644 index 9be9cf6..0000000 --- a/placement/module.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "module": "mesh-placement-contracts", - "version": "1", - "build": { - "artifacts": [ - { - "name": "go-meshmoduleassigner", - "kind": "package", - "language": "go", - "paths": [ - "go/seats/meshmoduleassigner" - ], - "series": "0.1", - "generated-from": "." - }, - { - "name": "go-meshwants", - "kind": "package", - "language": "go", - "paths": [ - "go/seats/meshwants" - ], - "series": "0.1", - "generated-from": "." - }, - { - "name": "go-meshmoduleassignerclient", - "kind": "package", - "language": "go", - "paths": [ - "go/clients/meshmoduleassignerclient" - ], - "series": "0.1" - }, - { - "name": "ts-meshmoduleassigner", - "kind": "package", - "language": "typescript", - "paths": [ - "ts/seats/meshmoduleassigner" - ], - "series": "0.1", - "generated-from": "." - }, - { - "name": "ts-meshwants", - "kind": "package", - "language": "typescript", - "paths": [ - "ts/seats/meshwants" - ], - "series": "0.1", - "generated-from": "." - } - ] - }, - "resources": [] -} diff --git a/placement/seats/index.json b/placement/seats/index.json deleted file mode 100644 index 6c07971..0000000 --- a/placement/seats/index.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "format": 1, - "seats": [ - "mesh-module-assigner", - "mesh-wants", - "node-service-manager", - "node-package-manager", - "node-container-runtime", - "node-environment" - ] -} diff --git a/placement/seats/mesh-wants/fixtures/states.json b/placement/seats/mesh-wants/fixtures/states.json deleted file mode 100644 index cb38a39..0000000 --- a/placement/seats/mesh-wants/fixtures/states.json +++ /dev/null @@ -1,47 +0,0 @@ -[ - { - "name": "a want", - "of": "state:wants/want..", - "given": { - "v": 1, - "fp": "ab12", - "rule": 3 - } - }, - { - "name": "a node's own want", - "of": "state:wants/want..node", - "given": { - "v": 1, - "composed": "sha256:aa", - "self": true - } - }, - { - "name": "a held fingerprint", - "of": "state:wants/held..", - "given": { - "v": 1, - "node": "ace" - } - }, - { - "name": "a difference", - "of": "state:differences/.", - "given": { - "v": 1, - "want": "ab12", - "unexplained": true - } - }, - { - "name": "a composition", - "of": "state:compositions/.", - "given": { - "v": 1, - "group": "g1", - "heads": "0123456789ab", - "state": "pass" - } - } -] diff --git a/placement/seats/mesh-wants/seat.json b/placement/seats/mesh-wants/seat.json deleted file mode 100644 index ececc75..0000000 --- a/placement/seats/mesh-wants/seat.json +++ /dev/null @@ -1,284 +0,0 @@ -{ - "format": 1, - "seat": "mesh-wants", - "scope": "mesh", - "grain": "whole", - "domain": "Placement", - "decision": "novox/hq ADR 0565, ADR 0634, ADR 0635", - "description": "The seat that says what each node should run and whether each module's new builds wait for a person (ADR 0565, ADR 0539).", - "verbs": [ - { - "name": "upgrade", - "description": "Whether each module's new builds wait for a person: its policy (roll, together or record) and where it comes from (a person, the module, the bus, its irreplaceable data, the default); the bus is never rolled. With module, that one; with module, policy and why, a person's choice for it, recorded as a hand act; with policy default, the choice taken back. With compare, each node's own composition and the assigner's findings.", - "input": { - "properties": { - "compare": { - "description": "\"true\": each node's own composition, by its digest, and the assigner's findings", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "module": { - "description": "one module", - "type": "string" - }, - "policy": { - "description": "with module: roll, together, record, or default to take a person's choice back", - "type": "string" - }, - "why": { - "description": "with policy: why, required and kept", - "type": "string" - } - }, - "type": "object" - }, - "answer": { - "type": "object", - "description": "each module's policy, or the one chosen, or the comparison" - } - }, - { - "name": "bus", - "description": "What a bus upgrade would do: the fingerprint of the bus module each node runs against the indexer's newest, and the last bus steps from the hand-act log and the assigner's puts of the bus. Read-only: the step itself is the put on the operator's warrant.", - "input": { - "properties": {}, - "type": "object" - }, - "answer": { - "type": "object", - "description": "what a bus upgrade would do, read only" - } - } - ], - "events": [ - { - "name": "policy-changed", - "body": { - "type": "object" - } - } - ], - "family": { - "name": "mesh-wants.events", - "max-age-days": 7 - }, - "states": [ - { - "name": "wants", - "template": "state", - "key-forms": [ - { - "key": "want..", - "value": { - "type": "object", - "description": "a node's want for a module", - "properties": { - "v": { - "type": "integer" - }, - "fp": { - "type": "string" - }, - "composed": { - "type": "string" - }, - "removed": { - "type": "boolean" - }, - "newest": { - "type": "string" - }, - "rule": { - "type": "integer", - "description": "the rule that holds the node back, 1 to 10" - }, - "backup": { - "type": "string", - "description": "again or skip", - "enum": [ - "again", - "skip" - ] - }, - "words": { - "type": "string" - }, - "put": { - "type": "boolean" - }, - "hold": { - "type": "boolean" - }, - "yes": { - "type": "string" - }, - "release": { - "type": "string" - }, - "self": { - "type": "boolean" - } - } - }, - "description": "a node's want for a module" - }, - { - "key": "want..node", - "value": { - "type": "object", - "description": "a node's own want", - "properties": { - "v": { - "type": "integer" - }, - "composed": { - "type": "string" - }, - "words": { - "type": "string" - }, - "self": { - "type": "boolean" - } - }, - "required": [ - "composed" - ] - }, - "description": "a node's own want, naming the digest of its own composition" - }, - { - "key": "held..", - "value": { - "type": "object", - "description": "a fingerprint the assigner holds", - "properties": { - "v": { - "type": "integer" - }, - "node": { - "type": "string" - }, - "words": { - "type": "string" - } - }, - "required": [ - "node" - ] - }, - "description": "a module's fingerprint the assigner holds" - } - ], - "history": 20, - "description": "what each node should run, and the fingerprints held back" - }, - { - "name": "differences", - "template": "state", - "key-forms": [ - { - "key": ".", - "value": { - "type": "object", - "description": "the difference between a want and what is sent", - "properties": { - "v": { - "type": "integer" - }, - "want": { - "type": "string" - }, - "want-removed": { - "type": "boolean" - }, - "sent": { - "type": "string" - }, - "sent-build": { - "type": "string" - }, - "rule": { - "type": "integer" - }, - "unexplained": { - "type": "boolean" - }, - "words": { - "type": "string" - } - } - }, - "description": "a module on a node" - } - ], - "history": 64, - "description": "differences between a want and what the node-engine is sent" - }, - { - "name": "compositions", - "template": "state", - "key-forms": [ - { - "key": ".", - "value": { - "type": "object", - "description": "the composition verdict of a group's check at its heads", - "properties": { - "v": { - "type": "integer" - }, - "group": { - "type": "string" - }, - "heads": { - "type": "string", - "description": "12 lowercase hex digits" - }, - "state": { - "type": "string", - "description": "pass, fail or waits" - }, - "words": { - "type": "string" - }, - "sdk": { - "type": "string" - }, - "machines": { - "type": "array", - "items": { - "type": "string" - } - }, - "machine": { - "type": "string" - }, - "warnings": { - "type": "array", - "items": { - "type": "string" - } - }, - "took": { - "type": "integer", - "description": "milliseconds" - } - }, - "required": [ - "group", - "heads", - "state" - ] - }, - "description": "a group's check at its heads" - } - ], - "history": 5, - "description": "composition verdicts of a group's check; the checker reads them" - } - ] -} diff --git a/placement/ts/seats/meshmoduleassigner/package-lock.json b/placement/ts/seats/meshmoduleassigner/package-lock.json deleted file mode 100644 index b346586..0000000 --- a/placement/ts/seats/meshmoduleassigner/package-lock.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "lockfileVersion": 3, - "name": "@novox/mesh-placement-contracts-mesh-module-assigner", - "packages": { - "": { - "name": "@novox/mesh-placement-contracts-mesh-module-assigner", - "version": "0.1.0" - } - }, - "requires": true, - "version": "0.1.0" -} diff --git a/placement/ts/seats/meshwants/package.json b/placement/ts/seats/meshwants/package.json deleted file mode 100644 index d4a29ee..0000000 --- a/placement/ts/seats/meshwants/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "description": "The contract of the seat mesh-wants, generated from its definition.", - "exports": { - ".": "./src/index.ts" - }, - "files": [ - "src" - ], - "name": "@novox/mesh-placement-contracts-mesh-wants", - "type": "module", - "types": "./src/index.ts", - "version": "0.1.0" -} diff --git a/placement/ts/seats/meshwants/src/index.ts b/placement/ts/seats/meshwants/src/index.ts deleted file mode 100644 index 38dab79..0000000 --- a/placement/ts/seats/meshwants/src/index.ts +++ /dev/null @@ -1,211 +0,0 @@ -// Code generated by seatgen from the seat definitions; DO NOT EDIT. - -// The contract of the seat "mesh-wants", defined whole (seats/mesh-wants/seat.json): The seat that says what each node should run and whether each module's new builds wait for a person (ADR 0565, ADR 0539). - -export const seat = "mesh-wants"; - -export const verbs = { - upgrade: "upgrade", - bus: "bus", -} as const; - -export const events = { - policyChanged: "policy-changed", -} as const; - -export const family = { name: "mesh-wants.events", maxAgeDays: 7 } as const; - -export const policyChangedSubject = "mesh.seat.mesh-wants.event.policy-changed"; - -export const states = { - wants: "wants", - differences: "differences", - compositions: "compositions", -} as const; - -/** The key of one value of the state wants under the form "want..". */ -export function wantsByWantNodeModuleKey(node: string, module: string): string { - return "want." + node + "." + module; -} - -/** The parts of a key of the form "want..", or undefined for a key of another form. */ -export function parseWantsByWantNodeModuleKey(_key: string): { node: string; module: string } | undefined { - let _rest = _key; - if (!_rest.startsWith("want.")) return undefined; - _rest = _rest.slice(5); - const _at1 = _rest.indexOf("."); - if (_at1 <= 0) return undefined; - const node = _rest.slice(0, _at1); - _rest = _rest.slice(_at1 + 1); - if (_rest === "") return undefined; - const module = _rest; - _rest = ""; - if (_rest !== "") return undefined; - return { node, module }; -} - -/** The key of one value of the state wants under the form "want..node". */ -export function wantsByWantNodeNodeKey(node: string): string { - return "want." + node + ".node"; -} - -/** The parts of a key of the form "want..node", or undefined for a key of another form. */ -export function parseWantsByWantNodeNodeKey(_key: string): { node: string } | undefined { - let _rest = _key; - if (!_rest.startsWith("want.")) return undefined; - _rest = _rest.slice(5); - const _at1 = _rest.indexOf(".node"); - if (_at1 <= 0) return undefined; - const node = _rest.slice(0, _at1); - _rest = _rest.slice(_at1 + 5); - if (_rest !== "") return undefined; - return { node }; -} - -/** The key of one value of the state wants under the form "held..". */ -export function wantsByHeldModuleFingerprintKey(module: string, fingerprint: string): string { - return "held." + module + "." + fingerprint; -} - -/** The parts of a key of the form "held..", or undefined for a key of another form. */ -export function parseWantsByHeldModuleFingerprintKey(_key: string): { module: string; fingerprint: string } | undefined { - let _rest = _key; - if (!_rest.startsWith("held.")) return undefined; - _rest = _rest.slice(5); - const _at1 = _rest.indexOf("."); - if (_at1 <= 0) return undefined; - const module = _rest.slice(0, _at1); - _rest = _rest.slice(_at1 + 1); - if (_rest === "") return undefined; - const fingerprint = _rest; - _rest = ""; - if (_rest !== "") return undefined; - return { module, fingerprint }; -} - -/** The key of one value of the state differences under the form ".". */ -export function differencesByNodeModuleKey(node: string, module: string): string { - return node + "." + module; -} - -/** The parts of a key of the form ".", or undefined for a key of another form. */ -export function parseDifferencesByNodeModuleKey(_key: string): { node: string; module: string } | undefined { - let _rest = _key; - const _at0 = _rest.indexOf("."); - if (_at0 <= 0) return undefined; - const node = _rest.slice(0, _at0); - _rest = _rest.slice(_at0 + 1); - if (_rest === "") return undefined; - const module = _rest; - _rest = ""; - if (_rest !== "") return undefined; - return { node, module }; -} - -/** The key of one value of the state compositions under the form ".". */ -export function compositionsByGroupHeadsKey(group: string, heads: string): string { - return group + "." + heads; -} - -/** The parts of a key of the form ".", or undefined for a key of another form. */ -export function parseCompositionsByGroupHeadsKey(_key: string): { group: string; heads: string } | undefined { - let _rest = _key; - const _at0 = _rest.indexOf("."); - if (_at0 <= 0) return undefined; - const group = _rest.slice(0, _at0); - _rest = _rest.slice(_at0 + 1); - if (_rest === "") return undefined; - const heads = _rest; - _rest = ""; - if (_rest !== "") return undefined; - return { group, heads }; -} - -/** is what upgrade takes. */ -export interface UpgradeInput { - /** "true": each node's own composition, by its digest, and the assigner's findings */ - "compare"?: "true" | "false"; - /** one module */ - "module"?: string; - /** with module: roll, together, record, or default to take a person's choice back */ - "policy"?: string; - /** with policy: why, required and kept */ - "why"?: string; -} - -/** is what upgrade answers. */ -export type UpgradeAnswer = Record; - -/** is what bus takes. */ -export interface BusInput { -} - -/** is what bus answers. */ -export type BusAnswer = Record; - -/** is the body of the event policy-changed. */ -export type PolicyChangedEvent = Record; - -/** is one value of the state wants under the key want... */ -export interface WantsByWantNodeModuleValue { - /** again or skip */ - "backup"?: "again" | "skip"; - "composed"?: string; - "fp"?: string; - "hold"?: boolean; - "newest"?: string; - "put"?: boolean; - "release"?: string; - "removed"?: boolean; - /** the rule that holds the node back, 1 to 10 */ - "rule"?: number; - "self"?: boolean; - "v"?: number; - "words"?: string; - "yes"?: string; -} - -/** is one value of the state wants under the key want..node. */ -export interface WantsByWantNodeNodeValue { - "composed": string; - "self"?: boolean; - "v"?: number; - "words"?: string; -} - -/** is one value of the state wants under the key held... */ -export interface WantsByHeldModuleFingerprintValue { - "node": string; - "v"?: number; - "words"?: string; -} - -/** is one value of the state differences under the key .. */ -export interface DifferencesByNodeModuleValue { - "rule"?: number; - "sent"?: string; - "sent-build"?: string; - "unexplained"?: boolean; - "v"?: number; - "want"?: string; - "want-removed"?: boolean; - "words"?: string; -} - -/** is one value of the state compositions under the key .. */ -export interface CompositionsByGroupHeadsValue { - "group": string; - /** 12 lowercase hex digits */ - "heads": string; - "machine"?: string; - "machines"?: string[]; - "sdk"?: string; - /** pass, fail or waits */ - "state": string; - /** milliseconds */ - "took"?: number; - "v"?: number; - "warnings"?: string[]; - "words"?: string; -} - diff --git a/placement/ts/seats/meshwants/tsconfig.json b/placement/ts/seats/meshwants/tsconfig.json deleted file mode 100644 index 60274c6..0000000 --- a/placement/ts/seats/meshwants/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "noEmit": true, - "strict": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true - }, - "include": ["src/**/*.ts"] -} diff --git a/seats/index.json b/seats/index.json index 6d96506..1b83c4b 100644 --- a/seats/index.json +++ b/seats/index.json @@ -21,6 +21,11 @@ "node-lock-screen", "node-clipboard", "node-bar", - "node-compositor" + "node-compositor", + "mesh-module-assigner", + "node-service-manager", + "node-package-manager", + "node-container-runtime", + "node-environment" ] } diff --git a/placement/seats/mesh-module-assigner/fixtures/events.json b/seats/mesh-module-assigner/fixtures/events.json similarity index 100% rename from placement/seats/mesh-module-assigner/fixtures/events.json rename to seats/mesh-module-assigner/fixtures/events.json diff --git a/placement/seats/mesh-module-assigner/fixtures/states.json b/seats/mesh-module-assigner/fixtures/states.json similarity index 94% rename from placement/seats/mesh-module-assigner/fixtures/states.json rename to seats/mesh-module-assigner/fixtures/states.json index 4c176ff..04e940b 100644 --- a/placement/seats/mesh-module-assigner/fixtures/states.json +++ b/seats/mesh-module-assigner/fixtures/states.json @@ -1,7 +1,7 @@ [ { "name": "a node's key", - "of": "state:placement/.node", + "of": "state:assignments/.node", "given": { "v": 1, "node": "ace", @@ -18,7 +18,7 @@ }, { "name": "a node that came with a tunnel", - "of": "state:placement/.node", + "of": "state:assignments/.node", "given": { "v": 1, "node": "ace", @@ -40,7 +40,7 @@ }, { "name": "a module's key", - "of": "state:placement/.", + "of": "state:assignments/.", "given": { "v": 1, "node": "ace", diff --git a/placement/seats/mesh-module-assigner/seat.json b/seats/mesh-module-assigner/seat.json similarity index 97% rename from placement/seats/mesh-module-assigner/seat.json rename to seats/mesh-module-assigner/seat.json index 77108a2..4e49210 100644 --- a/placement/seats/mesh-module-assigner/seat.json +++ b/seats/mesh-module-assigner/seat.json @@ -3,13 +3,13 @@ "seat": "mesh-module-assigner", "scope": "mesh", "grain": "whole", - "domain": "Placement", + "domain": "Core", "decision": "novox/hq ADR 0567, ADR 0634, ADR 0635", "description": "The seat that places modules on nodes (ADR 0567): the state of placement and the proposals that change it. Renamed from mesh-placement (ADR 0634 §9).", "verbs": [ { - "name": "placement", - "description": "Read placement: one node's key, the nodes one module is placed on, or every key, each with its revision, and how full the state is against its cap. With history, one node's or one module's changes in order: a revision the state keeps whole, an older one as the proposal's changes from its placement-changed event.", + "name": "assignments", + "description": "Read the assignments: one node's key, the nodes one module is placed on, or every key, each with its revision, and how full the state is against its cap. With history, one node's or one module's changes in order: a revision the state keeps whole, an older one as the proposal's changes from its placement-changed event.", "optional": true, "input": { "properties": { @@ -188,7 +188,7 @@ }, "states": [ { - "name": "placement", + "name": "assignments", "template": "state", "key-forms": [ { @@ -276,7 +276,7 @@ } ], "history": 64, - "description": "placements, one key per node and one per module placed on it, written only on the operator's Approve of a proposal by compare-and-set at the revision it was laid over" + "description": "assignments, one key per node and one per module placed on it, written only on the operator's Approve of a proposal by compare-and-set at the revision it was laid over" }, { "name": "proposals", diff --git a/placement/seats/node-container-runtime/seat.json b/seats/node-container-runtime/seat.json similarity index 85% rename from placement/seats/node-container-runtime/seat.json rename to seats/node-container-runtime/seat.json index 1b1bf02..8346a78 100644 --- a/placement/seats/node-container-runtime/seat.json +++ b/seats/node-container-runtime/seat.json @@ -3,6 +3,6 @@ "seat": "node-container-runtime", "scope": "node", "grain": "seat-list", - "domain": "Placement", + "domain": "Core", "decision": "novox/hq ADR 0166, ADR 0207" } diff --git a/placement/seats/node-environment/seat.json b/seats/node-environment/seat.json similarity index 83% rename from placement/seats/node-environment/seat.json rename to seats/node-environment/seat.json index 75bb5d3..6a73957 100644 --- a/placement/seats/node-environment/seat.json +++ b/seats/node-environment/seat.json @@ -3,6 +3,6 @@ "seat": "node-environment", "scope": "node", "grain": "seat-list", - "domain": "Placement", + "domain": "Core", "decision": "novox/hq ADR 0203" } diff --git a/placement/seats/node-package-manager/seat.json b/seats/node-package-manager/seat.json similarity index 83% rename from placement/seats/node-package-manager/seat.json rename to seats/node-package-manager/seat.json index 034e878..b5fec3a 100644 --- a/placement/seats/node-package-manager/seat.json +++ b/seats/node-package-manager/seat.json @@ -3,6 +3,6 @@ "seat": "node-package-manager", "scope": "node", "grain": "seat-list", - "domain": "Placement", + "domain": "Core", "decision": "novox/hq ADR 0207" } diff --git a/placement/seats/node-service-manager/seat.json b/seats/node-service-manager/seat.json similarity index 99% rename from placement/seats/node-service-manager/seat.json rename to seats/node-service-manager/seat.json index 695bc00..c46eeff 100644 --- a/placement/seats/node-service-manager/seat.json +++ b/seats/node-service-manager/seat.json @@ -3,7 +3,7 @@ "seat": "node-service-manager", "scope": "node", "grain": "seat-list", - "domain": "Placement", + "domain": "Core", "decision": "novox/hq ADR 0177", "verbs": [ { diff --git a/placement/ts/seats/meshwants/package-lock.json b/ts/seats/meshmoduleassigner/package-lock.json similarity index 53% rename from placement/ts/seats/meshwants/package-lock.json rename to ts/seats/meshmoduleassigner/package-lock.json index e274634..c05cbfb 100644 --- a/placement/ts/seats/meshwants/package-lock.json +++ b/ts/seats/meshmoduleassigner/package-lock.json @@ -1,9 +1,9 @@ { "lockfileVersion": 3, - "name": "@novox/mesh-placement-contracts-mesh-wants", + "name": "@novox/mesh-contracts-mesh-module-assigner", "packages": { "": { - "name": "@novox/mesh-placement-contracts-mesh-wants", + "name": "@novox/mesh-contracts-mesh-module-assigner", "version": "0.1.0" } }, diff --git a/placement/ts/seats/meshmoduleassigner/package.json b/ts/seats/meshmoduleassigner/package.json similarity index 78% rename from placement/ts/seats/meshmoduleassigner/package.json rename to ts/seats/meshmoduleassigner/package.json index 5c25c3f..e7e83e9 100644 --- a/placement/ts/seats/meshmoduleassigner/package.json +++ b/ts/seats/meshmoduleassigner/package.json @@ -6,7 +6,7 @@ "files": [ "src" ], - "name": "@novox/mesh-placement-contracts-mesh-module-assigner", + "name": "@novox/mesh-contracts-mesh-module-assigner", "type": "module", "types": "./src/index.ts", "version": "0.1.0" diff --git a/placement/ts/seats/meshmoduleassigner/src/index.ts b/ts/seats/meshmoduleassigner/src/index.ts similarity index 93% rename from placement/ts/seats/meshmoduleassigner/src/index.ts rename to ts/seats/meshmoduleassigner/src/index.ts index 116f6b5..062c37d 100644 --- a/placement/ts/seats/meshmoduleassigner/src/index.ts +++ b/ts/seats/meshmoduleassigner/src/index.ts @@ -5,7 +5,7 @@ export const seat = "mesh-module-assigner"; export const verbs = { - placement: "placement", + assignments: "assignments", proposals: "proposals", propose: "propose", withdraw: "withdraw", @@ -30,20 +30,20 @@ export const placementJudgedSubject = "mesh.seat.mesh-module-assigner.event.plac export const placementChangedSubject = "mesh.seat.mesh-module-assigner.event.placement-changed"; export const states = { - placement: "placement", + assignments: "assignments", proposals: "proposals", candidates: "candidates", pulses: "pulses", seatHolders: "seat-holders", } as const; -/** The key of one value of the state placement under the form ".node". */ -export function placementByNodeNodeKey(node: string): string { +/** The key of one value of the state assignments under the form ".node". */ +export function assignmentsByNodeNodeKey(node: string): string { return node + ".node"; } /** The parts of a key of the form ".node", or undefined for a key of another form. */ -export function parsePlacementByNodeNodeKey(_key: string): { node: string } | undefined { +export function parseAssignmentsByNodeNodeKey(_key: string): { node: string } | undefined { let _rest = _key; const _at0 = _rest.indexOf(".node"); if (_at0 <= 0) return undefined; @@ -53,13 +53,13 @@ export function parsePlacementByNodeNodeKey(_key: string): { node: string } | un return { node }; } -/** The key of one value of the state placement under the form ".". */ -export function placementByNodeModuleKey(node: string, module: string): string { +/** The key of one value of the state assignments under the form ".". */ +export function assignmentsByNodeModuleKey(node: string, module: string): string { return node + "." + module; } /** The parts of a key of the form ".", or undefined for a key of another form. */ -export function parsePlacementByNodeModuleKey(_key: string): { node: string; module: string } | undefined { +export function parseAssignmentsByNodeModuleKey(_key: string): { node: string; module: string } | undefined { let _rest = _key; const _at0 = _rest.indexOf("."); if (_at0 <= 0) return undefined; @@ -381,8 +381,8 @@ export interface Warrant { "warrant": string; } -/** is what placement takes. */ -export interface PlacementInput { +/** is what assignments takes. */ +export interface AssignmentsInput { /** "true": the changes in order, from the stream and the state's revisions */ "history"?: "true" | "false"; /** one module: the nodes it is placed on */ @@ -393,8 +393,8 @@ export interface PlacementInput { "seats"?: "true" | "false"; } -/** is what placement answers. */ -export type PlacementAnswer = Record; +/** is what assignments answers. */ +export type AssignmentsAnswer = Record; /** is what proposals takes. */ export interface ProposalsInput { @@ -450,25 +450,25 @@ export type PlacementJudgedEvent = PlacementEvent; /** is the body of the event placement-changed. */ export type PlacementChangedEvent = PlacementEvent; -export interface PlacementByNodeNodeValueModulesItem { +export interface AssignmentsByNodeNodeValueModulesItem { /** the digest of the module key's value it stands for */ "digest": string; "module": string; } -/** is one value of the state placement under the key .node. */ -export interface PlacementByNodeNodeValue { +/** is one value of the state assignments under the key .node. */ +export interface AssignmentsByNodeNodeValue { "decided": NodeDecided; "holds"?: Holding[]; /** the modules placed, in the order the composition reads them */ - "modules": PlacementByNodeNodeValueModulesItem[]; + "modules": AssignmentsByNodeNodeValueModulesItem[]; "node": string; "pins"?: Record; "v"?: number; } -/** is one value of the state placement under the key .. */ -export interface PlacementByNodeModuleValue { +/** is one value of the state assignments under the key .. */ +export interface AssignmentsByNodeModuleValue { "node": string; "placed": PlacedModule; /** the value it replaces while a write is under way */ diff --git a/placement/ts/seats/meshmoduleassigner/tsconfig.json b/ts/seats/meshmoduleassigner/tsconfig.json similarity index 100% rename from placement/ts/seats/meshmoduleassigner/tsconfig.json rename to ts/seats/meshmoduleassigner/tsconfig.json