diff --git a/modules/mesh-delivery-assigner/internal/assign/compose.go b/modules/mesh-delivery-assigner/internal/assign/compose.go index 6de7006..d609c87 100644 --- a/modules/mesh-delivery-assigner/internal/assign/compose.go +++ b/modules/mesh-delivery-assigner/internal/assign/compose.go @@ -175,6 +175,14 @@ func Compose(m Mesh, c Change, sdk string, took time.Duration) delivery.Composit "and a field the running controller cannot read is not judged (issues 1621, 1622, 1623)", sdk, c.SDKHead)) } v.Warnings = append(v.Warnings, NotJudged) + if could := CouldRun(m, c, machines); len(could) > 0 { + v.Warnings = append(v.Warnings, "not judged until the facts snapshot is on the bus (issue 1461, ADR 0537 clause 1): "+ + "the machines that could run a moved or added module and are not assigned it: "+strings.Join(could, ", ")) + } + if grants := GrantsAlone(m, c, machines); len(grants) > 0 { + v.Warnings = append(v.Warnings, "not judged until the facts snapshot is on the bus (issue 1461, ADR 0537 clause 1): "+ + "the machines whose grants alone the change alters: "+strings.Join(grants, ", ")) + } if len(machines) == 0 { v.Warnings = append(v.Warnings, "the change touches no machine: no module it moves is assigned anywhere") } @@ -186,6 +194,71 @@ func Compose(m Mesh, c Change, sdk string, took time.Duration) delivery.Composit return v } +// CouldRun is, by name, every machine of the mesh not composed that is not assigned a module the change moves or adds +// (ADR 0537 clause 1): which of them could run it, the bus does not say (capabilities, settings and places are only in +// the facts snapshot), so each is named as not judged. +func CouldRun(m Mesh, c Change, composed []string) []string { + var out []string + for node, modules := range m.Assigned { + if slices.Contains(composed, node) { + continue + } + for name := range c.Manifests { + if !slices.Contains(modules, name) { + out = append(out, node) + break + } + } + } + sort.Strings(out) + return out +} + +// grantsOf is what of a manifest makes grants: its bus declarations and the provisions it requires and offers. +func grantsOf(man catalogue.Manifest) string { + var offers []string + for _, o := range man.Provides { + offers = append(offers, o.Name) + } + return fmt.Sprint(man.Requires, offers, man.Reads, man.State, man.Emits, man.Consumes, man.Invokes, man.Claims, man.Grants) +} + +// GrantsAlone is, by name, every machine not composed whose grants alone the change alters (ADR 0537 clause 1): for a +// moved module whose grants differ from the trunk's, the machines assigned the bus's holder (whose user list carries +// every module's bus grants) and those assigned a provider of a provision it requires, before or after. +func GrantsAlone(m Mesh, c Change, composed []string) []string { + provisions := map[string]bool{} + altered := false + for name, man := range c.Manifests { + before, had := m.Manifests[name] + if had && grantsOf(before) == grantsOf(man) { + continue + } + altered = true + for _, r := range append(slices.Clone(man.Requires), before.Requires...) { + provisions[r] = true + } + } + if !altered { + return nil + } + var out []string + for node, modules := range m.Assigned { + if slices.Contains(composed, node) { + continue + } + for _, mod := range modules { + man := m.Manifests[mod] + if man.ClaimsSeat("mesh-broker") || slices.ContainsFunc(man.Provides, func(o catalogue.Offer) bool { return provisions[o.Name] }) { + out = append(out, node) + break + } + } + } + sort.Strings(out) + return out +} + func sortedKeys[V any](m map[string]V) []string { out := make([]string, 0, len(m)) for k := range m { diff --git a/modules/mesh-delivery-assigner/internal/assign/compose_test.go b/modules/mesh-delivery-assigner/internal/assign/compose_test.go index 889988a..96c46ab 100644 --- a/modules/mesh-delivery-assigner/internal/assign/compose_test.go +++ b/modules/mesh-delivery-assigner/internal/assign/compose_test.go @@ -114,3 +114,28 @@ func TestTouched(t *testing.T) { t.Errorf("an added module assigned nowhere: %+v", v) } } + +// ADR 0537 clause 1: every verdict names, by machine, those that could run a moved or added module and are not assigned +// it, and those whose grants alone the change alters (the bus's machine, a provider of what the module requires), as not +// judged; shanks, providing what web now requires, is composed and so named in neither. A change that alters no grant +// names none of the second. +func TestTheMachinesNotJudgedAreNamed(t *testing.T) { + m := meshOf() + m.Manifests["bus"] = catalogue.Manifest{Module: "bus", Version: "1", Claims: []catalogue.Claim{{Name: "mesh-broker", Scope: "mesh"}}} + m.Manifests["db"] = catalogue.Manifest{Module: "db", Version: "1", Provides: []catalogue.Offer{{Name: "cache"}}} + m.Assigned["novox"] = []string{"bus"} + m.Assigned["shanks"] = []string{"db"} + c := Change{Group: "g", Heads: "0123456789ab", Manifests: map[string]catalogue.Manifest{ + "web": {Module: "web", Version: "2", Reads: []string{"mesh-controller.assignments"}, Requires: []string{"cache"}}}} + v := Compose(m, c, "sdk", 0) + if !hasWarning(v, "could run a moved or added module and are not assigned it: ace, novox") || !slices.Contains(v.Machines, "shanks") { + t.Errorf("the machines that could run web are not named: %v", v.Warnings) + } + if !hasWarning(v, "whose grants alone the change alters: novox") { + t.Errorf("the machines whose grants alone change are not named: %v", v.Warnings) + } + c.Manifests["web"] = catalogue.Manifest{Module: "web", Version: "3"} + if v := Compose(meshOf(), c, "sdk", 0); hasWarning(v, "grants alone") { + t.Errorf("a change altering no grant names machines whose grants change: %v", v.Warnings) + } +}