The controller plans config, templates and moved providers' provisions, hands bus accounts to the vault and judges the new manifest words, on the feature's SDK and node-engine validator (feature 57, config and provisioning).
130 lines
5.5 KiB
Go
130 lines
5.5 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/novox/mesh-host/validate"
|
|
)
|
|
|
|
// novox/hq ADR 0366: a module that runs as its own account declares its runtime's health under one manifest
|
|
// key, `runtime-health`, in the form a resource's `health` takes; the controller puts it on the process it
|
|
// writes for that runtime, and a tool check there stands beside the node-engine's judgement that the runtime
|
|
// is alive. A module that runs as no account of its own is refused the key.
|
|
|
|
// ownAccountManifest is a module of its own account with nothing long-running but its own runtime, given
|
|
// runtime-health (or none, when health is nil), and running as no account when runsAs is false.
|
|
func ownAccountManifest(t *testing.T, runsAs bool, health any) []byte {
|
|
t.Helper()
|
|
m := map[string]any{"module": "keeper", "tools": []any{"keeper_status", "keeper_health"},
|
|
"own-secrets": map[string]any{"broker": map[string]any{"path": "/var/lib/keeper/broker"}},
|
|
"resources": []any{map[string]any{"id": "account", "type": "user", "name": "keeper"}}}
|
|
if runsAs {
|
|
m["runs-as"], m["secrets-owner"] = "keeper", "keeper"
|
|
}
|
|
if health != nil {
|
|
m["runtime-health"] = health
|
|
}
|
|
raw, err := json.Marshal(m)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return raw
|
|
}
|
|
|
|
func TestAModuleOfItsOwnAccountDeclaresItsRuntimesHealthByItsOwnTool(t *testing.T) {
|
|
m, err := ParseManifest(ownAccountManifest(t, true, map[string]any{"kind": "tool", "tool": "keeper_health"}))
|
|
if err != nil {
|
|
t.Fatalf("a tool check on its own runtime was refused: %v", err)
|
|
}
|
|
if got := HealthChecks(m); len(got) != 1 || got[0] != "keeper.keeper_health" {
|
|
t.Errorf("the engine is granted %v, want keeper.keeper_health", got)
|
|
}
|
|
if r, has := OwnRuntimeHealth(m); !has || r["id"] != "keeper."+OwnRuntimeID() {
|
|
t.Errorf("its runtime's health reads as %v %v", r, has)
|
|
}
|
|
}
|
|
|
|
func TestRuntimeHealthIsRefusedWithoutAnAccountOfItsOwn(t *testing.T) {
|
|
_, err := ParseManifest(ownAccountManifest(t, false, map[string]any{"kind": "tool", "tool": "keeper_health"}))
|
|
if err == nil || !strings.Contains(err.Error(), "runtime-health") || !strings.Contains(err.Error(), "runs-as") {
|
|
t.Errorf("runtime-health without runs-as: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestARuntimeHealthIsHeldToTheHealthRules(t *testing.T) {
|
|
for _, c := range []struct {
|
|
health any
|
|
says string
|
|
}{
|
|
{map[string]any{"kind": "tool", "tool": "keeper_restart"}, "does not serve"},
|
|
{map[string]any{"kind": "tool", "tool": "keeper_health", "interval": "2s"}, "no more often"},
|
|
{map[string]any{"kind": "exec", "command": "true"}, "only a container"},
|
|
{map[string]any{"kind": "tool", "tool": "keeper_health", "port": 80}, "names a port"},
|
|
{"tool", "is an object"},
|
|
} {
|
|
_, err := ParseManifest(ownAccountManifest(t, true, c.health))
|
|
if err == nil || !strings.Contains(err.Error(), c.says) {
|
|
t.Errorf("%v: %v, want it refused saying %q", c.health, err, c.says)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 := Manifest{Module: RuntimeModule, Version: "1",
|
|
OwnSecrets: OwnSecrets{"broker": {Path: "/var/lib/mesh/" + RuntimeModule + "/broker"}},
|
|
Build: &Build{Artifacts: []Artifact{{Name: "runtime", Kind: ArtifactBundle, Language: "go",
|
|
System: "arch", From: "cmd/node-tools"}}}}
|
|
goRuntime, err := goRuntime.Resolve([]Built{{Name: "runtime", Kind: ArtifactBundle,
|
|
Reference: ArtifactStoreScheme + RuntimeModule + "/runtime/blobs/" + bundleDigest, Digest: bundleDigest}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
keeper := aToolsModule(t, "keeper", "tools/index.js")
|
|
keeper.Tools = []string{"keeper_health"}
|
|
keeper.RunsAs, keeper.SecretsOwner = "keeper", "keeper"
|
|
keeper.OwnSecrets = OwnSecrets{"broker": {Path: "/var/lib/keeper/broker"}}
|
|
keeper.RuntimeHealth = map[string]any{"kind": "tool", "tool": "keeper_health", "interval": "60s", "timeout": "10s",
|
|
"looks": 2.0}
|
|
for _, reads := range []bool{true, false} {
|
|
with := Rendering{ArtifactStore: "anchor.internal:5101", ReadsHealth: reads,
|
|
Needed: map[string]map[string]string{RuntimeModule: {"broker": "sealed"}, "keeper": {"broker": "own"}}}
|
|
out, err := Resolution{Node: "anchor", Account: "ops", Modules: []Manifest{keeper, goRuntime}}.Declaration(with)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
own := fileNamed(out, "keeper."+OwnRuntimeID())
|
|
if own == nil {
|
|
t.Fatalf("no own runtime: %v", ids(out))
|
|
}
|
|
h, has := own[HealthField].(map[string]any)
|
|
if !reads {
|
|
if has {
|
|
t.Errorf("health sent to an engine that does not read it: %v", h)
|
|
}
|
|
continue
|
|
}
|
|
if !has || h["kind"] != "tool" || h["tool"] != "keeper_health" || h["interval"] != "1m0s" ||
|
|
h["timeout"] != "10s" || h["looks"] != 2 || h["grace"] != "1m0s" {
|
|
t.Errorf("its own runtime's health: %v", own[HealthField])
|
|
}
|
|
if machine := fileNamed(out, RuntimeModule+"."+RuntimeProcessID()); machine[HealthField] != nil {
|
|
t.Errorf("the machine's runtime was given the module's health: %v", machine[HealthField])
|
|
}
|
|
body, err := json.Marshal(map[string]any{"declaration": validate.Version, "resources": out})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if problems := validate.Declaration(body); len(problems) > 0 {
|
|
t.Errorf("composed into something the node-engine refuses: %v", problems)
|
|
}
|
|
}
|
|
// The catalogue's own manifest is not written into.
|
|
if _, ok := keeper.RuntimeHealth.(map[string]any)["grace"]; ok {
|
|
t.Error("composing wrote the defaults into the catalogue's manifest")
|
|
}
|
|
}
|