Swaps three import paths for mesh-sdk's go/catalogue, go/overlay and go/validate (the same packages, copied from this repository at 6c2a8d62) and lets gofmt order the groups. The six swap commits compile together once the pin commit (go.mod, modules.txt) is on the branch; each alone does not, because a file on the SDK's types cannot call one still on the old package.
132 lines
5.7 KiB
Go
132 lines
5.7 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
"github.com/novox/mesh-controller/internal/broker"
|
|
"github.com/novox/mesh-controller/internal/conditions"
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
)
|
|
|
|
// The core's own credentials travel sealed by the vault to their machine (novox/hq ADR 0349 §16, issue 755; the lead's
|
|
// decision, issue 755 notes 4 and 5): to an engine that opens the vault's box, the send carries the box the vault
|
|
// answered the controller and never the controller's own seal; when the vault does not answer, a kept box is carried
|
|
// again and a module with none is sealed by the controller (ADR 0396, issue 908), with a condition naming the vault and the machine. An engine below the
|
|
// contract composes exactly as before, and the vault is not asked.
|
|
|
|
// coreStandIn answers `get` with a box per name and generation, or its silence.
|
|
type coreStandIn struct {
|
|
asked []string
|
|
keys []string
|
|
generation int
|
|
silent error
|
|
}
|
|
|
|
func (v *coreStandIn) ask(name, key string) (coreAnswer, error) {
|
|
v.asked, v.keys = append(v.asked, name), append(v.keys, key)
|
|
if v.silent != nil {
|
|
return coreAnswer{}, v.silent
|
|
}
|
|
box := `{"v":1,"eph":"ZQ==","iv":"aQ==","tag":"dA==","ct":"` + strings.Repeat("c", len(v.asked)) + `"}`
|
|
return coreAnswer{Name: name, Generation: v.generation, Sealed: json.RawMessage(box)}, nil
|
|
}
|
|
|
|
func withVault(t *testing.T, v *coreStandIn) {
|
|
t.Helper()
|
|
before := coreVault
|
|
coreVault = func(context.Context) coreAsk { return v.ask }
|
|
t.Cleanup(func() { coreVault = before })
|
|
}
|
|
|
|
func clientsBroker(resources []map[string]any) map[string]any {
|
|
for _, r := range resources {
|
|
if r["id"] == "mesh-provisioner-client.needs-"+broker.ProvisionerSecret {
|
|
return r
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func vaultSilence(t *testing.T, node string) (conditions.Condition, bool) {
|
|
t.Helper()
|
|
c, found, err := conditionsFrom.Get(t.Context(), vaultSilentObservation(node, coreBoxes{}).Key())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return c, found
|
|
}
|
|
|
|
// Issue 908: a machine at the vault-box contract, the vault silent, no kept box: nats is composed with the
|
|
// controller's own seal, never left out, and the vault-silent condition names it.
|
|
func TestReplay908(t *testing.T) {
|
|
key := "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8="
|
|
core := map[string][]string{"nats": {"password"}}
|
|
ask := func(name, publicKey string) (coreAnswer, error) { return coreAnswer{}, errors.New("no responders") }
|
|
got := coreBoxesOf("novox", key, core, nil, Allocating, ask)
|
|
if _, sealed := got.sealed["nats"]; sealed || len(got.notPlaced) != 0 {
|
|
t.Fatalf("nats is vault-sealed %v or not placed %v", got.sealed, got.notPlaced)
|
|
}
|
|
if !reflect.DeepEqual(got.byController, []string{"nats"}) {
|
|
t.Fatalf("nats is not left to the controller's seal: %+v", got)
|
|
}
|
|
o := vaultSilentObservation("novox", got)
|
|
if o.Severity != conditions.Warning || !strings.Contains(o.Summary, "sealed by the controller until the vault holds it: nats") {
|
|
t.Fatalf("the condition does not name nats: %+v", o)
|
|
}
|
|
// A kept box is still carried while the vault is silent.
|
|
kept := map[string]map[string]inventory.CoreBox{"nats": {"password": {SealedTo: key, Box: json.RawMessage(`{"v":1}`)}}}
|
|
got = coreBoxesOf("novox", key, core, kept, Allocating, ask)
|
|
if string(got.sealed["nats"]["password"]) != `{"v":1}` || len(got.byController) != 0 {
|
|
t.Fatalf("the kept box was not held: %+v", got)
|
|
}
|
|
}
|
|
|
|
// An engine below VaultBoxContract is sent the controller's own seal, as before, and the vault is not asked.
|
|
|
|
// Every question but a send reads the kept box and asks nothing; with none kept, a stand-in the next send asks for.
|
|
func TestOnlyASendAsksTheVault(t *testing.T) {
|
|
core := map[string][]string{"nats": {"broker"}}
|
|
key := aPublicKey(t)
|
|
kept := map[string]map[string]inventory.CoreBox{"nats": {"broker": {Module: "nats", Name: "broker",
|
|
Generation: 1, SealedTo: key, Box: json.RawMessage(`{"v":1}`)}}}
|
|
never := func(string, string) (coreAnswer, error) {
|
|
t.Fatal("the vault was asked by a question that is not a send")
|
|
return coreAnswer{}, nil
|
|
}
|
|
for _, choosing := range []Choosing{Reading, Foreseeing} {
|
|
if got := coreBoxesOf("anchor", key, core, kept, choosing, never); string(got.sealed["nats"]["broker"]) != `{"v":1}` {
|
|
t.Errorf("read %s", got.sealed["nats"]["broker"])
|
|
}
|
|
if got := coreBoxesOf("anchor", key, core, nil, choosing, never); string(got.sealed["nats"]["broker"]) !=
|
|
string(catalogue.ForeseenVaultBox) {
|
|
t.Errorf("with none kept, read %s", got.sealed["nats"]["broker"])
|
|
}
|
|
}
|
|
// A box kept for another key of the machine is never carried.
|
|
other := coreBoxesOf("anchor", aPublicKey(t), core, kept, Allocating,
|
|
func(string, string) (coreAnswer, error) { return coreAnswer{}, errors.New("silent") })
|
|
if _, vaultSealed := other.sealed["nats"]; vaultSealed || !reflect.DeepEqual(other.byController, []string{"nats"}) {
|
|
t.Fatalf("a box sealed to the machine's old key was carried: %+v", other)
|
|
}
|
|
}
|
|
|
|
// The core's modules on a machine: on the list, not handed, with their own secrets but one issued outside.
|
|
func TestTheCoresModulesAreTheListsWithTheirOwnSecrets(t *testing.T) {
|
|
modules := []catalogue.Manifest{
|
|
{Module: "nats", OwnSecrets: catalogue.OwnSecrets{"broker": {Path: "/a"}, "given": {Path: "/b",
|
|
IssuedBy: catalogue.IssuedOutside}}},
|
|
{Module: "postgres", OwnSecrets: catalogue.OwnSecrets{"superuser": {Path: "/c"}}},
|
|
{Module: "mesh-provisioner", OwnSecrets: catalogue.OwnSecrets{"broker": {Path: "/d"}}},
|
|
}
|
|
got := coreModulesOf(modules, func(m catalogue.Manifest) bool { return m.Module == "mesh-provisioner" })
|
|
if want := map[string][]string{"nats": {"broker"}}; !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("got %v, want %v", got, want)
|
|
}
|
|
}
|