fix9-8: the grants part composes the node-engine's grants for the seat mesh-recovery and for a module's catch-up tool
The node-engine of a node hears the kept events ended and restored through an ordered consumer, and calls the catch-up tool of a module placed on its node (its manifest's kept data name it); the verbs snapshot-kept, restore-kept and kept-points were granted already as the engine's invokes. No nodegrant mirror lives in mesh-delivery (it is mesh-host's). Builds: design 47 G §406, §554, §631, A-108
This commit is contained in:
@@ -85,6 +85,17 @@ func engineGrants(p Principal) (pub, sub []string) {
|
||||
sub = append(sub, k.Filter)
|
||||
}
|
||||
}
|
||||
// And the kept events of other seats it follows (the recovery's ended and restored): each stream read through an ordered
|
||||
// consumer made for the read, with no durable of its own, and the stream's own info (design 47 G §554, A-108).
|
||||
for _, h := range p.EngineHears {
|
||||
if !safeSubject.MatchString(h.Stream) {
|
||||
continue
|
||||
}
|
||||
pub = append(pub, "$JS.API.STREAM.INFO."+h.Stream,
|
||||
"$JS.API.CONSUMER.CREATE."+h.Stream, "$JS.API.CONSUMER.CREATE."+h.Stream+".>",
|
||||
"$JS.API.CONSUMER.MSG.NEXT."+h.Stream+".*", "$JS.API.CONSUMER.INFO."+h.Stream+".*",
|
||||
"$JS.API.CONSUMER.DELETE."+h.Stream+".*")
|
||||
}
|
||||
sub = append(sub, announcing(EngineSeat)...)
|
||||
pub = append(pub, discovering()...)
|
||||
tp, ts := seatTrafficGrants(SeatTrafficOf(occ.EngineAsker(p.Node), nil, p.Uses, nil))
|
||||
|
||||
@@ -124,6 +124,9 @@ type Principal struct {
|
||||
// (engineGrants).
|
||||
EngineEvents map[string]string
|
||||
EngineKept []KeptEvent
|
||||
// EngineHears are the kept events of other seats its engine consumes (design 47 G §554, A-108: the events ended and
|
||||
// restored of the seat mesh-recovery), each stream followed through an ordered consumer made for the read.
|
||||
EngineHears []Heard
|
||||
// Ledgers are the local names of the ledgers its module asks for in `requires`, for a KindLedger principal alone
|
||||
// (issue 1966 D5): its whole authority is publishing on those ledgers' histories and snapshots (LedgerPublishGrants).
|
||||
Ledgers []string
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package buscompose
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Design 47 G §406, §554, A-108: the node-engine of a node is granted to hear the seat mesh-recovery's kept events ended and
|
||||
// restored through an ordered consumer, to ask the verbs snapshot-kept, restore-kept and kept-points of the seat, and to call
|
||||
// the catch-up tool of a module placed on its node, on its own node's tool subject and no other module's tool.
|
||||
func TestTheNodeEngineIsGrantedWhatItNeedsOfTheRecoverySeat(t *testing.T) {
|
||||
kept := []KeptEvent{
|
||||
{Seat: "mesh-recovery", Event: "ended", Stream: "SEAT_mesh-recovery_ended", Filter: "mesh.seat.mesh-recovery.event.ended.*.*"},
|
||||
{Seat: "mesh-recovery", Event: "restored", Stream: "SEAT_mesh-recovery_restored", Filter: "mesh.seat.mesh-recovery.event.restored.*.*"},
|
||||
}
|
||||
records := Records{Nodes: []string{"one"}, Kept: kept,
|
||||
Engine: &Declared{Module: EngineModule, Consumes: []string{"mesh-recovery.ended", "mesh-recovery.restored"},
|
||||
Invokes: []string{"seat:mesh-recovery.snapshot-kept", "seat:mesh-recovery.restore-kept", "seat:mesh-recovery.kept-points"}},
|
||||
Assigned: map[string][]Declared{"one": {{Module: "keeper", CatchUps: []string{"catch_up"}}, {Module: "plain"}}}}
|
||||
users, err := Users(records)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var node Principal
|
||||
for _, u := range users {
|
||||
if u.Kind == KindNode {
|
||||
node = u
|
||||
}
|
||||
}
|
||||
perms, err := PermissionsFor(node)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, want := range []string{
|
||||
"$JS.API.CONSUMER.CREATE.SEAT_mesh-recovery_ended", "$JS.API.CONSUMER.CREATE.SEAT_mesh-recovery_ended.>",
|
||||
"$JS.API.CONSUMER.MSG.NEXT.SEAT_mesh-recovery_ended.*", "$JS.API.CONSUMER.DELETE.SEAT_mesh-recovery_restored.*",
|
||||
"$JS.API.STREAM.INFO.SEAT_mesh-recovery_restored",
|
||||
"mesh.seat.mesh-recovery.tool.snapshot-kept", "mesh.seat.mesh-recovery.tool.restore-kept", "mesh.seat.mesh-recovery.tool.kept-points",
|
||||
"mesh.mod.keeper.tool.catch_up.one",
|
||||
} {
|
||||
if !MayPublish(perms, want) {
|
||||
t.Errorf("the node-engine of one may not publish %s", want)
|
||||
}
|
||||
}
|
||||
for _, bad := range []string{"mesh.mod.plain.tool.catch_up.one", "mesh.mod.keeper.tool.other.one", "$JS.API.CONSUMER.CREATE.SEAT_mesh-recovery_set-aside"} {
|
||||
if MayPublish(perms, bad) {
|
||||
t.Errorf("the node-engine of one may publish %s", bad)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,9 @@ type Declared struct {
|
||||
Watches []Seat
|
||||
// Invokes are the tools it calls, `<module>.<tool>` or `*` (novox/hq ADR 0152).
|
||||
Invokes []string
|
||||
// CatchUps are the module's own tools that bring a kept data forward after a restore of it, as its manifest's kept data
|
||||
// name them (design 47 G §406, §631, A-108): the node-engine on a node that places the module is granted to call each.
|
||||
CatchUps []string
|
||||
// Waits are the event families a session may wait on through the tool runner, the tool runner's module's
|
||||
// alone (novox/hq ADR 0367 §2): its account subscribes them for a call, through no consumer.
|
||||
Waits []Wait
|
||||
@@ -223,9 +226,20 @@ func Users(r Records) ([]Principal, error) {
|
||||
if r.Engine != nil {
|
||||
engineHears, engineWatches, engineInvokes = r.Engine.Consumes, r.Engine.Watches, r.Engine.Invokes
|
||||
}
|
||||
// The node-engine's hearing of kept events of other seats (the recovery's ended and restored) and its call of each
|
||||
// catch-up tool of a module placed on its node (design 47 G §406, §554, A-108), granted as the seat's grants name them.
|
||||
_, engineKeptHears := SplitKept(r.Kept, engineHears)
|
||||
engineInvokes = slices.Clone(engineInvokes)
|
||||
for _, d := range r.Assigned[node] {
|
||||
for _, tool := range d.CatchUps {
|
||||
if t := d.Module + "." + tool; !slices.Contains(engineInvokes, t) {
|
||||
engineInvokes = append(engineInvokes, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, login := range nodeLoginsOf(r, node) {
|
||||
out = append(out, Principal{Kind: KindNode, Node: node, Login: login, Checks: checks, Runner: runner, Consumes: engineHears,
|
||||
Watches: engineWatches, Invokes: engineInvokes, EngineEvents: r.EngineEvents, EngineKept: keptOfSeat(r.Kept, EngineSeat),
|
||||
EngineHears: engineKeptHears, Watches: engineWatches, Invokes: engineInvokes, EngineEvents: r.EngineEvents, EngineKept: keptOfSeat(r.Kept, EngineSeat),
|
||||
MovesIntoTheVault: servesMoveIn(r), StatesItsKeyToTheVault: servesVerb(r, VaultNodeKey),
|
||||
AsksItsOwnSecrets: servesVerb(r, VaultOwn), AsksTheVaultToSign: servesVerb(r, VaultSign), Uses: asksSeatOf(r),
|
||||
FollowsGrantsUserList: bus, ServedReads: servedOn(served, node), RotatesItsLogin: servesVerb(r, VaultRotateNode)})
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package grants
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
)
|
||||
|
||||
// Design 47 G §406, §631, A-108: the catch-up tools a manifest's kept data name are the module's own, each once, whichever
|
||||
// kind of kept data names them.
|
||||
func TestCatchUpsAreTheToolsTheManifestsKeptDataName(t *testing.T) {
|
||||
m, err := catalogue.Decode([]byte(`{"module":"keeper","version":"1.0","state":[{"name":"seen","form":2,"catch-up":{"tool":"seen_again","arguments":{"all":true}}},"plain"],` +
|
||||
`"logs":[{"name":"lines","form":1,"catch-up":"seen_again"}],"streams":[{"name":"ev","events":["a"],"max-age-days":1,"max-mib":1,"form":1,"catch-up":{"tool":"ev_again"}}]}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := catchUpsOf(m); !slices.Equal(got, []string{"seen_again", "ev_again"}) && !slices.Equal(got, []string{"ev_again", "seen_again"}) {
|
||||
t.Errorf("catchUpsOf = %v, want seen_again and ev_again once each", got)
|
||||
}
|
||||
}
|
||||
@@ -156,6 +156,7 @@ func declaredFor(m catalogue.Manifest, seats map[string]catalogue.SeatDeclaratio
|
||||
Ledgers: ledgersOf(m),
|
||||
}
|
||||
d.Loads = loadsABundle(m)
|
||||
d.CatchUps = catchUpsOf(m)
|
||||
if _, reads := m.OwnSecrets["broker"]; !reads {
|
||||
d.NoAccount = true
|
||||
}
|
||||
@@ -608,3 +609,15 @@ func unionFields(dst, src reflect.Value) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// catchUpsOf are the catch-up tools a manifest's kept data name, each once (design 47 G §406, §631): the module's own tools
|
||||
// the node-engine calls after a restore of that data, which it is granted to call on its own node (A-108).
|
||||
func catchUpsOf(m catalogue.Manifest) []string {
|
||||
var tools []string
|
||||
for _, k := range catalogue.KeptDeclarations(m) {
|
||||
if k.CatchUp != nil && k.CatchUp.Tool != "" && !slices.Contains(tools, k.CatchUp.Tool) {
|
||||
tools = append(tools, k.CatchUp.Tool)
|
||||
}
|
||||
}
|
||||
return tools
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user