Files
mesh-controller/internal/inventory/mirrors.go
T

205 lines
8.4 KiB
Go

package inventory
import (
"bytes"
"context"
"encoding/json"
"fmt"
"time"
)
// The values of the controller's record mirrors, built from the store (novox/hq ADR 0289, design 54 §1,
// design 57 package F3).
//
// **Each value is built from a named list of fields, and nothing else** (design 54 §1): no epoch, no time
// of writing and no time of the last look, so any lease holder builds the same bytes from the same record
// and a pass over unchanged records writes nothing (ADR 0289 §1, §4). The encoding is fixed: the fields in
// their struct's order, a declaration's maps sorted.
// AssignmentMirrored is one assignment as the assignments mirror holds it: the module, the node, and since
// when it is assigned there. Design 54 §1 also lists whether it is pending, the builds sent and running and
// the seats and provisions it holds there; those join with issue 419, which builds the rest of design 54.
type AssignmentMirrored struct {
Node string `json:"node"`
Module string `json:"module"`
Assigned time.Time `json:"assigned,omitzero"`
// RequiredBy is, for a module only pulled in by another and handed to the provisioner on this node (issue 765),
// the module that requires it there; such a value has no time assigned. Absent for an assignment.
RequiredBy string `json:"required-by,omitempty"`
// Provisioning is the plan of a module handed to the provisioner on this node (novox/hq ADR 0349 clause 4,
// design 57 P5): absent for every other, so their values are the bytes they were.
Provisioning *Provisioning `json:"provisioning,omitempty"`
}
// DeclaredConfigMirrored is one registered module's config declarations as the declared-config mirror holds
// them: the module, and each key its manifest declares — under `config`, or under its old name `settings` for a
// module not yet moved (ADR 0348 §1) — with that declaration's listed fields. An empty object when the module
// declares none: registered, declaring nothing.
type DeclaredConfigMirrored struct {
Module string `json:"module"`
Settings map[string]DeclaredMirrored `json:"settings"`
}
// DeclaredMirrored is one key's declaration as the mirror holds it: **its listed fields and no other**
// (ADR 0289 §12), and every field a declaration has (catalogue.SettingDeclaration), under the same names. The
// mirror holds what the module's manifest declares about its config values, and mesh-config validates and
// resolves from it (ADR 0356 §4, ADR 0348 §4, §9), so it carries the whole declaration: kind, default and why
// of the settings form, and type, description, validation, trusted, predefined, required and runtime of the
// config form. None is a secret: a secret lives in the vault, never in config, and the catalogue refuses a
// default for a key whose name says secret. A field the manifest gains reaches the bus only once it is added
// here, deliberately: a test fails until it is listed. The config form's fields are left out when unset, so a
// module not yet moved is mirrored as it was before they were carried.
type DeclaredMirrored struct {
Kind string `json:"kind"`
Default json.RawMessage `json:"default"`
Why string `json:"why"`
Type string `json:"type,omitempty"`
Description string `json:"description,omitempty"`
Enum json.RawMessage `json:"enum,omitempty"`
Minimum *float64 `json:"minimum,omitempty"`
Maximum *float64 `json:"maximum,omitempty"`
Pattern string `json:"pattern,omitempty"`
Trusted bool `json:"trusted,omitempty"`
Predefined string `json:"predefined,omitempty"`
Required bool `json:"required,omitempty"`
Runtime bool `json:"runtime,omitempty"`
}
// DeclaredMirroredFields are the fields of a declaration the mirror carries, by their manifest names.
var DeclaredMirroredFields = []string{"kind", "default", "why", "type", "description", "enum", "minimum", "maximum",
"pattern", "trusted", "predefined", "required", "runtime"}
// AssignmentsMirror is every assignment's mirrored value, keyed `<node>.<module>`, with the provisioner's plan of
// each assignment of a module handed to it, and a value of its own for each module handed to it on a node that
// nothing assigned there, only pulled in by another (issue 765).
func (i *Inventory) AssignmentsMirror(ctx context.Context) (map[string][]byte, error) {
var plans map[string]Provisioning
if i.provisioningPlans != nil {
var err error
if plans, err = i.provisioningPlans(ctx); err != nil {
return nil, fmt.Errorf("composing the provisioner's plans: %w", err)
}
}
assignments, err := i.assignmentsMirrored(ctx)
if err != nil {
return nil, err
}
return AssignmentValues(assignments, plans)
}
func (i *Inventory) assignmentsMirrored(ctx context.Context) ([]AssignmentMirrored, error) {
rows, err := i.store.Pool().Query(ctx,
`select n.name, a.module, a.assigned from assignment a join node n on n.id = a.node`)
if err != nil {
return nil, err
}
defer rows.Close()
var out []AssignmentMirrored
for rows.Next() {
var a AssignmentMirrored
if err := rows.Scan(&a.Node, &a.Module, &a.Assigned); err != nil {
return nil, err
}
a.Assigned = a.Assigned.UTC()
out = append(out, a)
}
return out, rows.Err()
}
// DeclaredConfigMirror is every registered module's mirrored config declarations, keyed by the module.
func (i *Inventory) DeclaredConfigMirror(ctx context.Context) (map[string][]byte, error) {
rows, err := i.store.Pool().Query(ctx,
`select name, coalesce(manifest->'config', manifest->'settings', '{}'::jsonb) from module`)
if err != nil {
return nil, err
}
defer rows.Close()
out := map[string][]byte{}
for rows.Next() {
var (
d DeclaredConfigMirrored
raw []byte
)
if err := rows.Scan(&d.Module, &raw); err != nil {
return nil, err
}
if d.Settings, err = declaredMirrored(raw); err != nil {
return nil, fmt.Errorf("the declarations of %s cannot be read: %w", d.Module, err)
}
value, err := json.Marshal(d)
if err != nil {
return nil, err
}
out[d.Module] = value
}
return out, rows.Err()
}
// declaredMirrored is a manifest's config declarations, keyed by the value, as the mirror holds them: each
// with its listed fields, a default and an enum encoded the one way.
func declaredMirrored(raw []byte) (map[string]DeclaredMirrored, error) {
var declared map[string]DeclaredMirrored
if err := json.Unmarshal(raw, &declared); err != nil {
return nil, err
}
out := map[string]DeclaredMirrored{}
for key, decl := range declared {
var err error
if len(decl.Default) > 0 {
if decl.Default, err = canonical(decl.Default); err != nil {
return nil, err
}
}
if len(decl.Enum) > 0 {
if decl.Enum, err = canonical(decl.Enum); err != nil {
return nil, err
}
}
out[key] = decl
}
return out, nil
}
// canonical is a JSON document encoded the one way: objects' keys sorted, numbers as written.
func canonical(raw []byte) ([]byte, error) {
dec := json.NewDecoder(bytes.NewReader(raw))
dec.UseNumber()
var v any
if err := dec.Decode(&v); err != nil {
return nil, err
}
return json.Marshal(v)
}
// PredefinedMirrorKey is the predefined-keys mirror's one key: the list kept is one record.
const PredefinedMirrorKey = "list"
// PredefinedMirrored is the list of predefined keys kept as its mirror holds it (design 57 K3, issue 662): the
// keys, canonical, and the trunk build of the catalogue they came from, by its commit and generation, so a reader
// says which list it holds. Not when it was kept: any lease holder builds the same bytes from the same record.
type PredefinedMirrored struct {
Keys json.RawMessage `json:"keys"`
Repository string `json:"repository"`
Commit string `json:"commit"`
Generation int `json:"generation"`
Build string `json:"build"`
}
// PredefinedMirror is the list of predefined keys kept, under PredefinedMirrorKey; no key while none is kept.
func (i *Inventory) PredefinedMirror(ctx context.Context) (map[string][]byte, error) {
kept, has, err := i.PredefinedKept(ctx)
if err != nil || !has {
return map[string][]byte{}, err
}
keys, err := kept.Keys.Canonical()
if err != nil {
return nil, err
}
value, err := json.Marshal(PredefinedMirrored{Keys: keys, Repository: kept.Repository, Commit: kept.Commit,
Generation: kept.Generation, Build: kept.Build})
if err != nil {
return nil, err
}
return map[string][]byte{PredefinedMirrorKey: value}, nil
}