Refuse a manifest judge in mesh-sdk's own check, and never end the data items' read on one module (ADR 0578 §3, §4)
mesh/delivery-group group feat/judge-once rejected: composed together: the checker's verdict at the members' heads (8a8ede2fb2c8): unit: go.gofmt
mesh/delivery closed: a newer head of the same pull request
mesh/check failed: unit: go.gofmt: not as gofmt writes them (run gofmt -w): go/catalogue/streams.go | 62cddf4efc62 on ff5e00f1f894 | unit 0 run, 78 ke…
mesh/delivery-group group feat/judge-once rejected: composed together: the checker's verdict at the members' heads (8a8ede2fb2c8): unit: go.gofmt
mesh/delivery closed: a newer head of the same pull request
mesh/check failed: unit: go.gofmt: not as gofmt writes them (run gofmt -w): go/catalogue/streams.go | 62cddf4efc62 on ff5e00f1f894 | unit 0 run, 78 ke…
Review round 1 (issue 1585): L1, DataItemsHeld holds a module by the culprit rule PlansHeld uses, a held module's data left out, so an item its composition refuses holds that module alone; L2, mesh-sdk's own tests scan its go/ tree as mesh-catalog's checks/sdkjudge does, with the same list of composition's refusals. Two tests merged into one. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
@@ -545,10 +545,16 @@ func PlansHeld(ctx context.Context, r Reader, mesh Mesh, s Scope) (plans map[str
|
||||
// plansHeldOf is the plans of node over in, every module in held held, and each module a refusal is of held too until
|
||||
// the plans compose (culprit's rule over the Planning mode: its stub first, then its bare stub).
|
||||
func plansHeldOf(in compose.Input, node string, held map[string]string) (map[string]catalogue.Plan, error) {
|
||||
return heldOf(in, node, held, func(in compose.Input) (map[string]catalogue.Plan, error) { return plansIn(in, node, compose.Planning) })
|
||||
}
|
||||
|
||||
// heldOf composes compose1 over in with every module in held held, and each module a refusal is of held too until it
|
||||
// composes (culprit's rule: its stub first, then its bare stub); what compose1 answers of a held module is left out.
|
||||
// Plans and data items are composed so (PlansHeld, DataItemsHeld): one module never ends the read (novox/hq ADR 0578 §3).
|
||||
func heldOf[V any](in compose.Input, node string, held map[string]string, compose1 func(compose.Input) (map[string]V, error)) (map[string]V, error) {
|
||||
for _, m := range sortedKeys(held) {
|
||||
in = stubbed(in, node, m, "")
|
||||
}
|
||||
compose1 := func(in compose.Input) (map[string]catalogue.Plan, error) { return plansIn(in, node, compose.Planning) }
|
||||
// Each try holds one module more or leaves out all of one held module's offer: bounded by twice the modules placed.
|
||||
for tries := 0; tries <= 2*len(assignedOn(in, node))+1; tries++ {
|
||||
plans, err := compose1(in)
|
||||
@@ -585,7 +591,7 @@ func plansHeldOf(in compose.Input, node string, held map[string]string) (map[str
|
||||
}
|
||||
in = stubbed(in, node, module, leave)
|
||||
}
|
||||
return nil, fmt.Errorf("frombus: the plans of %s do not compose with %d module(s) held: %s", node, len(held),
|
||||
return nil, fmt.Errorf("frombus: what %s composes does not compose with %d module(s) held: %s", node, len(held),
|
||||
strings.Join(sortedKeys(held), ", "))
|
||||
}
|
||||
|
||||
@@ -1057,9 +1063,34 @@ func DataItemsHeld(ctx context.Context, r Reader, mesh Mesh, s Scope) (items map
|
||||
if err != nil {
|
||||
return nil, nil, held, err
|
||||
}
|
||||
items, unplaced, err = compose.DataItems(in, s.Node)
|
||||
for m := range held {
|
||||
delete(items, m)
|
||||
}
|
||||
// Held module by module, as PlansHeld holds them: a refused stub or one module's composition refusal (an item's
|
||||
// template) holds that module and never ends the read.
|
||||
items, err = heldOf(in, s.Node, held, func(in compose.Input) (map[string][]catalogue.PlacedDataItem, error) {
|
||||
var err error
|
||||
items, unplaced, err = compose.DataItems(withoutHeldData(in, s.Node), s.Node)
|
||||
return items, err
|
||||
})
|
||||
return items, unplaced, held, err
|
||||
}
|
||||
|
||||
// withoutHeldData is in with no data item of a module held on node: none of a held module's items is measured, so its
|
||||
// data never refuses the rest (its stub keeps its data for the backups the plans compose).
|
||||
func withoutHeldData(in compose.Input, node string) compose.Input {
|
||||
out := in
|
||||
out.Nodes = append([]compose.Node(nil), in.Nodes...)
|
||||
for i := range out.Nodes {
|
||||
n := &out.Nodes[i]
|
||||
if n.Name != node || len(n.Held) == 0 {
|
||||
continue
|
||||
}
|
||||
kept := maps.Clone(n.Kept)
|
||||
for m := range n.Held {
|
||||
if man, ok := kept[m]; ok {
|
||||
man.Data = nil
|
||||
kept[m] = man
|
||||
}
|
||||
}
|
||||
n.Kept = kept
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -29,16 +29,25 @@ func TestTheProvisionedMeshHandsAPlanWithItsProvision(t *testing.T) {
|
||||
// refused is the indexer's verdict on bad, a problem its judge found (novox/hq ADR 0578 §1): Plans then holds
|
||||
// backup too, and never ends the read.
|
||||
refused string
|
||||
// dataHeld says the data items' read holds backup too: a refusal it meets (a refused manifest, an unusable build,
|
||||
// an item composition refuses), not one only a plan meets.
|
||||
dataHeld bool
|
||||
// dataOnly checks the data items' read alone: the other reads blame restic, whose templates collect the item.
|
||||
dataOnly bool
|
||||
}{
|
||||
{name: "every module composes"},
|
||||
{name: "a built manifest that cannot be used",
|
||||
bad: `{"module":"backup","version":"1","build":{"artifacts":[{"name":"files","kind":"archive","from":"files"}]}}`,
|
||||
built: `{"module":"backup","version":"1","requires":"amqp"}`, why: "backup"},
|
||||
built: `{"module":"backup","version":"1","requires":"amqp"}`, why: "backup", dataHeld: true},
|
||||
{name: "a plan that refuses",
|
||||
bad: `{"module":"backup","version":"1","resources":[{"id":"conf","type":"file","path":"/etc/backup.conf",` +
|
||||
`"mode":"0600","content":"key=${secret:nope}"}]}`, why: "${secret:nope}"},
|
||||
{name: "an indexer's manifest the mesh refuses", bad: unusableItem, why: `"shows" is none of them`,
|
||||
refused: `"shows" is none of them`},
|
||||
refused: `"shows" is none of them`, dataHeld: true},
|
||||
// ADR 0578 §3: an accepted manifest whose data item its composition refuses is held by the culprit rule, in every read.
|
||||
{name: "a data item its composition refuses",
|
||||
bad: `{"module":"backup","version":"1","data":{"own":[{"id":"d","path":"${dir:nope}","class":"cache"}]}}`,
|
||||
why: "nope", dataHeld: true, dataOnly: true},
|
||||
{name: "an indexer's manifest the mesh refuses, placed elsewhere", bad: unusableItem, elsewhere: true,
|
||||
refused: `"shows" is none of them`},
|
||||
} {
|
||||
@@ -65,6 +74,13 @@ func TestTheProvisionedMeshHandsAPlanWithItsProvision(t *testing.T) {
|
||||
b.Builds["backup "+fp] = &delivery.BuilderBuild{State: delivery.BuildDone, Manifest: builtDigest,
|
||||
Artifacts: []delivery.BuiltArtifact{{Name: "files", Kind: "archive", Reference: "artifact-store://backup/files/blobs/x", Digest: "sha256:x"}}}
|
||||
}
|
||||
if row.dataOnly {
|
||||
items, _, iheld, err := frombus.DataItemsHeld(context.Background(), b, frombus.Mesh{}, frombus.Scope{Node: Node})
|
||||
if why, ok := iheld["backup"]; err != nil || !ok || !strings.Contains(why, row.why) || len(items["restic"]) == 0 {
|
||||
t.Errorf("%s: data items %v held %v: %v", row.name, items, iheld, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
plans, err := frombus.Plans(context.Background(), b, frombus.Mesh{}, frombus.Scope{Node: Node})
|
||||
switch {
|
||||
case row.refused != "" && (err != nil || !planned(plans, "restic") || planned(plans, "backup")):
|
||||
@@ -111,6 +127,13 @@ func TestTheProvisionedMeshHandsAPlanWithItsProvision(t *testing.T) {
|
||||
if _, planned := plans["backup"]; planned {
|
||||
t.Errorf("%s: the held module has a plan", row.name)
|
||||
}
|
||||
// The data items are read by the same rule (ADR 0578 §3: disk-load's read): the bad module held, the read whole.
|
||||
items, _, iheld, err := frombus.DataItemsHeld(context.Background(), b, frombus.Mesh{}, frombus.Scope{Node: Node})
|
||||
// A refusal only a plan meets (a file's secret) holds nothing here: data items render no such file.
|
||||
mustHold := row.dataHeld
|
||||
if why, ok := iheld["backup"]; err != nil || ok != mustHold || mustHold && !strings.Contains(why, row.why) || items["backup"] != nil {
|
||||
t.Errorf("%s: data items %v held %v: %v", row.name, items, iheld, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user