|
|
|
@@ -6,6 +6,7 @@ import (
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"git.novox.be/novox/mesh-contracts/go/clients/meshconfigclient"
|
|
|
|
|
"regexp"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
@@ -61,32 +62,25 @@ type PullDecl struct {
|
|
|
|
|
At time.Time `json:"at"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// KeptValue is an approved held value, kept apart from the values mesh-config resolves, in this module's own layout
|
|
|
|
|
// (the owner's: its state `kept`), which the checker and the assigner's composition check read (novox/hq ADR 0565 §5).
|
|
|
|
|
type KeptValue struct {
|
|
|
|
|
Pull string `json:"pull"`
|
|
|
|
|
Head string `json:"head"`
|
|
|
|
|
Digest string `json:"declaration"`
|
|
|
|
|
Node string `json:"node"`
|
|
|
|
|
Module string `json:"module"`
|
|
|
|
|
Key string `json:"key"`
|
|
|
|
|
// Value is the value held, as the operator approved it.
|
|
|
|
|
Value json.RawMessage `json:"value"`
|
|
|
|
|
By string `json:"by"`
|
|
|
|
|
Via string `json:"via"`
|
|
|
|
|
Why string `json:"why"`
|
|
|
|
|
// Approved is who approved it, as the proposal kept it.
|
|
|
|
|
Approved string `json:"approved"`
|
|
|
|
|
Proposal string `json:"proposal"`
|
|
|
|
|
At time.Time `json:"at"`
|
|
|
|
|
}
|
|
|
|
|
// KeptValue is an approved held value, kept apart from the values mesh-config resolves, in the layout of the seat's state
|
|
|
|
|
// `kept`, which the checker and the assigner's composition check read (novox/hq ADR 0565 §5, ADR 0634 §4).
|
|
|
|
|
type KeptValue = meshconfigclient.Kept
|
|
|
|
|
|
|
|
|
|
// keptKey is a held value's key in `kept`: the pull request's key form (pullKey), the node, the module and the config
|
|
|
|
|
// key, joined by dots.
|
|
|
|
|
func keptKey(pull, node, module, key string) string {
|
|
|
|
|
return pullKey(pull) + "." + node + "." + module + "." + key
|
|
|
|
|
k, _ := meshconfigclient.HeldValueKey(pull, node, module, key) // a held value of no pull request has no key, and is found by none
|
|
|
|
|
return k
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// heldValue is the value held, as the operator approved it, in its JSON form.
|
|
|
|
|
func heldValue(k KeptValue) json.RawMessage {
|
|
|
|
|
raw, _ := meshconfigclient.ValueOf(k) // a value that was read from JSON writes as JSON
|
|
|
|
|
return raw
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func heldAt(t time.Time) *time.Time { return &t }
|
|
|
|
|
|
|
|
|
|
func keptStateKey(k KeptValue) string { return keptKey(k.Pull, k.Node, k.Module, k.Key) }
|
|
|
|
|
|
|
|
|
|
func keptLevel(k KeptValue) Level { return Level{Kind: LevelModule, Node: k.Node, Module: k.Module} }
|
|
|
|
@@ -123,9 +117,9 @@ func (c *Config) readPulls(s *Sources) error {
|
|
|
|
|
return fmt.Errorf("the pull requests' declarations cannot be read: %w", err)
|
|
|
|
|
}
|
|
|
|
|
if err := c.each(KeptState, func(key string, raw []byte) error {
|
|
|
|
|
var k KeptValue
|
|
|
|
|
if err := json.Unmarshal(raw, &k); err != nil || k.Pull == "" {
|
|
|
|
|
return nil
|
|
|
|
|
k, err := meshconfigclient.DecodeKept(key, raw)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil // not a held value of the seat's layout under its own key
|
|
|
|
|
}
|
|
|
|
|
s.Kept[key] = k
|
|
|
|
|
return nil
|
|
|
|
@@ -176,11 +170,11 @@ func (c *Config) declarePull(pull, head string, declares map[string]map[string]D
|
|
|
|
|
switch {
|
|
|
|
|
case !declared:
|
|
|
|
|
why = "the head no longer adds or changes its declaration"
|
|
|
|
|
case declDigest(d) != kv.Digest:
|
|
|
|
|
case declDigest(d) != kv.Declaration:
|
|
|
|
|
why = "the head declares the key otherwise than the value was approved against"
|
|
|
|
|
default:
|
|
|
|
|
var v any
|
|
|
|
|
_ = json.Unmarshal(kv.Value, &v)
|
|
|
|
|
_ = json.Unmarshal(heldValue(kv), &v)
|
|
|
|
|
if p := d.rule().check(v); len(p) > 0 {
|
|
|
|
|
why = "the value no longer passes: " + strings.Join(p, "; ")
|
|
|
|
|
}
|
|
|
|
@@ -208,13 +202,13 @@ func (c *Config) declarePull(pull, head string, declares map[string]map[string]D
|
|
|
|
|
// config-changed; what could not be done is answered, not dropped.
|
|
|
|
|
func (c *Config) heldChange(kv KeptValue, action, why, approved string) []string {
|
|
|
|
|
level := keptLevel(kv)
|
|
|
|
|
ch := Change{Action: action, Level: &level, Key: kv.Key, Value: kv.Value, By: kv.By, Via: kv.Via, Why: why,
|
|
|
|
|
ch := Change{Action: action, Level: &level, Key: kv.Key, Value: heldValue(kv), By: kv.By, Via: kv.Via, Why: why,
|
|
|
|
|
Approved: approved, Pull: kv.Pull, Head: kv.Head, At: c.now(), Moved: []Moved{}}
|
|
|
|
|
unsaid := c.record(level.ValueKey(kv.Key), ch)
|
|
|
|
|
cause := "a value held for " + kv.Pull
|
|
|
|
|
what := fmt.Sprintf("config %s %s of %s for %s, held for %s", action, kv.Key, kv.Module, kv.Node, kv.Pull)
|
|
|
|
|
if action != ActionDrop {
|
|
|
|
|
what += " (" + string(kv.Value) + ")"
|
|
|
|
|
what += " (" + string(heldValue(kv)) + ")"
|
|
|
|
|
}
|
|
|
|
|
if err := c.Store.HandAct(what, why, cause); err != nil {
|
|
|
|
|
unsaid = append(unsaid, "the hand-act log does not hold it: "+err.Error())
|
|
|
|
@@ -260,8 +254,8 @@ type HeldSkipped struct {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func heldValueOf(k KeptValue) HeldValue {
|
|
|
|
|
return HeldValue{Pull: k.Pull, Head: k.Head, Node: k.Node, Module: k.Module, Key: k.Key, Value: compact(k.Value),
|
|
|
|
|
Approved: k.Approved, By: k.By, Why: k.Why, At: k.At}
|
|
|
|
|
return HeldValue{Pull: k.Pull, Head: k.Head, Node: k.Node, Module: k.Module, Key: k.Key, Value: compact(heldValue(k)),
|
|
|
|
|
Approved: k.Approved, By: k.By, Why: k.Why, At: timeOf(k.At)}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Held answers the approved held values for the pull requests asked, at the heads asked, to the controller alone: a
|
|
|
|
@@ -373,13 +367,13 @@ func (c *Config) keep(p Proposal) (string, error) {
|
|
|
|
|
"propose it again", p.Pull, p.Key)
|
|
|
|
|
}
|
|
|
|
|
was, had := s.Kept[keptKey(p.Pull, p.Level.Node, p.Level.Module, p.Key)]
|
|
|
|
|
if had != p.HadValue || valueDigest(was.Value, had) != p.WasDigest {
|
|
|
|
|
if had != p.HadValue || valueDigest(heldValue(was), had) != p.WasDigest {
|
|
|
|
|
return "", fmt.Errorf("the value held for %s changed since the operator was shown the change: propose it "+
|
|
|
|
|
"again", p.Pull)
|
|
|
|
|
}
|
|
|
|
|
kv := KeptValue{Pull: p.Pull, Head: pd.Head, Digest: p.DeclDigest, Node: p.Level.Node, Module: p.Level.Module,
|
|
|
|
|
kv := KeptValue{Pull: p.Pull, Head: pd.Head, Declaration: p.DeclDigest, Node: p.Level.Node, Module: p.Level.Module,
|
|
|
|
|
Key: p.Key, Value: p.Value, By: "the operator", Via: p.From, Why: p.Why, Approved: p.Approved, Proposal: p.ID,
|
|
|
|
|
At: c.now()}
|
|
|
|
|
At: heldAt(c.now())}
|
|
|
|
|
if err := c.Store.Put(KeptState, keptStateKey(kv), kv); err != nil {
|
|
|
|
|
return "", fmt.Errorf("the value was not kept: %v", err)
|
|
|
|
|
}
|
|
|
|
@@ -434,7 +428,7 @@ func (c *Config) settlePull(pull string, merged bool, head string) []string {
|
|
|
|
|
why = "the merged head is not known, so the value cannot be held to it"
|
|
|
|
|
case !strings.EqualFold(head, kv.Head):
|
|
|
|
|
why = fmt.Sprintf("it is held at head %s and the pull request merged at %s", short(kv.Head), short(head))
|
|
|
|
|
case !known || !declared || declDigest(d) != kv.Digest:
|
|
|
|
|
case !known || !declared || declDigest(d) != kv.Declaration:
|
|
|
|
|
why = "the merged head does not declare the key as the operator was shown"
|
|
|
|
|
}
|
|
|
|
|
if why != "" {
|
|
|
|
@@ -444,7 +438,7 @@ func (c *Config) settlePull(pull string, merged bool, head string) []string {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
vk := keptLevel(kv).ValueKey(kv.Key)
|
|
|
|
|
v := Value{Value: compact(kv.Value), By: kv.By, Via: kv.Via, Why: kv.Why + " (held for " + kv.Pull +
|
|
|
|
|
v := Value{Value: compact(heldValue(kv)), By: kv.By, Via: kv.Via, Why: kv.Why + " (held for " + kv.Pull +
|
|
|
|
|
", merged)", Approved: kv.Approved, At: c.now()}
|
|
|
|
|
if err := c.Store.Put(ValuesState, vk, v); err != nil {
|
|
|
|
|
said = append(said, fmt.Sprintf("%s of %s on %s held for %s is not written yet: %v; it is tried "+
|
|
|
|
|