Where node-tools is in a node's set, the declaration ends with one process: the runtime module's own bundle, run from its one entrypoint by its language's interpreter, told in MESH_TOOL_MODULES every <module>=<file> the machine's bundles load, where its credential is (the module's own broker secret as this node places it), and — on a machine with an operator account — who the operator is, running as that account so a tool that needs root can escalate as the operator would. Restarted when any bundle it loads or the credential changes. A machine with no account runs it as root without the two operator words; a machine without the runtime is sent nothing new. A bundle says which of its entrypoints the runtime LOADS (`loads`), because one bundle may carry a daemon beside its tools and importing the daemon into the runtime would start it there; absent, a module declaring tools has every entrypoint loaded. And the TypeScript toolchain is rooted at the module, so an entrypoint lands at the path it is named by — the runtime loading bundles by their declared paths is what made the compiler's common-directory default visible.
110 lines
5.0 KiB
Go
110 lines
5.0 KiB
Go
package broker
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
// The mesh issues an assignment's subjects (novox/hq ADR 0160): a module alone on one machine
|
|
// answers for the module and for its machine; a stateful module on two machines answers only for
|
|
// each machine; one that says its instances are interchangeable answers for the module everywhere;
|
|
// a holder serves its seat's verbs; and what a module may reach is resolved the same way.
|
|
func TestAMembershipIsIssuedFromWhereEverythingRuns(t *testing.T) {
|
|
records := Records{Assigned: map[string][]Declared{
|
|
"anchor": {
|
|
{Module: "postgres", Serves: []string{"query"}, Holds: []Seat{{Name: "mesh-store", Scope: "mesh", Serves: []string{"databases", "query"}}}},
|
|
{Module: "catalog", Invokes: []string{"postgres.query", "search.find"}},
|
|
},
|
|
"home-server": {
|
|
{Module: "postgres"},
|
|
{Module: "search"},
|
|
{Module: "dashboard", Invokes: []string{"postgres.query"}},
|
|
},
|
|
"laptop": {{Module: "search"}},
|
|
}, Interchangeable: map[string]bool{"search": true}}
|
|
where := PlacementsOf(records, records.Interchangeable)
|
|
|
|
pg := MembershipFor("anchor", records.Assigned["anchor"][0], where)
|
|
if !reflect.DeepEqual(pg.Serves, []Served{{Subject: "mesh.mod.postgres.tool.{tool}.anchor"}}) {
|
|
t.Fatalf("a stateful module on two machines answers only for its machine: %+v", pg.Serves)
|
|
}
|
|
if len(pg.Seats) != 2 || pg.Seats[0].Subject != "mesh.seat.mesh-store.tool.databases" {
|
|
t.Fatalf("the holder serves the seat's verbs at the seat's subjects: %+v", pg.Seats)
|
|
}
|
|
if pg.Emits != "mesh.mod.postgres.event.{event}" || pg.Tools != "mesh.mod.postgres.tool.tools" {
|
|
t.Fatalf("events and the tools verb: %+v", pg)
|
|
}
|
|
|
|
search := MembershipFor("laptop", records.Assigned["laptop"][0], where)
|
|
if !reflect.DeepEqual(search.Serves, []Served{
|
|
{Subject: "mesh.mod.search.tool.{tool}.laptop"},
|
|
{Subject: "mesh.mod.search.tool.{tool}", Queue: "serve.search"},
|
|
}) {
|
|
t.Fatalf("an interchangeable module answers for the module in the queue too: %+v", search.Serves)
|
|
}
|
|
|
|
dashboard := MembershipFor("home-server", records.Assigned["home-server"][2], where)
|
|
if !reflect.DeepEqual(dashboard.Serves, []Served{
|
|
{Subject: "mesh.mod.dashboard.tool.{tool}.home-server"},
|
|
{Subject: "mesh.mod.dashboard.tool.{tool}", Queue: "serve.dashboard"},
|
|
}) {
|
|
t.Fatalf("a module alone on one machine answers for the module: %+v", dashboard.Serves)
|
|
}
|
|
if !reflect.DeepEqual(dashboard.Reaches["postgres.query"],
|
|
[]string{"mesh.mod.postgres.tool.query.anchor", "mesh.mod.postgres.tool.query.home-server"}) {
|
|
t.Fatalf("reaching a stateful module names each machine and no plain subject: %v", dashboard.Reaches)
|
|
}
|
|
catalog := MembershipFor("anchor", records.Assigned["anchor"][1], where)
|
|
if !reflect.DeepEqual(catalog.Reaches["search.find"],
|
|
[]string{"mesh.mod.search.tool.find", "mesh.mod.search.tool.find.home-server", "mesh.mod.search.tool.find.laptop"}) {
|
|
t.Fatalf("reaching an interchangeable module offers the plain subject first: %v", catalog.Reaches)
|
|
}
|
|
if MembershipSubject("anchor", "postgres") != "mesh.assignment.anchor.postgres" {
|
|
t.Fatal("the one subject a runtime derives for itself")
|
|
}
|
|
}
|
|
|
|
func TestAnAccountMayReadItsOwnMembershipAndNoOthers(t *testing.T) {
|
|
perms, err := PermissionsFor(Principal{Kind: KindModule, Node: "anchor", Module: "postgres", PasswordHash: "x"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
has(t, perms.Subscribe, "mesh.assignment.anchor.postgres")
|
|
has(t, perms.Publish, "$JS.API.DIRECT.GET.ASSIGNMENTS.mesh.assignment.anchor.postgres")
|
|
hasNot(t, perms.Subscribe, "mesh.assignment.>")
|
|
}
|
|
|
|
// The runtime arriving on a machine changes nothing about what each module is issued (to-be 38 WP2):
|
|
// the memberships are composed as before and the runtime reads several of them. What the machine's
|
|
// user list gains is one runtime principal, and loses nothing but the runtime module's own.
|
|
func TestTheRuntimeArrivingLeavesEveryMembershipAsItWas(t *testing.T) {
|
|
filter := Seat{Name: "node-packet-filter", Scope: "node", Serves: []string{"rules", "reload"}}
|
|
three := []Declared{
|
|
{Module: "nftables", Holds: []Seat{filter}, Serves: []string{"firewall_rules"}},
|
|
{Module: "zsh", Serves: []string{"execute"}},
|
|
{Module: "systemd", Serves: []string{"units"}},
|
|
}
|
|
before := Records{Nodes: []string{"anchor"}, Assigned: map[string][]Declared{"anchor": three}}
|
|
after := Records{Nodes: []string{"anchor"}, Assigned: map[string][]Declared{
|
|
"anchor": append(append([]Declared{}, three...), Declared{Module: RuntimeModule}),
|
|
}}
|
|
for _, d := range three {
|
|
was := MembershipFor("anchor", d, PlacementsOf(before, nil))
|
|
is := MembershipFor("anchor", d, PlacementsOf(after, nil))
|
|
if !reflect.DeepEqual(was, is) {
|
|
t.Errorf("%s's membership changed when the runtime arrived:\n%+v\n%+v", d.Module, was, is)
|
|
}
|
|
}
|
|
users, err := Users(after)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
kinds := map[Kind]int{}
|
|
for _, p := range users {
|
|
kinds[p.Kind]++
|
|
}
|
|
if kinds[KindNodeTools] != 1 || kinds[KindModule] != 3 || kinds[KindNode] != 1 || kinds[KindController] != 1 {
|
|
t.Errorf("the machine's users are %v; one runtime, the three modules, the host and the controller", kinds)
|
|
}
|
|
}
|