Every module follows the assigner's new places: the contracts' go/ path, mesh-wants in the delivery contracts, the state and tool assignments (issue 1966)
The legacy key form placement.<node> is gone from the client, so the readers and their fixtures lose it. The check noplacement now also refuses the assigner's retired names in a manifest. Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
@@ -33,7 +33,7 @@ module node-tools serves on loopback (novox/hq ADR 0152: whoever is on the machi
|
||||
puts the call on the bus, as every agent session on the machine does. The console's address is read from
|
||||
`/etc/mesh-cli/console`, or `MESH_CONSOLE` when set, or node-tools' declared port when the file is not there yet.
|
||||
|
||||
- **Who judges.** The seat that owns the job: a change of placement is a proposal the operator approves
|
||||
- **Who judges.** The seat that owns the job: a change of assignments is a proposal the operator approves
|
||||
(`mesh-module-assigner.propose`), a config value is set with its why (`mesh-config.config`), a silence or a hand act is
|
||||
recorded as one. Until novox/hq issue 1126 the controller ran these lines and judged the account (ADR 0272); with
|
||||
the controller unassigned, that judgement went with it.
|
||||
|
||||
@@ -111,17 +111,17 @@ var commands = []command{
|
||||
// What runs where.
|
||||
{words: []string{"status"}, does: "what is wrong, what is quiet, what is moving (the delivery view)",
|
||||
build: fixed("mesh-delivery-view.view_status")},
|
||||
{words: []string{"nodes"}, does: "the nodes placement holds", build: fixed("mesh-module-assigner.placement"),
|
||||
{words: []string{"nodes"}, does: "the nodes assignments holds", build: fixed("mesh-module-assigner.assignments"),
|
||||
render: renderNodes},
|
||||
{words: []string{"node", "list"}, does: "the nodes placement holds", build: fixed("mesh-module-assigner.placement"),
|
||||
{words: []string{"node", "list"}, does: "the nodes assignments holds", build: fixed("mesh-module-assigner.assignments"),
|
||||
render: renderNodes},
|
||||
{words: []string{"node", "show"}, usage: "<node>", does: "one node's placement: what it runs and its decided fields",
|
||||
build: nodeArg("mesh-module-assigner.placement")},
|
||||
{words: []string{"node", "show"}, usage: "<node>", does: "one node's assignments: what it runs and its decided fields",
|
||||
build: nodeArg("mesh-module-assigner.assignments")},
|
||||
{words: []string{"node", "public-domain"}, usage: "<node> [<domain> | --clear] [--why <text>]",
|
||||
does: "the domain a node composes its routed names under; with a domain or --clear, proposed to placement",
|
||||
does: "the domain a node composes its routed names under; with a domain or --clear, proposed to assignments",
|
||||
allowed: []string{"clear", "why"}, build: publicDomain},
|
||||
{words: []string{"node", "add"}, instead: "a new machine joins through the seat mesh-node-enroller: nox enrol issue <node> --why <text>"},
|
||||
{words: []string{"node", "networks"}, instead: "no seat keeps a node's routed networks yet; mesh-module-assigner.placement shows what a node decides"},
|
||||
{words: []string{"node", "networks"}, instead: "no seat keeps a node's routed networks yet; mesh-module-assigner.assignments shows what a node decides"},
|
||||
{words: []string{"plan"}, usage: "<node>", does: "what the assigner wants the node to run", build: optNode("mesh-delivery-view.assigner_wants")},
|
||||
{words: []string{"haves"}, usage: "[<node>]", does: "what each node runs, as its node-engine said", build: optNode("mesh-delivery-view.node_haves")},
|
||||
{words: []string{"switch"}, does: "what each node runs, as its node-engine said", build: fixed("mesh-delivery-view.node_haves")},
|
||||
@@ -133,7 +133,7 @@ var commands = []command{
|
||||
return call{l.pos[0] + "/node-engine.composition", map[string]any{"module": l.pos[1]}}, nil
|
||||
}},
|
||||
{words: []string{"seats"}, does: "who holds each seat, and where", build: func(line) (call, error) {
|
||||
return call{"mesh-module-assigner.placement", map[string]any{"seats": "true"}}, nil
|
||||
return call{"mesh-module-assigner.assignments", map[string]any{"seats": "true"}}, nil
|
||||
}},
|
||||
{words: []string{"seat"}, instead: "a seat is held by placing its module: nox assign <node> <module> --why <text> (mesh-module-assigner.propose)"},
|
||||
{words: []string{"assign"}, usage: "<node> <module>... --why <text>", does: "propose placing modules on a node",
|
||||
@@ -155,9 +155,9 @@ var commands = []command{
|
||||
}
|
||||
return propose(l, map[string]any{"op": "unpin", "node": l.pos[0], "provision": l.pos[1]})
|
||||
}},
|
||||
{words: []string{"proposals"}, usage: "[<id>]", does: "the open proposals of placement", build: optArg("mesh-module-assigner.proposals", "id")},
|
||||
{words: []string{"overlay", "show"}, does: "every node's placement, with its tunnel", build: fixed("mesh-module-assigner.placement")},
|
||||
{words: []string{"overlay"}, instead: "a node's tunnel is a decided field of its placement: mesh-module-assigner.propose with op decide"},
|
||||
{words: []string{"proposals"}, usage: "[<id>]", does: "the open proposals of assignments", build: optArg("mesh-module-assigner.proposals", "id")},
|
||||
{words: []string{"overlay", "show"}, does: "every node's assignments, with its tunnel", build: fixed("mesh-module-assigner.assignments")},
|
||||
{words: []string{"overlay"}, instead: "a node's tunnel is a decided field of its assignments: mesh-module-assigner.propose with op decide"},
|
||||
|
||||
// Deliveries and builds.
|
||||
{words: []string{"deliveries"}, usage: "[--state s] [--repository owner/repo] [--group g] [--all]",
|
||||
@@ -621,10 +621,10 @@ func numbers(a map[string]any, names ...string) (map[string]any, error) {
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// propose is a change of placement with its why.
|
||||
// propose is a change of assignments with its why.
|
||||
func propose(l line, changes ...map[string]any) (call, error) {
|
||||
if !l.has("why") {
|
||||
return call{}, usageErr("a change of placement needs --why; the operator is asked to approve it")
|
||||
return call{}, usageErr("a change of assignments needs --why; the operator is asked to approve it")
|
||||
}
|
||||
list := make([]any, len(changes))
|
||||
for i, c := range changes {
|
||||
@@ -649,7 +649,7 @@ func placeModules(op string) func(line) (call, error) {
|
||||
func publicDomain(l line) (call, error) {
|
||||
switch {
|
||||
case len(l.pos) == 1 && !l.has("clear"):
|
||||
return call{"mesh-module-assigner.placement", map[string]any{"node": l.pos[0]}}, nil
|
||||
return call{"mesh-module-assigner.assignments", map[string]any{"node": l.pos[0]}}, nil
|
||||
case len(l.pos) == 1:
|
||||
return propose(l, map[string]any{"op": "decide", "node": l.pos[0], "field": "public-domain", "value": nil})
|
||||
case len(l.pos) == 2 && !l.has("clear"):
|
||||
@@ -718,14 +718,14 @@ func addressCall(address string, rest []string) (call, error) {
|
||||
return call{address, a}, nil
|
||||
}
|
||||
|
||||
// renderNodes shapes placement's keys as one line per node: its name, address and site.
|
||||
// renderNodes shapes the assignments' keys as one line per node: its name, address and site.
|
||||
func renderNodes(text string) string {
|
||||
var p struct {
|
||||
Keys map[string]struct {
|
||||
Placement struct {
|
||||
Assignments struct {
|
||||
Decided map[string]any `json:"decided"`
|
||||
Modules []any `json:"modules"`
|
||||
} `json:"placement"`
|
||||
} `json:"assignments"`
|
||||
} `json:"keys"`
|
||||
}
|
||||
// The console says after the answer which machine answered it ("answered by ace"): kept, below the lines.
|
||||
@@ -745,8 +745,8 @@ func renderNodes(text string) string {
|
||||
var b strings.Builder
|
||||
for _, n := range names {
|
||||
k := p.Keys[n]
|
||||
fmt.Fprintf(&b, "%-10s %-15v %-8v %d modules\n", n, orDash(k.Placement.Decided["address"]),
|
||||
orDash(k.Placement.Decided["site"]), len(k.Placement.Modules))
|
||||
fmt.Fprintf(&b, "%-10s %-15v %-8v %d modules\n", n, orDash(k.Assignments.Decided["address"]),
|
||||
orDash(k.Assignments.Decided["site"]), len(k.Assignments.Modules))
|
||||
}
|
||||
if after != "" {
|
||||
b.WriteString(after + "\n")
|
||||
|
||||
@@ -87,13 +87,13 @@ func TestEachLineCallsItsOwner(t *testing.T) {
|
||||
address string
|
||||
args string
|
||||
}{
|
||||
{"node show ace", "mesh-module-assigner.placement", `{"node":"ace"}`},
|
||||
{"node public-domain ace", "mesh-module-assigner.placement", `{"node":"ace"}`},
|
||||
{"node show ace", "mesh-module-assigner.assignments", `{"node":"ace"}`},
|
||||
{"node public-domain ace", "mesh-module-assigner.assignments", `{"node":"ace"}`},
|
||||
{"node public-domain ace example.org --why moved", "mesh-module-assigner.propose", `{"changes":[{"field":"public-domain","node":"ace","op":"decide","value":"example.org"}],"why":"moved"}`},
|
||||
{"plan ace", "mesh-delivery-view.assigner_wants", `{"node":"ace"}`},
|
||||
{"haves", "mesh-delivery-view.node_haves", `{}`},
|
||||
{"composition ace plex", "ace/node-engine.composition", `{"module":"plex"}`},
|
||||
{"seats", "mesh-module-assigner.placement", `{"seats":"true"}`},
|
||||
{"seats", "mesh-module-assigner.assignments", `{"seats":"true"}`},
|
||||
{"assign ace plex sonarr --why=media", "mesh-module-assigner.propose", `{"changes":[{"module":"plex","node":"ace","op":"place"},{"module":"sonarr","node":"ace","op":"place"}],"why":"media"}`},
|
||||
{"unassign ace plex --why gone", "mesh-module-assigner.propose", `{"changes":[{"module":"plex","node":"ace","op":"take-off"}],"why":"gone"}`},
|
||||
{"pin ace postgres-database novox postgres --why one", "mesh-module-assigner.propose", `{"changes":[{"node":"ace","op":"pin","provider":{"module":"postgres","node":"novox"},"provision":"postgres-database"}],"why":"one"}`},
|
||||
@@ -194,9 +194,9 @@ func TestAnswersRefusalsAndSilenceAreSaid(t *testing.T) {
|
||||
if code, out, errOut := runLine("enrol", "tokens"); code != 1 || out != "" || !strings.Contains(errOut, "nothing serves") {
|
||||
t.Errorf("an error answer: exit %d, stdout %q, stderr %q", code, out, errOut)
|
||||
}
|
||||
placement := `{"keys":{"novox":{"placement":{"decided":{"address":"10.10.0.1","site":"hub"},"modules":[{},{}]}},` +
|
||||
`"ace":{"placement":{"decided":{"address":"10.10.0.2"},"modules":[{}]}}}}`
|
||||
console(t, map[string]string{"mesh-module-assigner.placement": placement})
|
||||
assignments := `{"keys":{"novox":{"assignments":{"decided":{"address":"10.10.0.1","site":"hub"},"modules":[{},{}]}},` +
|
||||
`"ace":{"assignments":{"decided":{"address":"10.10.0.2"},"modules":[{}]}}}}`
|
||||
console(t, map[string]string{"mesh-module-assigner.assignments": assignments})
|
||||
if code, out, _ := runLine("nodes"); code != 0 || !strings.HasPrefix(out, "ace ") ||
|
||||
!strings.Contains(out, "10.10.0.1") || !strings.Contains(out, "2 modules") || !strings.HasSuffix(out, "answered by ace\n") {
|
||||
t.Errorf("nodes: exit %d, %q", code, out)
|
||||
|
||||
Reference in New Issue
Block a user