The node-engine owns the composition it alone runs (H1, H2)
ADR 0593 §1 and §7: compose, its bus reader and runtime reader, takein, validate, the overlay generator, the composer's run and mesh-compose, and the judgement of a move leave mesh-sdk for the node-engine's internal packages, so no other module composes and a change to them rebuilds only the node-engine. The shapes others read (the composer's request and verdict, the overlay's names, compositions and answers) stay in mesh-sdk. The node's grant is now a golden file of what the grants part composes, since the composer of the bus's user list leaves mesh-sdk too. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
package compose
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
)
|
||||
|
||||
// Rendered is everything a node's declaration is composed with (plan.go renderingFor), over an Input: the place half (Places)
|
||||
// and the credential half. Nothing was allocated, made, kept or written; what a send would do is returned for the caller to do.
|
||||
type Rendered struct {
|
||||
Rendering catalogue.Rendering
|
||||
// ForeseenPorts are the ports the mesh may assign that the Input holds none for ("<module>:<port>").
|
||||
ForeseenPorts []string
|
||||
// Foreseen are the stand-ins the composition used for what the Input does not hold: an own secret the next send makes
|
||||
// ("<module>/<name>"), a grant's sealed copy, the certificate and the bus's user list.
|
||||
Foreseen []string
|
||||
// CoreBoxesWanted are the core credentials whose box the send must ask the vault for and keep.
|
||||
CoreBoxesWanted []CoreBoxWanted
|
||||
// Withheld are the consumers the grants leave out because their identity overflows the provision's bound (novox/hq ADR 0225),
|
||||
// and Unbound those whose credential from this node is on record and whose own resolution binds them elsewhere (issue
|
||||
// 274). The controller says them; a check refuses a change that adds one (ADR 0446 clause 1).
|
||||
Withheld []catalogue.Overflow
|
||||
Unbound []catalogue.Unbound
|
||||
// Said are the lines the controller says while composing (the bus users left out for want of a credential).
|
||||
Said []string
|
||||
}
|
||||
|
||||
// Render composes the rendering of a node: the grants it must create, where it puts its ports, its own secrets, the vault's boxes,
|
||||
// its certificate, the names of the mesh, the bus's user list and its membership. The refusals are the controller's, in its words;
|
||||
// where more than one fault stands, the first reported may differ from the controller's, which interleaves the halves.
|
||||
func Render(in Input, node string, mode Mode) (Rendered, error) {
|
||||
return newComposer(in).render(node, mode)
|
||||
}
|
||||
|
||||
// Composing is Resolve, Render and Generators of one node over one composition, which resolves each node of the mesh
|
||||
// once for all three (issue 1954: three compositions resolved the mesh again each, 11.7 s for one node's plans). Each
|
||||
// answers as it does alone; the first refusal ends it, in that order, with what follows it empty.
|
||||
func Composing(in Input, node string, mode Mode) (Resolved, Rendered, map[string]catalogue.Generator, error) {
|
||||
c := newComposer(in)
|
||||
resolved, err := c.planFor(node)
|
||||
if err != nil {
|
||||
return Resolved{}, Rendered{}, nil, err
|
||||
}
|
||||
rendered, err := c.render(node, mode)
|
||||
if err != nil {
|
||||
return resolved, Rendered{}, nil, err
|
||||
}
|
||||
gens, err := c.generators()
|
||||
if err != nil {
|
||||
return resolved, rendered, nil, err
|
||||
}
|
||||
return resolved, rendered, gens, nil
|
||||
}
|
||||
|
||||
func (c *composer) render(nodeName string, mode Mode) (Rendered, error) {
|
||||
resolved, err := c.planFor(nodeName)
|
||||
if err != nil {
|
||||
return Rendered{}, err
|
||||
}
|
||||
node, err := c.in.node(nodeName)
|
||||
if err != nil {
|
||||
return Rendered{}, err
|
||||
}
|
||||
plan := resolved.Plan
|
||||
var out Rendered
|
||||
foreseen := func(s string) { out.Foreseen = append(out.Foreseen, s) }
|
||||
|
||||
grants, withheld, unbound, grantsForeseen, err := c.grantsFor(nodeName)
|
||||
if err != nil {
|
||||
return Rendered{}, err
|
||||
}
|
||||
placed, err := c.places(nodeName, resolved)
|
||||
if err != nil {
|
||||
return Rendered{}, err
|
||||
}
|
||||
r := placed.Rendering
|
||||
out.ForeseenPorts = placed.ForeseenPorts
|
||||
// A plan carries its contributions' machine ports, refused where the placement assigned none (issue 774).
|
||||
r.Planned = mode == Planning
|
||||
r.Grants, r.Withheld, r.Unbound = grants, withheld, unbound
|
||||
out.Withheld, out.Unbound = withheld, unbound
|
||||
for _, g := range grantsForeseen {
|
||||
foreseen(g)
|
||||
}
|
||||
|
||||
// And each module's own secrets: a module handed to the provisioner has its secrets from the vault through the client.
|
||||
handing := catalogue.Rendering{Hands: r.Hands, Provisioner: r.Provisioner, Assigned: r.Assigned}
|
||||
needed, foreseenOwn, err := c.ownSecretsFor(node, plan, handing, mode)
|
||||
if err != nil {
|
||||
return Rendered{}, err
|
||||
}
|
||||
r.Needed = needed
|
||||
if mode == Foreseeing || mode == Planning || len(foreseenOwn) > 0 {
|
||||
r.Foreseen = foreseenOwn
|
||||
}
|
||||
var names []string
|
||||
for module, secrets := range foreseenOwn {
|
||||
for name := range secrets {
|
||||
names = append(names, module+"/"+name)
|
||||
}
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, n := range names {
|
||||
foreseen(n)
|
||||
}
|
||||
|
||||
// The core's own credentials as the vault sealed them, for an engine that opens the vault's box.
|
||||
if node.Stated && node.Contract >= VaultBoxContract {
|
||||
sealed, notPlaced, wanted := c.coreBoxesFor(node, plan.Modules, handing)
|
||||
r.VaultSealed, r.NotPlaced = sealed, notPlaced
|
||||
out.CoreBoxesWanted = wanted
|
||||
}
|
||||
|
||||
// And a certificate for this machine's name inside the mesh, when anything on it asks.
|
||||
for _, m := range plan.Modules {
|
||||
if m.Certificate == nil {
|
||||
continue
|
||||
}
|
||||
certificate, authority, stood, err := c.certificateFor(node)
|
||||
if err != nil {
|
||||
return Rendered{}, err
|
||||
}
|
||||
r.Certificate, r.Authority = certificate, authority
|
||||
if stood {
|
||||
foreseen("the certificate and the authority of " + nodeName)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// The bus's user list, for the machine that runs the bus: names and hashes the Input carries, never passwords.
|
||||
if holdsTheBus(plan.Modules) {
|
||||
r.BusUsers = c.in.BusUsers
|
||||
if r.BusUsers == "" {
|
||||
r.BusUsers = ForeseenBusUsers
|
||||
foreseen(BusUsersStandIn)
|
||||
}
|
||||
if len(c.in.BusMissing) > 0 {
|
||||
out.Said = append(out.Said, fmt.Sprintf("the bus's user list leaves out %d user(s) the mesh has minted no credential "+
|
||||
"for: %s. Each is a user that cannot connect until one is issued", len(c.in.BusMissing), strings.Join(c.in.BusMissing, ", ")))
|
||||
}
|
||||
}
|
||||
r.BusMembership = c.in.BusMemberships[nodeName]
|
||||
out.Rendering = r
|
||||
// And what the composition found on the way, which the controller writes to its log: a machine not counted as on the network.
|
||||
out.Said = c.saidBeside(out.Said)
|
||||
return out, nil
|
||||
}
|
||||
Reference in New Issue
Block a user