diff --git a/cmd/mesh-controller/check.go b/cmd/mesh-controller/check.go index cb4c6acd..f3c5b3a4 100644 --- a/cmd/mesh-controller/check.go +++ b/cmd/mesh-controller/check.go @@ -32,6 +32,9 @@ import ( // provision it wants that a manifest given here offers. An overflow is refused in the pull request // that introduces it — a new requirement, a lowered bound, a longer slug — instead of on the // provider's machine when a real machine's name first meets the module's. +// SomeManifestsVar, set by the merge gate, says the manifests given are only some of their repository's. +const SomeManifestsVar = "MESH_MODULE_CHECK_SOME" + func moduleCheck(paths []string, out io.Writer) error { return moduleCheckFor(paths, catalogue.DefaultLongestMachine, out) } @@ -85,12 +88,24 @@ func moduleCheckFor(paths []string, longestMachine int, out io.Writer) error { // Between the manifests: a seat declared twice, a use of a seat nothing declares, a claim on // a seat that does not exist. Run only over what parsed, because a problem inside one manifest // has already been said and would be said again here in a worse form. + // + // **Over some of a repository's manifests, a seat none of them declares is a note** (novox/hq issue 364), as + // this command's own word says above: the merge gate passes only the manifests a change touches, and says so + // with SomeManifestsVar, so a module that uses or claims a seat another module declares (the operator + // channel's, a channel bench) was refused there for a manifest it was not given. Given every manifest — the + // catalogue's own check, and registration — it stays a refusal. + some := os.Getenv(SomeManifestsVar) != "" problems := catalogue.CatalogueProblems(shelf) sort.Strings(problems) for _, p := range problems { + if some && catalogue.IsUndeclaredSeat(p) { + fmt.Fprintf(out, "note: %s among the manifests given; registration judges it against the whole catalogue, "+ + "and passing the declaring module's manifest too judges it here\n", p) + continue + } fmt.Fprintln(out, p) + failed++ } - failed += len(problems) // Between the manifests too: an identity against the bounds of the provisions it wants, which // only the provider's manifest states. diff --git a/cmd/mesh-controller/check_test.go b/cmd/mesh-controller/check_test.go index 6754bd19..853570a2 100644 --- a/cmd/mesh-controller/check_test.go +++ b/cmd/mesh-controller/check_test.go @@ -103,3 +103,23 @@ func TestTheControllersManifestServesEveryVerbOfItsSeat(t *testing.T) { t.Fatalf("the controller's own module.json fails module check: %v\n%s", err, out.String()) } } + +// A module that uses a seat another module declares (novox/hq issue 364): refused over the whole catalogue when the +// declarer is missing, a note when the gate says it gives only the manifests a change touches. +func TestASeatAnotherModuleDeclaresIsANoteOverSomeManifests(t *testing.T) { + dir := t.TempDir() + user := filepath.Join(dir, "user.json") + os.WriteFile(user, []byte(`{"module":"asker","version":"1","uses":["operator-channel"]}`), 0o600) + var out bytes.Buffer + if err := moduleCheck([]string{user}, &out); err == nil { + t.Fatalf("a use of a seat nothing given declares passed the whole-catalogue check:\n%s", out.String()) + } + t.Setenv(SomeManifestsVar, "1") + out.Reset() + if err := moduleCheck([]string{user}, &out); err != nil { + t.Fatalf("over some manifests the use was refused:\n%s", out.String()) + } + if !strings.Contains(out.String(), "note: asker uses the seat \"operator-channel\"") { + t.Fatalf("the note was not said:\n%s", out.String()) + } +} diff --git a/internal/builder/check.go b/internal/builder/check.go index 00a31acc..773f6702 100644 --- a/internal/builder/check.go +++ b/internal/builder/check.go @@ -543,6 +543,11 @@ func passedSoFar(ran []string) string { return strings.Join(ran, ", ") + " passed; " } +// SomeManifestsEnv tells the judge's module check that the manifests it is given are only those a change touches, +// so a seat another module of the repository declares is a note there, not a refusal (novox/hq issue 364). A judge +// that predates it reads nothing of it and refuses as before. +const SomeManifestsEnv = "MESH_MODULE_CHECK_SOME=1" + // gateLayer runs the gate: the touched manifests through `module check`, every machine composed with the // change, and the replays of what the mesh runs. It answers the gate's verdict and summary. func gateLayer(ctx context.Context, spec CheckSpec, tree, root, gate, verdictFile string, env []string, @@ -562,7 +567,7 @@ func gateLayer(ctx context.Context, spec CheckSpec, tree, root, gate, verdictFil checked := func(dir string) (string, error) { var own tail cmd := exec.CommandContext(ctx, "docker", LabelledArgs("docker", - inToolchain(dir, env, append([]string{gate, "module", "check"}, manifests...)...), spec.ID)...) + inToolchain(dir, append(append([]string{}, env...), SomeManifestsEnv), append([]string{gate, "module", "check"}, manifests...)...), spec.ID)...) inItsOwnGroup(cmd) w := io.MultiWriter(out, &own) cmd.Stdout, cmd.Stderr = w, w diff --git a/internal/catalogue/seats_declared.go b/internal/catalogue/seats_declared.go index 972eabf7..eee4ebe5 100644 --- a/internal/catalogue/seats_declared.go +++ b/internal/catalogue/seats_declared.go @@ -230,6 +230,14 @@ type Shelf map[string]Manifest // // Run at registration, which is the last moment the mesh can still refuse: after it, a caller is // bound to a seat and a refusal is an outage rather than a conversation. +// UndeclaredSeat ends the problem of a `uses` or a claim naming a seat no manifest given declares: over the whole +// catalogue (registration, the catalogue's own check) a refusal, and over some manifests alone a seat whose +// declaring module was not given (novox/hq issue 364). +const UndeclaredSeat = "which no module declares and the mesh does not define" + +// IsUndeclaredSeat says whether a problem CatalogueProblems gave is a seat no manifest given declares. +func IsUndeclaredSeat(problem string) bool { return strings.HasSuffix(problem, UndeclaredSeat) } + func CatalogueProblems(shelf Shelf) []string { var problems []string @@ -282,7 +290,7 @@ func CatalogueProblems(shelf Shelf) []string { for _, u := range m.Uses { if !exists(u) { problems = append(problems, fmt.Sprintf( - "%s uses the seat %q, which no module declares and the mesh does not define", + "%s uses the seat %q, "+UndeclaredSeat, module, u)) } } @@ -290,7 +298,7 @@ func CatalogueProblems(shelf Shelf) []string { for _, c := range m.Claims { if !exists(c.Name) { problems = append(problems, fmt.Sprintf( - "%s claims the seat %q, which no module declares and the mesh does not define", + "%s claims the seat %q, "+UndeclaredSeat, module, c.Name)) continue }