diff --git a/internal/composer/composer.go b/internal/composer/composer.go index 72e3db2..5b2682e 100644 --- a/internal/composer/composer.go +++ b/internal/composer/composer.go @@ -241,11 +241,17 @@ func runCase(r Request, c Case, depth string) (CaseVerdict, error) { // Composed without the build it runs at: named per node and module, never a silent pass (issue 1921). The // machine is then not judged, as for a node whose running state its engine does not send (ADR 0593 ยง9). Where // it does not compose without that build (a tool runner's bundle), it is composed with the module held as its - // node-engine holds it, and named so; a node that refuses then too is the change's refusal. + // node-engine holds it, and named so; a node that refuses then too is the change's refusal, in the words of that + // composition, never of the one from the unbuilt manifests, whose refusals the lift itself made (a tool runner + // with no bundle, a resource whose artifact or template no build resolved: the group check of c5d32074c945 on + // shanks). how := "composed from the change's manifest without its build: " if !m.Composes && m.NotJudged == "" { - if again := machine(heldAgain(in, held, awaited), node, depth, r.Declarations); again.Composes { + switch again := machine(heldAgain(in, held, awaited), node, depth, r.Declarations); { + case again.Composes: m, how = again, "composed with it held as its node-engine holds it, since it does not compose without its build: " + case again.NotJudged == "": + m, how = again, "refused with it held as its node-engine holds it, since it does not compose without its build: " } } words = how + words diff --git a/internal/composer/composer_test.go b/internal/composer/composer_test.go index 51bced2..1dc0792 100644 --- a/internal/composer/composer_test.go +++ b/internal/composer/composer_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "errors" + "fmt" "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" @@ -317,30 +318,50 @@ func TestARequestItCannotRunIsRefused(t *testing.T) { if err != nil { t.Fatal(err) } + runner := `{"module": "app", "version": "1", "tool-runner": true, ` + + `"build": {"artifacts": [{"name": "runtime", "kind": "bundle", "language": "go", "from": "cmd/runner", "binary": "runner"}]}}` for _, row := range []struct { name, raw, digest string words string // in NotJudged, its build's paths not declared; "" composes at its build + beside string // a module the change refuses on the same node: words are then in the refusal }{ - {"the digest of the case's manifest", plain, same, ""}, - {"another digest, the same contract", plain, strings.Repeat("b", 64), "of another manifest than the change's (its build was made of the manifest " + strings.Repeat("b", 64)}, - {"no digest, the same contract", plain, "", ""}, + {"the digest of the case's manifest", plain, same, "", ""}, + {"another digest, the same contract", plain, strings.Repeat("b", 64), "of another manifest than the change's (its build was made of the manifest " + strings.Repeat("b", 64), ""}, + {"no digest, the same contract", plain, "", "", ""}, {"no digest, another contract", strings.Replace(plain, `"version": "1"`, `"version": "2"`, 1), "", - "app is composed by the running node-engine at its build fp1, of another manifest than the change's (its node-engine names no manifest"}, - {"a tool runner, its bundle awaited", `{"module": "app", "version": "1", "tool-runner": true, ` + - `"build": {"artifacts": [{"name": "runtime", "kind": "bundle", "language": "go", "from": "cmd/runner", "binary": "runner"}]}}`, "", - "composed with it held as its node-engine holds it, since it does not compose without its build: app is composed by the running node-engine at its build fp1"}, + "app is composed by the running node-engine at its build fp1, of another manifest than the change's (its node-engine names no manifest", ""}, + {"a tool runner, its bundle awaited", runner, "", + "composed with it held as its node-engine holds it, since it does not compose without its build: app is composed by the running node-engine at its build fp1", ""}, + // The group check of c5d32074c945, shanks: the change refused lab there, and the words were those of the + // composition from the change's manifests unbuilt ("mesh-build-go runs as its own account and its build + // produced no bundle to serve"), not of the one with them held, so the lift's own refusals failed the node. + {"a tool runner awaited beside a module the change refuses", runner, "", + "refused with it held as its node-engine holds it, since it does not compose without its build: app is composed by the running node-engine at its build fp1", + `{"module": "office", "version": "1", "resources": [{"id": "c", "type": "file", "path": "/etc/office.conf", "template": "x"}]}`}, } { in := compose.Input{Catalogue: map[string]catalogue.Manifest{"app": built}, Nodes: []compose.Node{{Name: "a", Assigned: []string{"app"}}}, ComposedHow: &compose.ComposedHow{Node: "a", Builds: map[string]string{"app": "fp1"}}} if row.digest != "" { in.ComposedHow.Manifests = map[string]string{"app": row.digest} } + manifests := map[string]Manifest{"app": {Raw: json.RawMessage(row.raw)}} + if row.beside != "" { + manifests["office"] = Manifest{Raw: json.RawMessage(row.beside)} + in.Nodes[0].Assigned = append(in.Nodes[0].Assigned, "office") + } v, err := Run(withInput(t, Request{V: Version, Machines: []string{"a"}, Depth: DepthCompose, Declarations: true, - Cases: []Case{{Name: "change", Manifests: map[string]Manifest{"app": {Raw: json.RawMessage(row.raw)}}}}}, in)) + Cases: []Case{{Name: "change", Manifests: manifests}}}, in)) if err != nil { t.Fatal(err) } m := v.Cases[0].Machines["a"] + if row.beside != "" { + said := fmt.Sprint(m.Reason, m.Problems, m.Refusals) + if m.Composes || m.NotJudged != "" || !strings.Contains(said, "office") || strings.Contains(said, "bundle") || !strings.Contains(m.Reason, row.words) { + t.Errorf("%s: machine %+v, want the change's refusal of office with app held, naming %q", row.name, m, row.words) + } + continue + } declared := false for _, p := range m.Declared { declared = declared || p.Path == "/var/lib/app-built"