The node-engine finds the tool runner by what its manifest says (tool-runner), never by its name, pinned at mesh-sdk a973bfa (issue 774, the operator 2026-11-03)
This commit is contained in:
@@ -1046,7 +1046,7 @@ func resolutionCompose(r mcat.Resolution, with mcat.Rendering, owner map[string]
|
||||
return nil, err
|
||||
}
|
||||
if process != nil { // none for a held runtime's stand-in: what runs of it is carried
|
||||
owner[fmt.Sprint(process["id"])] = mcat.RuntimeModule
|
||||
owner[fmt.Sprint(process["id"])] = fmt.Sprint(process["name"]) // the tool runner's module (resolutionRuntimeProcess)
|
||||
out = append(out, process)
|
||||
}
|
||||
// And a runtime of its own for each module of its own account, after it (novox/hq ADR 0259 §8).
|
||||
@@ -1308,7 +1308,7 @@ func resolutionOwnSecretOwner(r mcat.Resolution, m mcat.Manifest, name string) s
|
||||
if o := m.OwnSecrets[name].Owner; o != "" {
|
||||
return o
|
||||
}
|
||||
if r.Account != "" && (m.Module == mcat.RuntimeModule || ToolsBusSecret(m, name)) {
|
||||
if r.Account != "" && (mcat.IsToolRunner(m) || ToolsBusSecret(m, name)) {
|
||||
return r.Account
|
||||
}
|
||||
return m.SecretsOwner
|
||||
|
||||
@@ -113,7 +113,7 @@ func TestTheForgesOwnAddressFollowsThePortTheNodeGaveIt(t *testing.T) {
|
||||
// The number this node was given for the forge — the one the machine it is about to run on
|
||||
// already publishes.
|
||||
out, err := Declaration(r, mcat.Rendering{ArtifactStore: "anchor.internal:5101",
|
||||
Needed: map[string]map[string]string{mcat.RuntimeModule: {"broker": "sealed-broker"}},
|
||||
Needed: map[string]map[string]string{"node-tools": {"broker": "sealed-broker"}},
|
||||
Given: map[string]map[int]int{"gitea": {3000: 2999}},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -129,7 +129,7 @@ func TestTheForgesOwnAddressFollowsThePortTheNodeGaveIt(t *testing.T) {
|
||||
t.Fatalf("the forge is not published on the port this node gave it: %v", server["ports"])
|
||||
}
|
||||
// The forge's own code runs in the node's runtime (novox/hq ADR 0198), given its words there.
|
||||
runtime := fileNamed(out, mcat.RuntimeModule+"."+RuntimeProcessID())
|
||||
runtime := fileNamed(out, "node-tools"+"."+RuntimeProcessID())
|
||||
if runtime == nil {
|
||||
t.Fatalf("the node's runtime is not in the declaration: %v", ids(out))
|
||||
}
|
||||
@@ -168,7 +168,7 @@ func declaredGiteaSsh(t *testing.T, given map[int]int) map[string]any {
|
||||
givenPorts[k] = v
|
||||
}
|
||||
out, err := Declaration(r, mcat.Rendering{ArtifactStore: "anchor.internal:5101",
|
||||
Needed: map[string]map[string]string{mcat.RuntimeModule: {"broker": "sealed-broker"}},
|
||||
Needed: map[string]map[string]string{"node-tools": {"broker": "sealed-broker"}},
|
||||
Ports: map[string]map[int]int{"gitea": givenPorts},
|
||||
Given: map[string]map[int]int{"gitea": given},
|
||||
})
|
||||
|
||||
@@ -46,9 +46,9 @@ func twoMergeableFiles(app, sidecar []any) mcat.Manifest {
|
||||
// The indexer's judge's proofs, items 1 to 13 of its list.
|
||||
func TestTheJudgesProofsOfAComposition(t *testing.T) {
|
||||
runtimeWith := mcat.Rendering{ArtifactStore: "anchor.internal:5101",
|
||||
Needed: map[string]map[string]string{mcat.RuntimeModule: {"broker": "sealed-credential"}, "dash": {"token": "sealed-token"}}}
|
||||
Needed: map[string]map[string]string{"node-tools": {"broker": "sealed-credential"}, "dash": {"token": "sealed-token"}}}
|
||||
processOf := func(out []map[string]any) map[string]any {
|
||||
return fileNamed(out, mcat.RuntimeModule+"."+RuntimeProcessID())
|
||||
return fileNamed(out, "node-tools"+"."+RuntimeProcessID())
|
||||
}
|
||||
|
||||
t.Run("1 a bundle's words resolve per machine into the runtime's tool env", func(t *testing.T) {
|
||||
|
||||
@@ -13,13 +13,13 @@ import (
|
||||
// place the operator-sealed secrets are kept — and no other module is told.
|
||||
func TestTheHolderOfNodeReadsIsToldWhereItsNodeKeepsASecret(t *testing.T) {
|
||||
with := mcat.Rendering{ArtifactStore: "anchor.internal:5101",
|
||||
Needed: map[string]map[string]string{mcat.RuntimeModule: {"broker": "sealed-credential"}, "reads": {"broker": "own"}, "desk": {"broker": "own"}}}
|
||||
goRuntime := mcat.Manifest{Module: mcat.RuntimeModule, Version: "1",
|
||||
OwnSecrets: mcat.OwnSecrets{"broker": {Path: "/var/lib/mesh/" + mcat.RuntimeModule + "/broker"}},
|
||||
Needed: map[string]map[string]string{"node-tools": {"broker": "sealed-credential"}, "reads": {"broker": "own"}, "desk": {"broker": "own"}}}
|
||||
goRuntime := mcat.Manifest{Module: "node-tools", Version: "1", ToolRunner: true,
|
||||
OwnSecrets: mcat.OwnSecrets{"broker": {Path: "/var/lib/mesh/" + "node-tools" + "/broker"}},
|
||||
Build: &mcat.Build{Artifacts: []mcat.Artifact{{Name: "runtime", Kind: mcat.ArtifactBundle, Language: "go",
|
||||
System: "arch", From: "cmd/node-tools"}}}}
|
||||
goRuntime, err := goRuntime.Resolve([]mcat.Built{{Name: "runtime", Kind: mcat.ArtifactBundle,
|
||||
Reference: mcat.ArtifactStoreScheme + mcat.RuntimeModule + "/runtime/blobs/" + bundleDigest, Digest: bundleDigest}})
|
||||
Reference: mcat.ArtifactStoreScheme + "node-tools" + "/runtime/blobs/" + bundleDigest, Digest: bundleDigest}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -59,7 +59,7 @@ func TestTheHolderOfNodeReadsIsToldWhereItsNodeKeepsASecret(t *testing.T) {
|
||||
t.Fatalf("the holder is not told its node's secret places: %v (%v)", err, given)
|
||||
}
|
||||
want := []string{"/etc/app/db.secret", "/home/ops/.config/app", "/var/lib/desk", "/var/lib/mesh-reads",
|
||||
"/var/lib/mesh-vault", "/var/lib/mesh/" + mcat.RuntimeModule}
|
||||
"/var/lib/mesh-vault", "/var/lib/mesh/" + "node-tools"}
|
||||
if strings.Join(places, " ") != strings.Join(want, " ") {
|
||||
t.Errorf("secret places:\n got %v\nwant %v", places, want)
|
||||
}
|
||||
|
||||
@@ -21,12 +21,8 @@ func BundlePath(module, bundle string) string { return mcat.BundleRoot + "/" + m
|
||||
// a node is sent exactly what it was sent before, bundles included, because a bundle nothing loads
|
||||
// is bytes nobody reads.
|
||||
func resolutionRuntimeHere(r mcat.Resolution) bool {
|
||||
for _, m := range r.Modules {
|
||||
if m.Module == mcat.RuntimeModule {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
_, here := mcat.ToolRunnerOf(r.Modules)
|
||||
return here
|
||||
}
|
||||
|
||||
// bundleArchives is one archive per tools bundle of a module — a bundle the runtime LOADS something
|
||||
@@ -59,13 +55,13 @@ func RuntimeProcessID() string { return "runtime" }
|
||||
// interpreterFor is how a bundle in a language is run: the program the host's unit starts, with the
|
||||
// bundle's entrypoint after it. The one thing the composer takes from a language, and said here
|
||||
// rather than in a manifest because the runtime's process is the mesh's to compose (to-be 38 WP3).
|
||||
func interpreterFor(language string) (string, error) {
|
||||
func interpreterFor(module, language string) (string, error) {
|
||||
switch language {
|
||||
case "typescript":
|
||||
return "node", nil
|
||||
}
|
||||
return "", fmt.Errorf(
|
||||
"%s is written in %q, and the mesh knows no interpreter to run a %q bundle with", mcat.RuntimeModule, language, language)
|
||||
"%s is written in %q, and the mesh knows no interpreter to run a %q bundle with", module, language, language)
|
||||
}
|
||||
|
||||
// runtimeProcess is the one process a machine runs the node's tool runtime as (novox/hq ADR 0175,
|
||||
@@ -80,7 +76,7 @@ func interpreterFor(language string) (string, error) {
|
||||
func resolutionRuntimeProcess(r mcat.Resolution, with mcat.Rendering) (map[string]any, error) {
|
||||
var runtime *mcat.Manifest
|
||||
for i := range r.Modules {
|
||||
if r.Modules[i].Module == mcat.RuntimeModule {
|
||||
if mcat.IsToolRunner(r.Modules[i]) {
|
||||
runtime = &r.Modules[i]
|
||||
}
|
||||
}
|
||||
@@ -93,7 +89,7 @@ func resolutionRuntimeProcess(r mcat.Resolution, with mcat.Rendering) (map[strin
|
||||
if len(runtime.Bundles) != 1 {
|
||||
return nil, fmt.Errorf(
|
||||
"%s is assigned to %s and its build produced %d bundle(s); the runtime is one bundle "+
|
||||
"the mesh runs, so the module declares exactly one (novox/hq to-be 38)", mcat.RuntimeModule, r.Node, len(runtime.Bundles))
|
||||
"the mesh runs, so the module declares exactly one (novox/hq to-be 38)", runtime.Module, r.Node, len(runtime.Bundles))
|
||||
}
|
||||
bundle := runtime.Bundles[0]
|
||||
// What runs it (novox/hq ADR 0193): a runtime compiled to a binary runs itself, from its own
|
||||
@@ -105,9 +101,9 @@ func resolutionRuntimeProcess(r mcat.Resolution, with mcat.Rendering) (map[strin
|
||||
if len(bundle.Entrypoints) != 1 {
|
||||
return nil, fmt.Errorf(
|
||||
"%s's bundle %q names %d entrypoint(s); the runtime is run from one, so the module "+
|
||||
"declares exactly one (novox/hq to-be 38)", mcat.RuntimeModule, bundle.Name, len(bundle.Entrypoints))
|
||||
"declares exactly one (novox/hq to-be 38)", runtime.Module, bundle.Name, len(bundle.Entrypoints))
|
||||
}
|
||||
interpreter, err := interpreterFor(bundle.Language)
|
||||
interpreter, err := interpreterFor(runtime.Module, bundle.Language)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -117,7 +113,7 @@ func resolutionRuntimeProcess(r mcat.Resolution, with mcat.Rendering) (map[strin
|
||||
if !declared {
|
||||
return nil, fmt.Errorf(
|
||||
"%s declares no own secret named broker, and the node's credential is delivered there: "+
|
||||
"a module that speaks on the bus declares \"own-secrets\": {\"broker\": <path>}", mcat.RuntimeModule,
|
||||
"a module that speaks on the bus declares \"own-secrets\": {\"broker\": <path>}", runtime.Module,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -163,7 +159,7 @@ func resolutionRuntimeProcess(r mcat.Resolution, with mcat.Rendering) (map[strin
|
||||
}
|
||||
}
|
||||
sort.Strings(served)
|
||||
restartOn = append(restartOn, mcat.RuntimeModule+"."+mcat.NeedID("broker"))
|
||||
restartOn = append(restartOn, runtime.Module+"."+mcat.NeedID("broker"))
|
||||
sort.Strings(restartOn)
|
||||
|
||||
env := map[string]string{mcat.RuntimeToolModules: strings.Join(served, ","), mcat.RuntimeBrokerFile: credential.Path}
|
||||
@@ -176,7 +172,7 @@ func resolutionRuntimeProcess(r mcat.Resolution, with mcat.Rendering) (map[strin
|
||||
env[mcat.RuntimeToolEnv] = string(body)
|
||||
}
|
||||
process := map[string]any{
|
||||
"id": mcat.RuntimeModule + "." + RuntimeProcessID(), "type": "process", "name": mcat.RuntimeModule,
|
||||
"id": runtime.Module + "." + RuntimeProcessID(), "type": "process", "name": runtime.Module,
|
||||
"source": bundle.Source, "digest": bundle.Digest,
|
||||
"run": run,
|
||||
"env": env,
|
||||
@@ -193,7 +189,7 @@ func resolutionRuntimeProcess(r mcat.Resolution, with mcat.Rendering) (map[strin
|
||||
}
|
||||
// Routed through the artifact store as this network reaches it now, like everything the mesh
|
||||
// built; refused with the same words when there is no store to route through.
|
||||
if err := artifactsInto(process, mcat.RuntimeModule, with); err != nil {
|
||||
if err := artifactsInto(process, runtime.Module, with); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return process, nil
|
||||
@@ -229,14 +225,14 @@ func resolutionOwnRuntimes(r mcat.Resolution, with mcat.Rendering) ([]map[string
|
||||
}
|
||||
var runtime *mcat.Manifest
|
||||
for i := range r.Modules {
|
||||
if r.Modules[i].Module == mcat.RuntimeModule {
|
||||
if mcat.IsToolRunner(r.Modules[i]) {
|
||||
runtime = &r.Modules[i]
|
||||
}
|
||||
}
|
||||
if runtime == nil || len(runtime.Bundles) != 1 || runtime.Bundles[0].Binary == "" {
|
||||
return nil, &NoRuntimeToRunError{Held: runtime != nil && runtime.Stubbed, said: fmt.Sprintf("%s runs as its own "+
|
||||
"account in a runtime of its own, and %s is not here to run it from: assign %s to %s first (novox/hq ADR 0259)",
|
||||
own[0].Module, mcat.RuntimeModule, mcat.RuntimeModule, r.Node)}
|
||||
"account in a runtime of its own, and the tool runner is not here to run it from: assign the tool runner to %s first "+
|
||||
"(novox/hq ADR 0259)", own[0].Module, r.Node)}
|
||||
}
|
||||
program := runtime.Bundles[0]
|
||||
var out []map[string]any
|
||||
@@ -314,7 +310,7 @@ func resolutionOwnRuntimes(r mcat.Resolution, with mcat.Rendering) ([]map[string
|
||||
if yielding != nil {
|
||||
process["yields"] = yielding
|
||||
}
|
||||
if err := artifactsInto(process, mcat.RuntimeModule, with); err != nil {
|
||||
if err := artifactsInto(process, runtime.Module, with); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, process)
|
||||
|
||||
@@ -9,12 +9,12 @@ import (
|
||||
// The runtime-health is composed onto <module>.own-runtime, in the words the node-engine takes; to an engine
|
||||
// that does not read health it is not sent, as for every resource.
|
||||
func TestARuntimeHealthIsComposedOntoTheModulesOwnRuntime(t *testing.T) {
|
||||
goRuntime := mcat.Manifest{Module: mcat.RuntimeModule, Version: "1",
|
||||
OwnSecrets: mcat.OwnSecrets{"broker": {Path: "/var/lib/mesh/" + mcat.RuntimeModule + "/broker"}},
|
||||
goRuntime := mcat.Manifest{Module: "node-tools", Version: "1", ToolRunner: true,
|
||||
OwnSecrets: mcat.OwnSecrets{"broker": {Path: "/var/lib/mesh/" + "node-tools" + "/broker"}},
|
||||
Build: &mcat.Build{Artifacts: []mcat.Artifact{{Name: "runtime", Kind: mcat.ArtifactBundle, Language: "go",
|
||||
System: "arch", From: "cmd/node-tools"}}}}
|
||||
goRuntime, err := goRuntime.Resolve([]mcat.Built{{Name: "runtime", Kind: mcat.ArtifactBundle,
|
||||
Reference: mcat.ArtifactStoreScheme + mcat.RuntimeModule + "/runtime/blobs/" + bundleDigest, Digest: bundleDigest}})
|
||||
Reference: mcat.ArtifactStoreScheme + "node-tools" + "/runtime/blobs/" + bundleDigest, Digest: bundleDigest}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -26,7 +26,7 @@ func TestARuntimeHealthIsComposedOntoTheModulesOwnRuntime(t *testing.T) {
|
||||
"looks": 2.0}
|
||||
for _, reads := range []bool{true, false} {
|
||||
with := mcat.Rendering{ArtifactStore: "anchor.internal:5101", ReadsHealth: reads,
|
||||
Needed: map[string]map[string]string{mcat.RuntimeModule: {"broker": "sealed"}, "keeper": {"broker": "own"}}}
|
||||
Needed: map[string]map[string]string{"node-tools": {"broker": "sealed"}, "keeper": {"broker": "own"}}}
|
||||
out, err := Declaration(mcat.Resolution{Node: "anchor", Account: "ops", Modules: []mcat.Manifest{keeper, goRuntime}}, with)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -46,7 +46,7 @@ func TestARuntimeHealthIsComposedOntoTheModulesOwnRuntime(t *testing.T) {
|
||||
h["timeout"] != "10s" || h["looks"] != 2 || h["grace"] != "1m0s" {
|
||||
t.Errorf("its own runtime's health: %v", own[mcat.HealthField])
|
||||
}
|
||||
if machine := fileNamed(out, mcat.RuntimeModule+"."+RuntimeProcessID()); machine[mcat.HealthField] != nil {
|
||||
if machine := fileNamed(out, "node-tools"+"."+RuntimeProcessID()); machine[mcat.HealthField] != nil {
|
||||
t.Errorf("the machine's runtime was given the module's health: %v", machine[mcat.HealthField])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,12 +31,12 @@ func aToolsModule(t *testing.T, name string, entrypoints ...string) mcat.Manifes
|
||||
// loaded, and its broker secret to receive the node's credential in.
|
||||
func theRuntime(t *testing.T) mcat.Manifest {
|
||||
t.Helper()
|
||||
m := mcat.Manifest{Module: mcat.RuntimeModule, Version: "1",
|
||||
OwnSecrets: mcat.OwnSecrets{"broker": {Path: "/var/lib/mesh/" + mcat.RuntimeModule + "/broker"}},
|
||||
m := mcat.Manifest{Module: "node-tools", Version: "1", ToolRunner: true,
|
||||
OwnSecrets: mcat.OwnSecrets{"broker": {Path: "/var/lib/mesh/" + "node-tools" + "/broker"}},
|
||||
Build: &mcat.Build{Artifacts: []mcat.Artifact{{Name: "runtime", Kind: mcat.ArtifactBundle, Language: "typescript",
|
||||
Entrypoints: []string{"src/main.js"}}}}}
|
||||
resolved, err := m.Resolve([]mcat.Built{{Name: "runtime", Kind: mcat.ArtifactBundle,
|
||||
Reference: mcat.ArtifactStoreScheme + mcat.RuntimeModule + "/runtime/blobs/" + bundleDigest, Digest: bundleDigest}})
|
||||
Reference: mcat.ArtifactStoreScheme + "node-tools" + "/runtime/blobs/" + bundleDigest, Digest: bundleDigest}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -45,7 +45,7 @@ func theRuntime(t *testing.T) mcat.Manifest {
|
||||
|
||||
func TestEveryToolsBundleIsDeliveredWhereTheRuntimeRuns(t *testing.T) {
|
||||
store := mcat.Rendering{ArtifactStore: "anchor.internal:5101",
|
||||
Needed: map[string]map[string]string{mcat.RuntimeModule: {"broker": "sealed-credential"}}}
|
||||
Needed: map[string]map[string]string{"node-tools": {"broker": "sealed-credential"}}}
|
||||
nftables := aToolsModule(t, "nftables", "tools/index.js")
|
||||
zsh := aToolsModule(t, "zsh", "tools/index.js", "tools/more.js")
|
||||
|
||||
@@ -70,7 +70,7 @@ func TestEveryToolsBundleIsDeliveredWhereTheRuntimeRuns(t *testing.T) {
|
||||
t.Errorf("zsh's tools bundle was not delivered: %v", ids(out))
|
||||
}
|
||||
// The runtime's own bundle is run, not loaded: its process delivers it, not an archive.
|
||||
if fileNamed(out, mcat.RuntimeModule+"."+BundleID("runtime")) != nil {
|
||||
if fileNamed(out, "node-tools"+"."+BundleID("runtime")) != nil {
|
||||
t.Error("the runtime's own bundle was delivered as an archive beside its process")
|
||||
}
|
||||
})
|
||||
@@ -86,6 +86,18 @@ func TestEveryToolsBundleIsDeliveredWhereTheRuntimeRuns(t *testing.T) {
|
||||
t.Errorf("%s was delivered to a machine running no runtime to load it", id)
|
||||
}
|
||||
}
|
||||
// The tool runner is what says it is one, never a name (issue 774): node-tools saying nothing is no runner.
|
||||
silent := theRuntime(t)
|
||||
silent.ToolRunner = false
|
||||
out, err = Declaration(mcat.Resolution{Node: "anchor", Modules: []mcat.Manifest{nftables, zsh, silent}}, store)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, id := range ids(out) {
|
||||
if strings.Contains(id, BundleID("")) || strings.HasSuffix(id, "."+RuntimeProcessID()) {
|
||||
t.Errorf("%s was composed for a module that does not say it is the tool runner", id)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -101,7 +113,7 @@ func ids(out []map[string]any) []string {
|
||||
// where its credential is, and who the operator is — restarted when any of that changes.
|
||||
func TestTheMachineRunsOneRuntimeLoadingEveryDeliveredBundle(t *testing.T) {
|
||||
with := mcat.Rendering{ArtifactStore: "anchor.internal:5101",
|
||||
Needed: map[string]map[string]string{mcat.RuntimeModule: {"broker": "sealed-credential"}}}
|
||||
Needed: map[string]map[string]string{"node-tools": {"broker": "sealed-credential"}}}
|
||||
nftables := aToolsModule(t, "nftables", "tools/index.js")
|
||||
// A bundle carrying a daemon beside its tools says which files the runtime loads.
|
||||
showcase := mcat.Manifest{Module: "showcase", Version: "1", Tools: []string{"greet"},
|
||||
@@ -118,12 +130,12 @@ func TestTheMachineRunsOneRuntimeLoadingEveryDeliveredBundle(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
process := fileNamed(out, mcat.RuntimeModule+"."+RuntimeProcessID())
|
||||
process := fileNamed(out, "node-tools"+"."+RuntimeProcessID())
|
||||
if process == nil {
|
||||
t.Fatalf("no runtime process was composed: %v", ids(out))
|
||||
}
|
||||
if process["type"] != "process" || process["name"] != mcat.RuntimeModule || process["digest"] != bundleDigest ||
|
||||
process["source"] != "http://anchor.internal:5101/v2/"+mcat.RuntimeModule+"/runtime/blobs/"+bundleDigest {
|
||||
if process["type"] != "process" || process["name"] != "node-tools" || process["digest"] != bundleDigest ||
|
||||
process["source"] != "http://anchor.internal:5101/v2/"+"node-tools"+"/runtime/blobs/"+bundleDigest {
|
||||
t.Errorf("the runtime's process is %v", process)
|
||||
}
|
||||
if fmt.Sprint(process["run"]) != "[node src/main.js]" {
|
||||
@@ -134,7 +146,7 @@ func TestTheMachineRunsOneRuntimeLoadingEveryDeliveredBundle(t *testing.T) {
|
||||
"showcase="+mcat.BundleRoot+"/showcase/code/tools/index.js" {
|
||||
t.Errorf("the runtime is told to serve %q: every loaded file, by module, and nothing a bundle runs", env[mcat.RuntimeToolModules])
|
||||
}
|
||||
if env[mcat.RuntimeBrokerFile] != "/var/lib/mesh/"+mcat.RuntimeModule+"/broker" {
|
||||
if env[mcat.RuntimeBrokerFile] != "/var/lib/mesh/"+"node-tools"+"/broker" {
|
||||
t.Errorf("the runtime reads its credential at %q, not where the module's own secret is placed", env[mcat.RuntimeBrokerFile])
|
||||
}
|
||||
if env[mcat.RuntimeOperatorAccount] != "ops" || env[mcat.RuntimeOperatorHome] != "/home/ops" || process["user"] != "ops" {
|
||||
@@ -142,18 +154,18 @@ func TestTheMachineRunsOneRuntimeLoadingEveryDeliveredBundle(t *testing.T) {
|
||||
}
|
||||
// The credential the process reads belongs to the account it runs as, or it could not read it
|
||||
// (to-be 38 WP3); other modules' secrets are left as their manifests say.
|
||||
if credential := fileNamed(out, mcat.RuntimeModule+"."+mcat.NeedID("broker")); credential == nil || credential["owner"] != "ops" {
|
||||
if credential := fileNamed(out, "node-tools"+"."+mcat.NeedID("broker")); credential == nil || credential["owner"] != "ops" {
|
||||
t.Errorf("the runtime's credential is not the account's to read: %v", credential)
|
||||
}
|
||||
restarts := fmt.Sprint(process["restart-on"])
|
||||
for _, want := range []string{"nftables." + BundleID("tools"), "showcase." + BundleID("code"), mcat.RuntimeModule + "." + mcat.NeedID("broker")} {
|
||||
for _, want := range []string{"nftables." + BundleID("tools"), "showcase." + BundleID("code"), "node-tools" + "." + mcat.NeedID("broker")} {
|
||||
if !strings.Contains(restarts, want) {
|
||||
t.Errorf("the runtime is not restarted when %s changes: %s", want, restarts)
|
||||
}
|
||||
}
|
||||
// After every bundle and the credential, so both exist before it starts.
|
||||
names := ids(out)
|
||||
if names[len(names)-1] != mcat.RuntimeModule+"."+RuntimeProcessID() {
|
||||
if names[len(names)-1] != "node-tools"+"."+RuntimeProcessID() {
|
||||
t.Errorf("the runtime's process is not last: %v", names)
|
||||
}
|
||||
|
||||
@@ -162,7 +174,7 @@ func TestTheMachineRunsOneRuntimeLoadingEveryDeliveredBundle(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
process := fileNamed(out, mcat.RuntimeModule+"."+RuntimeProcessID())
|
||||
process := fileNamed(out, "node-tools"+"."+RuntimeProcessID())
|
||||
env := process["env"].(map[string]string)
|
||||
if _, set := env[mcat.RuntimeOperatorAccount]; set {
|
||||
t.Error("an operator account was named on a machine that has none")
|
||||
@@ -170,13 +182,13 @@ func TestTheMachineRunsOneRuntimeLoadingEveryDeliveredBundle(t *testing.T) {
|
||||
if _, set := process["user"]; set {
|
||||
t.Error("a user was set on a machine with no account")
|
||||
}
|
||||
if credential := fileNamed(out, mcat.RuntimeModule+"."+mcat.NeedID("broker")); credential == nil || credential["owner"] != nil {
|
||||
if credential := fileNamed(out, "node-tools"+"."+mcat.NeedID("broker")); credential == nil || credential["owner"] != nil {
|
||||
t.Errorf("the runtime's credential was given an owner on a machine with no account: %v", credential)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("a runtime module built wrong is refused by name", func(t *testing.T) {
|
||||
two := mcat.Manifest{Module: mcat.RuntimeModule, Version: "1", OwnSecrets: mcat.OwnSecrets{"broker": {Path: "/b"}},
|
||||
two := mcat.Manifest{Module: "node-tools", Version: "1", ToolRunner: true, OwnSecrets: mcat.OwnSecrets{"broker": {Path: "/b"}},
|
||||
Build: &mcat.Build{Artifacts: []mcat.Artifact{{Name: "runtime", Kind: mcat.ArtifactBundle, Language: "typescript",
|
||||
Entrypoints: []string{"a.js", "b.js"}}}}}
|
||||
resolved, err := two.Resolve([]mcat.Built{{Name: "runtime", Kind: mcat.ArtifactBundle,
|
||||
@@ -195,7 +207,7 @@ func TestTheMachineRunsOneRuntimeLoadingEveryDeliveredBundle(t *testing.T) {
|
||||
// build from before launchers — so the move needs no flag day.
|
||||
func TestTheRuntimeStartsTheLauncherWhereTheBuildWroteOne(t *testing.T) {
|
||||
with := mcat.Rendering{ArtifactStore: "anchor.internal:5101",
|
||||
Needed: map[string]map[string]string{mcat.RuntimeModule: {"broker": "sealed-credential"}}}
|
||||
Needed: map[string]map[string]string{"node-tools": {"broker": "sealed-credential"}}}
|
||||
launched := mcat.Manifest{Module: "dash", Version: "1", Tools: []string{"status"},
|
||||
Build: &mcat.Build{Artifacts: []mcat.Artifact{{Name: "tools", Kind: mcat.ArtifactBundle, Language: "typescript",
|
||||
Entrypoints: []string{"tools/index.js"}}}}}
|
||||
@@ -210,7 +222,7 @@ func TestTheRuntimeStartsTheLauncherWhereTheBuildWroteOne(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
env := fileNamed(out, mcat.RuntimeModule+"."+RuntimeProcessID())["env"].(map[string]string)
|
||||
env := fileNamed(out, "node-tools"+"."+RuntimeProcessID())["env"].(map[string]string)
|
||||
want := "dash=" + mcat.BundleRoot + "/dash/tools/tools/index.serve.mjs,nftables=" + mcat.BundleRoot + "/nftables/tools/tools/index.js"
|
||||
if env[mcat.RuntimeToolModules] != want {
|
||||
t.Errorf("the runtime is told %q, want %q", env[mcat.RuntimeToolModules], want)
|
||||
@@ -220,13 +232,13 @@ func TestTheRuntimeStartsTheLauncherWhereTheBuildWroteOne(t *testing.T) {
|
||||
// novox/hq ADR 0193: a runtime compiled to a binary runs itself from its own unpacked bundle.
|
||||
func TestARuntimeCompiledToABinaryRunsItself(t *testing.T) {
|
||||
with := mcat.Rendering{ArtifactStore: "anchor.internal:5101",
|
||||
Needed: map[string]map[string]string{mcat.RuntimeModule: {"broker": "sealed-credential"}}}
|
||||
goRuntime := mcat.Manifest{Module: mcat.RuntimeModule, Version: "1",
|
||||
OwnSecrets: mcat.OwnSecrets{"broker": {Path: "/var/lib/mesh/" + mcat.RuntimeModule + "/broker"}},
|
||||
Needed: map[string]map[string]string{"node-tools": {"broker": "sealed-credential"}}}
|
||||
goRuntime := mcat.Manifest{Module: "node-tools", Version: "1", ToolRunner: true,
|
||||
OwnSecrets: mcat.OwnSecrets{"broker": {Path: "/var/lib/mesh/" + "node-tools" + "/broker"}},
|
||||
Build: &mcat.Build{Artifacts: []mcat.Artifact{{Name: "runtime", Kind: mcat.ArtifactBundle, Language: "go",
|
||||
System: "arch", From: "cmd/node-tools"}}}}
|
||||
goRuntime, err := goRuntime.Resolve([]mcat.Built{{Name: "runtime", Kind: mcat.ArtifactBundle,
|
||||
Reference: mcat.ArtifactStoreScheme + mcat.RuntimeModule + "/runtime/blobs/" + bundleDigest, Digest: bundleDigest}})
|
||||
Reference: mcat.ArtifactStoreScheme + "node-tools" + "/runtime/blobs/" + bundleDigest, Digest: bundleDigest}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -234,7 +246,7 @@ func TestARuntimeCompiledToABinaryRunsItself(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
process := fileNamed(out, mcat.RuntimeModule+"."+RuntimeProcessID())
|
||||
process := fileNamed(out, "node-tools"+"."+RuntimeProcessID())
|
||||
if fmt.Sprint(process["run"]) != "[./node-tools]" {
|
||||
t.Errorf("a Go runtime is run as %v, want its own binary", process["run"])
|
||||
}
|
||||
@@ -249,13 +261,13 @@ func TestARuntimeCompiledToABinaryRunsItself(t *testing.T) {
|
||||
// which runs as the operator's account and is given none of its words.
|
||||
func TestAModuleOfItsOwnAccountIsServedByARuntimeOfItsOwn(t *testing.T) {
|
||||
with := mcat.Rendering{ArtifactStore: "anchor.internal:5101",
|
||||
Needed: map[string]map[string]string{mcat.RuntimeModule: {"broker": "sealed-credential"}, "telegram": {"broker": "own"}}}
|
||||
goRuntime := mcat.Manifest{Module: mcat.RuntimeModule, Version: "1",
|
||||
OwnSecrets: mcat.OwnSecrets{"broker": {Path: "/var/lib/mesh/" + mcat.RuntimeModule + "/broker"}},
|
||||
Needed: map[string]map[string]string{"node-tools": {"broker": "sealed-credential"}, "telegram": {"broker": "own"}}}
|
||||
goRuntime := mcat.Manifest{Module: "node-tools", Version: "1", ToolRunner: true,
|
||||
OwnSecrets: mcat.OwnSecrets{"broker": {Path: "/var/lib/mesh/" + "node-tools" + "/broker"}},
|
||||
Build: &mcat.Build{Artifacts: []mcat.Artifact{{Name: "runtime", Kind: mcat.ArtifactBundle, Language: "go",
|
||||
System: "arch", From: "cmd/node-tools"}}}}
|
||||
goRuntime, err := goRuntime.Resolve([]mcat.Built{{Name: "runtime", Kind: mcat.ArtifactBundle,
|
||||
Reference: mcat.ArtifactStoreScheme + mcat.RuntimeModule + "/runtime/blobs/" + bundleDigest, Digest: bundleDigest}})
|
||||
Reference: mcat.ArtifactStoreScheme + "node-tools" + "/runtime/blobs/" + bundleDigest, Digest: bundleDigest}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -267,7 +279,7 @@ func TestAModuleOfItsOwnAccountIsServedByARuntimeOfItsOwn(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
machine := fileNamed(out, mcat.RuntimeModule+"."+RuntimeProcessID())
|
||||
machine := fileNamed(out, "node-tools"+"."+RuntimeProcessID())
|
||||
if served := machine["env"].(map[string]string)[mcat.RuntimeToolModules]; strings.Contains(served, "telegram") || !strings.Contains(served, "nftables") {
|
||||
t.Errorf("the machine's runtime serves %q", served)
|
||||
}
|
||||
@@ -342,13 +354,13 @@ func TestAModuleOfItsOwnAccountIsServedByARuntimeOfItsOwn(t *testing.T) {
|
||||
// as the account agents run as there — either would hand what it holds back to the accounts it is kept from.
|
||||
func TestAModuleOfItsOwnAccountIsRefusedTheOperatorsAndTheAgentsAccount(t *testing.T) {
|
||||
with := mcat.Rendering{ArtifactStore: "anchor.internal:5101",
|
||||
Needed: map[string]map[string]string{mcat.RuntimeModule: {"broker": "sealed-credential"}, "telegram": {"broker": "own"}}}
|
||||
goRuntime := mcat.Manifest{Module: mcat.RuntimeModule, Version: "1",
|
||||
OwnSecrets: mcat.OwnSecrets{"broker": {Path: "/var/lib/mesh/" + mcat.RuntimeModule + "/broker"}},
|
||||
Needed: map[string]map[string]string{"node-tools": {"broker": "sealed-credential"}, "telegram": {"broker": "own"}}}
|
||||
goRuntime := mcat.Manifest{Module: "node-tools", Version: "1", ToolRunner: true,
|
||||
OwnSecrets: mcat.OwnSecrets{"broker": {Path: "/var/lib/mesh/" + "node-tools" + "/broker"}},
|
||||
Build: &mcat.Build{Artifacts: []mcat.Artifact{{Name: "runtime", Kind: mcat.ArtifactBundle, Language: "go",
|
||||
System: "arch", From: "cmd/node-tools"}}}}
|
||||
goRuntime, err := goRuntime.Resolve([]mcat.Built{{Name: "runtime", Kind: mcat.ArtifactBundle,
|
||||
Reference: mcat.ArtifactStoreScheme + mcat.RuntimeModule + "/runtime/blobs/" + bundleDigest, Digest: bundleDigest}})
|
||||
Reference: mcat.ArtifactStoreScheme + "node-tools" + "/runtime/blobs/" + bundleDigest, Digest: bundleDigest}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -368,16 +380,16 @@ func TestAModuleOfItsOwnAccountIsRefusedTheOperatorsAndTheAgentsAccount(t *testi
|
||||
// files it holds for its consumers, and what its words name — belongs to that account, never the operator's.
|
||||
func TestWhatAModuleOfItsOwnAccountReadsIsItsAccounts(t *testing.T) {
|
||||
with := mcat.Rendering{ArtifactStore: "anchor.internal:5101",
|
||||
Needed: map[string]map[string]string{mcat.RuntimeModule: {"broker": "sealed-credential"}, "vault": {"broker": "own"}},
|
||||
Needed: map[string]map[string]string{"node-tools": {"broker": "sealed-credential"}, "vault": {"broker": "own"}},
|
||||
Kept: &mcat.KeptExport{Export: 1, OperatorKey: "k"},
|
||||
Grants: []mcat.Grant{{Provision: "secret", Consumer: "anchor", From: "telegram", Sealed: "sealed-to-anchor",
|
||||
Values: map[string]any{"name": "telegram"}}}}
|
||||
goRuntime := mcat.Manifest{Module: mcat.RuntimeModule, Version: "1",
|
||||
OwnSecrets: mcat.OwnSecrets{"broker": {Path: "/var/lib/mesh/" + mcat.RuntimeModule + "/broker"}},
|
||||
goRuntime := mcat.Manifest{Module: "node-tools", Version: "1", ToolRunner: true,
|
||||
OwnSecrets: mcat.OwnSecrets{"broker": {Path: "/var/lib/mesh/" + "node-tools" + "/broker"}},
|
||||
Build: &mcat.Build{Artifacts: []mcat.Artifact{{Name: "runtime", Kind: mcat.ArtifactBundle, Language: "go",
|
||||
System: "arch", From: "cmd/node-tools"}}}}
|
||||
goRuntime, err := goRuntime.Resolve([]mcat.Built{{Name: "runtime", Kind: mcat.ArtifactBundle,
|
||||
Reference: mcat.ArtifactStoreScheme + mcat.RuntimeModule + "/runtime/blobs/" + bundleDigest, Digest: bundleDigest}})
|
||||
Reference: mcat.ArtifactStoreScheme + "node-tools" + "/runtime/blobs/" + bundleDigest, Digest: bundleDigest}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user