Keep a recorded module at the build a node runs, per node: Node.Kept and Node.KeptBuilds overlay that node's own resolution and the commit it carries, never another node's (review round 1, finding 1; mirrors plan.go keepRecorded and carriedOn). Both tests failed first; the suite is 1251 tests in 1.05 s
This commit is contained in:
+6
-1
@@ -29,7 +29,7 @@ import (
|
||||
// (ADR 0446 clause 2).
|
||||
type Input struct {
|
||||
// Catalogue is the shelf: every module's manifest by name (inv.Catalogue), as it is to be composed. A recorded
|
||||
// module kept at the build a machine runs (keepRecorded) is already in it, put there by the caller.
|
||||
// module kept at the build a machine runs (keepRecorded) is that node's Kept, applied to its own resolution only.
|
||||
Catalogue map[string]catalogue.Manifest
|
||||
// Nodes is every node the mesh has (inv.Nodes and inv.Overlays together, one row per node).
|
||||
Nodes []Node
|
||||
@@ -176,6 +176,11 @@ type Node struct {
|
||||
SentBuilds map[string]string
|
||||
SentKnown bool
|
||||
Runs map[string]Run
|
||||
// Kept is, by module, the manifest of a recorded or core module kept at the build this machine runs (plan.go keepRecorded:
|
||||
// the caller reads it with recordedKept and ManifestAt), and KeptBuilds the commit of that build. They apply to this node's
|
||||
// own resolution only, never to another node's, and a declaration carries the kept commit for the module.
|
||||
Kept map[string]catalogue.Manifest
|
||||
KeptBuilds map[string]string
|
||||
// Contract is the contract of its node-engine's newest statement (inv.HealthOf); Stated is whether it ever stated one.
|
||||
Contract int
|
||||
Stated bool
|
||||
|
||||
@@ -219,7 +219,7 @@ func (c *composer) onTheNetwork() []Node {
|
||||
if len(n.Assigned) == 0 {
|
||||
continue
|
||||
}
|
||||
got, err := catalogue.Resolve(c.in.Catalogue, n.Assigned,
|
||||
got, err := catalogue.Resolve(c.shelfFor(n), n.Assigned,
|
||||
catalogue.Node{Name: n.Name, Site: n.Place.Site, Capabilities: n.Capabilities},
|
||||
catalogue.World{Unchecked: true, Holdings: c.in.Holdings})
|
||||
if err != nil {
|
||||
|
||||
@@ -247,6 +247,10 @@ func (c *composer) carriedOn(node Node, plan catalogue.Resolution, leftOut map[s
|
||||
continue
|
||||
}
|
||||
out[m.Module] = c.in.CurrentBuilds[m.Module]
|
||||
// A module kept at the build the machine runs is carried at that build (plan.go carriedOn).
|
||||
if kept, ok := node.KeptBuilds[m.Module]; ok && kept != "" {
|
||||
out[m.Module] = kept
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -323,3 +323,62 @@ func TestIssue263AnIdentityARealMachineNameOverflowsIsARefusal(t *testing.T) {
|
||||
t.Errorf("withheld %+v", d.Withheld)
|
||||
}
|
||||
}
|
||||
|
||||
// Round 1, finding 1 (plan.go keepRecorded): a recorded module kept at the build a machine runs is that machine's own resolution
|
||||
// and no other's; the commit carried for it is the kept one.
|
||||
func TestAKeptModuleChangesOnlyTheResolutionOfTheNodeThatKeepsIt(t *testing.T) {
|
||||
in := aSmallMesh(t)
|
||||
in.Catalogue["tool"] = catalogue.Manifest{Module: "tool", Version: "2"}
|
||||
for _, name := range []string{"anchor", "laptop"} {
|
||||
n := nodeOf(t, &in, name)
|
||||
n.Assigned = append(n.Assigned, "tool")
|
||||
}
|
||||
anchor := nodeOf(t, &in, "anchor")
|
||||
anchor.Kept = map[string]catalogue.Manifest{"tool": {Module: "tool", Version: "1"}}
|
||||
versionOn := func(in Input, node string) string {
|
||||
t.Helper()
|
||||
got, err := Resolve(in, node)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, m := range got.Plan.Modules {
|
||||
if m.Module == "tool" {
|
||||
return m.Version
|
||||
}
|
||||
}
|
||||
t.Fatalf("no tool on %s", node)
|
||||
return ""
|
||||
}
|
||||
if v := versionOn(in, "anchor"); v != "1" {
|
||||
t.Errorf("the node that keeps it runs version %s", v)
|
||||
}
|
||||
if v := versionOn(in, "laptop"); v != "2" {
|
||||
t.Errorf("another node runs version %s: a kept build leaked", v)
|
||||
}
|
||||
if got := in.Catalogue["tool"].Version; got != "2" {
|
||||
t.Errorf("the Input's catalogue was changed: %s", got)
|
||||
}
|
||||
// And the commit carried for it is the kept one.
|
||||
c := newComposer(in)
|
||||
plan := catalogue.Resolution{Modules: []catalogue.Manifest{{Module: "tool"}, {Module: "other"}}}
|
||||
in.CurrentBuilds = map[string]string{"tool": "current", "other": "current-other"}
|
||||
c = newComposer(in)
|
||||
node := Node{KeptBuilds: map[string]string{"tool": "kept-commit"}}
|
||||
if got := c.carriedOn(node, plan, nil); got["tool"] != "kept-commit" || got["other"] != "current-other" {
|
||||
t.Errorf("carried builds are %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
// And the rest of the mesh, as a node composes against it, offers what each other node's kept build offers, not the current one.
|
||||
func TestTheRestOfTheMeshReadsEachOtherNodesKeptBuild(t *testing.T) {
|
||||
in := threeNodes(t)
|
||||
w, _, _, err := TheRestOfTheMesh(in, "laptop")
|
||||
if err != nil || len(providers(w, "s3-bucket")) != 1 {
|
||||
t.Fatalf("before: %v %v", providers(w, "s3-bucket"), err)
|
||||
}
|
||||
nodeOf(t, &in, "anchor").Kept = map[string]catalogue.Manifest{"objects": {Module: "objects", Version: "0"}}
|
||||
w, _, _, err = TheRestOfTheMesh(in, "laptop")
|
||||
if err != nil || len(providers(w, "s3-bucket")) != 0 {
|
||||
t.Errorf("a node keeping a build that offers nothing still offers: %v %v", providers(w, "s3-bucket"), err)
|
||||
}
|
||||
}
|
||||
|
||||
+19
-2
@@ -45,7 +45,7 @@ func (c *composer) planFor(nodeName string) (Resolved, error) {
|
||||
if err != nil {
|
||||
return Resolved{}, err
|
||||
}
|
||||
shelf := c.in.Catalogue
|
||||
shelf := c.shelfFor(node)
|
||||
var out Resolved
|
||||
|
||||
// The credential for each thing this node takes from elsewhere.
|
||||
@@ -195,7 +195,7 @@ func (c *composer) resolution(nodeName string) (catalogue.Resolution, error) {
|
||||
if err != nil {
|
||||
return catalogue.Resolution{}, err
|
||||
}
|
||||
shelf := c.in.Catalogue
|
||||
shelf := c.shelfFor(node)
|
||||
world, _, _, err := c.theRestOfTheMesh(nodeName)
|
||||
if err != nil {
|
||||
return catalogue.Resolution{}, err
|
||||
@@ -225,6 +225,23 @@ func (c *composer) resolution(nodeName string) (catalogue.Resolution, error) {
|
||||
return resolved, nil
|
||||
}
|
||||
|
||||
// shelfFor is the catalogue as one node composes its own set: the Input's, with each recorded or core module the node keeps at the
|
||||
// build it runs put in place of the current one (plan.go keepRecorded). A copy: the Input's catalogue, and so every other node's
|
||||
// resolution, is untouched.
|
||||
func (c *composer) shelfFor(node Node) map[string]catalogue.Manifest {
|
||||
if len(node.Kept) == 0 {
|
||||
return c.in.Catalogue
|
||||
}
|
||||
shelf := make(map[string]catalogue.Manifest, len(c.in.Catalogue)+len(node.Kept))
|
||||
for name, m := range c.in.Catalogue {
|
||||
shelf[name] = m
|
||||
}
|
||||
for name, m := range node.Kept {
|
||||
shelf[name] = m
|
||||
}
|
||||
return shelf
|
||||
}
|
||||
|
||||
// licencesFor is what this node can be answered with by record, and what it was put on. A mesh with no licences at all
|
||||
// is the ordinary case and not an error.
|
||||
func (c *composer) licencesFor(node string) (map[string][]catalogue.Record, map[string]map[string]catalogue.Record) {
|
||||
|
||||
+4
-3
@@ -71,6 +71,7 @@ func (c *composer) theRestOfTheMesh(exclude string) (catalogue.World, Passes, []
|
||||
node catalogue.Node
|
||||
assigned []string
|
||||
pins map[string]catalogue.Chosen
|
||||
shelf map[string]catalogue.Manifest
|
||||
}
|
||||
var others []candidate
|
||||
// Every machine's assignments, before the holdings are read by any resolution (novox/hq ADR 0449 clause 4): a seat
|
||||
@@ -94,14 +95,14 @@ func (c *composer) theRestOfTheMesh(exclude string) (catalogue.World, Passes, []
|
||||
others = append(others, candidate{
|
||||
node: catalogue.Node{Name: n.Name, Site: n.Place.Site, Capabilities: n.Capabilities,
|
||||
At: onNetwork[n.Name], PublicDomain: n.PublicDomain},
|
||||
assigned: theirs, pins: n.Pins})
|
||||
assigned: theirs, pins: n.Pins, shelf: c.shelfFor(n)})
|
||||
}
|
||||
|
||||
var left []LeftOut
|
||||
offered := map[string][]catalogue.Provider{}
|
||||
var firstHeld []catalogue.Held
|
||||
for _, o := range others {
|
||||
got, err := catalogue.Resolve(shelf, o.assigned, o.node, catalogue.World{Unchecked: true, Holdings: holdings})
|
||||
got, err := catalogue.Resolve(o.shelf, o.assigned, o.node, catalogue.World{Unchecked: true, Holdings: holdings})
|
||||
if err != nil {
|
||||
// Said, not skipped: a machine dropped here offers nothing and holds nothing.
|
||||
left = append(left, LeftOut{Node: o.node.Name, Err: err})
|
||||
@@ -138,7 +139,7 @@ func (c *composer) theRestOfTheMesh(exclude string) (catalogue.World, Passes, []
|
||||
// Each machine is resolved with its own pins, as its plan is.
|
||||
theirs := world
|
||||
theirs.Pinned = o.pins
|
||||
got, err := catalogue.Resolve(shelf, o.assigned, o.node, theirs)
|
||||
got, err := catalogue.Resolve(o.shelf, o.assigned, o.node, theirs)
|
||||
if err != nil {
|
||||
// Said only for the whole-mesh view: with one machine excluded, the view is partial by design.
|
||||
if exclude == "" {
|
||||
|
||||
Reference in New Issue
Block a user