The node-engine applies only a composition signed by the controller or the operator, and when the controller cannot compose its own fix the operator composes, signs and writes one at the terminal (ADR 0539 clauses 3 and 10).
95 lines
3.0 KiB
Go
95 lines
3.0 KiB
Go
package compose
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"sort"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/declaration"
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
)
|
|
|
|
// Compositions is one node's compositions (novox/hq ADR 0539 clauses 1 and 2): its declaration composed over in, as
|
|
// the controller composes it, split into each module's composition at the fingerprint fps gives it and the node's own
|
|
// (`<node>.node`), by key (delivery.ComposedKey). Each resource says its module, whatever the node reported it reads.
|
|
// A module fps gives no fingerprint for is not composed and is named in skipped, so a caller composing one module
|
|
// (mesh-compose) gets that one. Each module's Data says whether its manifest declares data. The compositions are not
|
|
// signed and not yet by digest: the writer does both (delivery.SignComposed, Composed.ByDigest).
|
|
func Compositions(in Input, node string, fps map[string]string, mode Mode) (out map[string]delivery.Composed, skipped []string, err error) {
|
|
reading := in
|
|
reading.Nodes = append([]Node(nil), in.Nodes...)
|
|
found := false
|
|
for i := range reading.Nodes {
|
|
if reading.Nodes[i].Name == node {
|
|
reading.Nodes[i].ReadsModules = true
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
return nil, nil, fmt.Errorf("compose: the mesh has no node %q", node)
|
|
}
|
|
d, problems, err := ComposeIn(reading, node, mode)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
if len(problems) > 0 {
|
|
return nil, nil, fmt.Errorf("compose: %s's declaration is refused by the node-engine's validator: %v", node, problems)
|
|
}
|
|
body, err := d.Body()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
var envelope struct {
|
|
Resources []json.RawMessage `json:"resources"`
|
|
Adoption *declaration.Adoption `json:"adoption"`
|
|
Filled []string `json:"filled_files"`
|
|
Provisioner string `json:"provisioner_account"`
|
|
}
|
|
if err := json.Unmarshal(body, &envelope); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
var kept []json.RawMessage
|
|
keptPaths := map[string]bool{}
|
|
left := map[string]bool{}
|
|
for _, raw := range envelope.Resources {
|
|
var head struct {
|
|
Module string `json:"module"`
|
|
Type string `json:"type"`
|
|
Path string `json:"path"`
|
|
}
|
|
if err := json.Unmarshal(raw, &head); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
if _, ok := fps[head.Module]; head.Module != "" && !ok {
|
|
left[head.Module] = true
|
|
continue
|
|
}
|
|
if head.Type == string(declaration.TypeFile) {
|
|
keptPaths[head.Path] = true
|
|
}
|
|
kept = append(kept, raw)
|
|
}
|
|
var filled []string
|
|
for _, p := range envelope.Filled {
|
|
if keptPaths[p] {
|
|
filled = append(filled, p)
|
|
}
|
|
}
|
|
out, err = delivery.SplitComposed(node, kept, fps, filled,
|
|
delivery.NodeOwn{Adoption: envelope.Adoption, Provisioner: envelope.Provisioner})
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
for key, c := range out {
|
|
if m, ok := in.Catalogue[c.Module]; ok && c.Module != "" {
|
|
c.Data = len(m.DataItems()) > 0
|
|
out[key] = c
|
|
}
|
|
}
|
|
for m := range left {
|
|
skipped = append(skipped, m)
|
|
}
|
|
sort.Strings(skipped)
|
|
return out, skipped, nil
|
|
}
|