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).
158 lines
6.4 KiB
Go
158 lines
6.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/novox/mesh-controller/internal/broker"
|
|
"github.com/novox/mesh-controller/internal/catalogue"
|
|
)
|
|
|
|
// Every other test mints without a vault to hand to: a stand-in that takes anything, replaced by the tests below.
|
|
func init() {
|
|
vaultAskerForIssue = func(context.Context) vaultAsker { return newStandInVault(nil).ask }
|
|
}
|
|
|
|
// A covered account is handed to the vault when it is minted (novox/hq ADR 0374, issue 787): the whole value, sealed
|
|
// to the key the vault made for the call, with origin issued. A module that is neither core nor moved hands nothing.
|
|
func TestModuleIssueHandsACoveredAccountToTheVaultAndOnlyThat(t *testing.T) {
|
|
open := aMesh(t)
|
|
ctx := t.Context()
|
|
theVaultIsInstalled(t, open)
|
|
inv := open.inventory
|
|
known := broker.Broker{Fingerprint: "ab"}
|
|
|
|
core := aProvisioner()
|
|
core.Module = "nats"
|
|
moved := aBot("moved-bot", true)
|
|
moved.OwnSecrets["broker"] = catalogue.OwnSecret{Path: "/var/lib/moved-bot/broker"}
|
|
plain := aBot("kept-bot", false)
|
|
plain.OwnSecrets["broker"] = catalogue.OwnSecret{Path: "/var/lib/kept-bot/broker"}
|
|
|
|
vault := newStandInVault(t)
|
|
vaultAskerForIssue = func(context.Context) vaultAsker { return vault.ask }
|
|
t.Cleanup(func() {
|
|
vaultAskerForIssue = func(context.Context) vaultAsker { return newStandInVault(nil).ask }
|
|
})
|
|
|
|
for _, m := range []catalogue.Manifest{core, moved, plain} {
|
|
register(t, open, m)
|
|
if _, err := assign(ctx, open, "laptop", m.Module); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
issue := func(m catalogue.Manifest, password string) {
|
|
t.Helper()
|
|
if err := issueWith(ctx, inv, m, "laptop", "", known, "10.0.0.1:4222", "module."+m.Module+".laptop", password); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
issue(core, "first-core-password")
|
|
issue(moved, "first-moved-password")
|
|
issue(plain, "kept-password")
|
|
if err := issueProvisionerClient(ctx, inv, core, "laptop", known, "10.0.0.1:4222"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
said := vault.said()
|
|
for _, want := range []string{`"module":"nats"`, `"module":"moved-bot"`, `"name":"broker"`,
|
|
`"name":"` + broker.ProvisionerSecret + `"`, `"node":"laptop"`} {
|
|
if !strings.Contains(said, want) {
|
|
t.Errorf("the vault was told %s, without %s", said, want)
|
|
}
|
|
}
|
|
if n := strings.Count(said, `"origin":"issued"`); n != 3 {
|
|
t.Errorf("%d accounts were handed with origin issued, want 3 (the core's two and the moved module's): %s", n, said)
|
|
}
|
|
if strings.Contains(said, "kept-bot") {
|
|
t.Errorf("an account that is neither core nor moved was handed to the vault: %s", said)
|
|
}
|
|
if strings.Contains(said, "first-core-password") || strings.Contains(said, "first-moved-password") {
|
|
t.Errorf("a value crossed the bus in the clear: %s", said)
|
|
}
|
|
|
|
// A re-issue hands the new value, which the vault opens: the whole {url, fingerprint, node, user, password}.
|
|
issue(moved, "second-moved-password")
|
|
if got := vault.taken["accept"]; !strings.Contains(got, "second-moved-password") ||
|
|
!strings.Contains(got, `"user":"module.moved-bot.laptop"`) || !strings.Contains(got, `"node":"laptop"`) ||
|
|
!strings.Contains(got, `"url":"nats://10.0.0.1:4222"`) || !strings.Contains(got, `"fingerprint":"ab"`) {
|
|
t.Errorf("the vault was handed %q after a re-issue, not the new account whole", got)
|
|
}
|
|
}
|
|
|
|
// A vault that does not take the value fails the issue loudly: the bus's user list holds the new hash already.
|
|
func TestModuleIssueFailsWhenTheVaultDoesNotTakeACoveredAccount(t *testing.T) {
|
|
open := aMesh(t)
|
|
ctx := t.Context()
|
|
moved := aBot("moved-bot", true)
|
|
moved.OwnSecrets["broker"] = catalogue.OwnSecret{Path: "/var/lib/moved-bot/broker"}
|
|
theVaultIsInstalled(t, open)
|
|
register(t, open, moved)
|
|
if _, err := assign(ctx, open, "laptop", moved.Module); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
vault := newStandInVault(t)
|
|
vault.refuse = errors.New("the vault is not there")
|
|
vaultAskerForIssue = func(context.Context) vaultAsker { return vault.ask }
|
|
t.Cleanup(func() {
|
|
vaultAskerForIssue = func(context.Context) vaultAsker { return newStandInVault(nil).ask }
|
|
})
|
|
err := issueWith(ctx, open.inventory, moved, "laptop", "", broker.Broker{Fingerprint: "ab"}, "10.0.0.1:4222",
|
|
"module.moved-bot.laptop", "pw")
|
|
if err == nil || !strings.Contains(err.Error(), "vault did not take") {
|
|
t.Fatalf("a vault that took nothing was reported as %v", err)
|
|
}
|
|
}
|
|
|
|
func theVaultIsInstalled(t *testing.T, open *stores) {
|
|
t.Helper()
|
|
vault := catalogue.Manifest{Module: "mesh-vault", Version: "1"}
|
|
register(t, open, vault)
|
|
if _, err := assign(t.Context(), open, "laptop", vault.Module); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// In genesis the bus's and the provisioner's accounts are issued before the vault is installed (ADR 0113 §15 to §18):
|
|
// there is no vault to hand to, so nothing is asked and nothing fails, and the value stays in the controller's store
|
|
// as before, for the move to carry into the vault. Once the vault is installed, one that does not answer fails.
|
|
func TestModuleIssueBeforeTheVaultIsInstalledKeepsTheValueAndHandsNothing(t *testing.T) {
|
|
open := aMesh(t)
|
|
ctx := t.Context()
|
|
inv := open.inventory
|
|
known := broker.Broker{Fingerprint: "ab"}
|
|
core := aProvisioner()
|
|
core.Module = "nats"
|
|
register(t, open, core)
|
|
if _, err := assign(ctx, open, "laptop", core.Module); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
vault := newStandInVault(t)
|
|
vault.refuse = errors.New("there is no vault yet")
|
|
vaultAskerForIssue = func(context.Context) vaultAsker { return vault.ask }
|
|
t.Cleanup(func() {
|
|
vaultAskerForIssue = func(context.Context) vaultAsker { return newStandInVault(nil).ask }
|
|
})
|
|
|
|
if err := issueWith(ctx, inv, core, "laptop", "", known, "10.0.0.1:4222", "module.nats.laptop", "pw"); err != nil {
|
|
t.Fatalf("genesis's issue of the bus's account failed with no vault installed: %v", err)
|
|
}
|
|
if err := issueProvisionerClient(ctx, inv, core, "laptop", known, "10.0.0.1:4222"); err != nil {
|
|
t.Fatalf("genesis's issue of the client's account failed with no vault installed: %v", err)
|
|
}
|
|
if said := vault.said(); said != "" {
|
|
t.Errorf("the vault was asked before it was installed: %s", said)
|
|
}
|
|
if _, held, err := inv.ModuleSecretIfIssued(ctx, "laptop", "nats", "broker"); err != nil || !held {
|
|
t.Errorf("the value is not kept in the controller's store (held %v, %v)", held, err)
|
|
}
|
|
|
|
theVaultIsInstalled(t, open)
|
|
if err := issueWith(ctx, inv, core, "laptop", "", known, "10.0.0.1:4222", "module.nats.laptop", "pw2"); err == nil {
|
|
t.Error("a vault that is installed and does not answer did not fail the issue")
|
|
}
|
|
}
|