Every method of builds, build requests, rollouts, merges and assignments reads and writes the chain alone: the else branches that read the tables, the movers and MoveChain with their tests, chain_moved reads, ChainReads and the flags that switched a kind, the store's assignments mirror (KeepAssignmentPlans needs none of it: it uses only the AssignmentMirrored type, mirrors.go, and AssignmentValues, provisioning.go) and its tick, the move code of the controller's start, and ForTest's chain_moved row. The listed store tests of the move go with their functions; the tests of the readers, requests, assignments and plans that lived beside them stay in chain_store_test.go. Tests that replayed old data migrations over the build, assignment and pin tables are removed with the tables. The static test that no plan is written but through SavePlansMoving now reads the plan's chain path.
182 lines
7.7 KiB
Go
182 lines
7.7 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"}
|
|
|
|
// assignmentsMirrored is every standing assignment as the provisioner's plans are matched to it (SentProvisioningPlans,
|
|
// KeepAssignmentPlans).
|
|
func (i *Inventory) assignmentsMirrored(ctx context.Context) ([]AssignmentMirrored, error) {
|
|
rows, err := i.assignments(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]AssignmentMirrored, 0, len(rows))
|
|
for _, a := range rows {
|
|
out = append(out, AssignmentMirrored{Node: a.Node, Module: a.Module, Assigned: a.Assigned.UTC()})
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// 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
|
|
}
|