The controller plans config, templates and moved providers' provisions, hands bus accounts to the vault and judges the new manifest words, on the feature's SDK and node-engine validator (feature 57, config and provisioning).
107 lines
4.4 KiB
Go
107 lines
4.4 KiB
Go
package inventory
|
|
|
|
import (
|
|
"context"
|
|
"sort"
|
|
|
|
"github.com/novox/mesh-controller/internal/catalogue"
|
|
)
|
|
|
|
// **The move of placement** (novox/hq ADR 0348 clause 11, issue 736; design 57: the moves of data run after the
|
|
// delivery, each a verb that says what it did). Migration 0097 moved `places`, `accesses` and `networks` out of
|
|
// every settings layer; `endpoints`, `expose`, `reach` and `ports` are placement too, and a layer set before this
|
|
// controller still holds them among its settings. Composition already reads them as placement wherever they are
|
|
// (SettingsFor), so a machine is told the same before the move and after; the move only puts them where they
|
|
// belong, so that no settings layer holds a placement key and the settings layers can move into mesh-config (K5).
|
|
//
|
|
// One transaction: each layer that holds a placement key is kept whole in the history first ("moved", with why),
|
|
// its placement keys are laid over the placement kept beside it, and its settings row keeps its other keys, when
|
|
// it was set and who set it. Asked again, it moves nothing.
|
|
|
|
// MovedLayer is one layer the move of placement moved, or would move: whose, and which keys.
|
|
type MovedLayer struct {
|
|
// Node is the layer's machine, empty for the whole mesh's.
|
|
Node string `json:"node"`
|
|
Module string `json:"module"`
|
|
Keys []string `json:"keys"`
|
|
}
|
|
|
|
// movedWhy is why a layer the move of placement moved went into the history, as migration 0097 says it.
|
|
const movedWhy = "moved to placement, ADR 0348 clause 11"
|
|
|
|
// MovePlacement moves every placement key a settings layer still holds beside it, and says which layers held which,
|
|
// the whole mesh's first, then by machine and module. Without apply it moves nothing and says what it would.
|
|
func (i *Inventory) MovePlacement(ctx context.Context, apply bool) ([]MovedLayer, error) {
|
|
keys := catalogue.PlacementKeys()
|
|
tx, err := i.store.Pool().Begin(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() { _ = tx.Rollback(context.WithoutCancel(ctx)) }()
|
|
// Locked for the move, so a set between the read and the write cannot be lost under it.
|
|
rows, err := tx.Query(ctx,
|
|
`select coalesce(n.name, ''), s.module,
|
|
(select array_agg(k order by k) from jsonb_object_keys(s.values) k where k = any($1))
|
|
from settings s left join node n on n.id = s.node
|
|
where s.values ?| $1
|
|
order by s.node is not null, n.name, s.module
|
|
for update of s`, keys)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
moved := []MovedLayer{}
|
|
for rows.Next() {
|
|
var m MovedLayer
|
|
if err := rows.Scan(&m.Node, &m.Module, &m.Keys); err != nil {
|
|
rows.Close()
|
|
return nil, err
|
|
}
|
|
sort.Strings(m.Keys)
|
|
moved = append(moved, m)
|
|
}
|
|
rows.Close()
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
if !apply || len(moved) == 0 {
|
|
return moved, nil
|
|
}
|
|
|
|
// Each layer kept whole first, as the history reads a layer: its settings and the placement beside it.
|
|
if _, err := tx.Exec(ctx,
|
|
`insert into settings_history (node, module, values, set_at, replaced_by, set_by, why)
|
|
select s.node, s.module, `+layerWhole+`, s.set_at, 'moved', s.set_by, $2
|
|
from settings s where s.values ?| $1`, keys, movedWhy); err != nil {
|
|
return nil, err
|
|
}
|
|
// Laid over what is kept beside the layer: a key the layer still holds was said after anything kept there.
|
|
if _, err := tx.Exec(ctx,
|
|
`update placement p
|
|
set values = p.values || (select jsonb_object_agg(e.key, e.value) from jsonb_each(s.values) e
|
|
where e.key = any($1))
|
|
from settings s
|
|
where s.module = p.module and s.node is not distinct from p.node and s.values ?| $1`, keys); err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := tx.Exec(ctx,
|
|
`insert into placement (node, module, values, set_at, set_by)
|
|
select s.node, s.module,
|
|
(select jsonb_object_agg(e.key, e.value) from jsonb_each(s.values) e where e.key = any($1)),
|
|
s.set_at, s.set_by
|
|
from settings s
|
|
where s.values ?| $1
|
|
and not exists (select 1 from placement p
|
|
where p.module = s.module and p.node is not distinct from s.node)`, keys); err != nil {
|
|
return nil, err
|
|
}
|
|
// The layer keeps its other keys, when it was set and who set it; one that held only placement stays, empty.
|
|
if _, err := tx.Exec(ctx,
|
|
`update settings set values = values - $1::text[] where values ?| $1`, keys); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := tx.Commit(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
return moved, nil
|
|
}
|