129 lines
4.8 KiB
Go
129 lines
4.8 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
|
|
// 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
|
|
}
|