mesh-config resolves a module's config values per node from which module runs where and what each registered module declares, and both lived only in the controller's store (hq issue 652, design 57 F3). The controller now keeps two record mirrors, mesh-controller_assignments (<node>.<module>) and mesh-controller_declared-config (<module>), built from the store by the lease holder every ten seconds and written only where a value differs, by compare-and-set, with one assignment-changed or declared-config-changed per write and a purge said as removed (ADR 0289, design 54 §2-3). A module reads them through `reads` as mesh-controller.<mirror>; the writers table keeps every other writer out. Neither holds a secret: an assignment names its node and module, a declaration is what the public manifest declares. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
101 lines
3.3 KiB
Go
101 lines
3.3 KiB
Go
package inventory
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"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"`
|
|
}
|
|
|
|
// DeclaredConfigMirrored is one registered module's config declarations as the declared-config mirror holds
|
|
// them: the module, and its manifest's `settings` exactly as the manifest holds them (each key's kind,
|
|
// default and why today), carried whole so a declaration that gains fields (ADR 0348 clause 4) is mirrored
|
|
// without a change here. An empty object when the module declares none: registered, declaring nothing.
|
|
type DeclaredConfigMirrored struct {
|
|
Module string `json:"module"`
|
|
Settings json.RawMessage `json:"settings"`
|
|
}
|
|
|
|
// AssignmentsMirror is every assignment's mirrored value, keyed `<node>.<module>`.
|
|
func (i *Inventory) AssignmentsMirror(ctx context.Context) (map[string][]byte, 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()
|
|
out := map[string][]byte{}
|
|
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()
|
|
value, err := json.Marshal(a)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out[a.Node+"."+a.Module] = value
|
|
}
|
|
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->'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
|
|
}
|
|
settings, err := canonical(raw)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
d.Settings = settings
|
|
value, err := json.Marshal(d)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out[d.Module] = value
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
// 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)
|
|
}
|