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 `.`. 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) }