Files
mesh-sdk/go/compose/render.go
T

127 lines
4.6 KiB
Go

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)
}
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
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}
needed, foreseenOwn, err := c.ownSecretsFor(node, plan, handing, mode)
if err != nil {
return Rendered{}, err
}
r.Needed = needed
if mode == Foreseeing || 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("the bus's user list")
}
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
return out, nil
}