The packages step sealed the build agent its genesis document and pushed, and the push returned as soon as it was sent. The sdk step then asked for the SDK's build at once, and the agent reads the document from the file the node writes when it applies that push: one genesis found it in time, the next was refused for having neither a binding nor the document. That one push now waits until the node reports it applied it (push --wait); every other push still returns on sending.
135 lines
5.1 KiB
Go
135 lines
5.1 KiB
Go
package bootstrap
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// Defends novox/hq issue 608 (ADR 0331 §5, ADR 0333 §4): until the bus is adopted as the nats module,
|
|
// every push genesis makes is followed by the installer placing the composed user list where the bus
|
|
// reads it, and making the bus re-read it. The build agent's account was minted at step 13 and sealed
|
|
// with that push, and the list was placed only at enrolment, so the bus refused the agent
|
|
// ("Authorization Violation") and genesis stopped at step 15.
|
|
|
|
// placing answers the controller as a mesh whose bus is or is not the nats module yet, and records
|
|
// what was run, in order.
|
|
func placing(adopted bool) (*asked, controlPlane) {
|
|
listed := "build-agent 1\n"
|
|
if adopted {
|
|
listed += "nats 1\n"
|
|
}
|
|
runtime := &asked{}
|
|
runtime.answer = func(name string, args []string) (string, error) {
|
|
joined := strings.Join(args, " ")
|
|
switch {
|
|
case name == "sh":
|
|
return "", nil
|
|
case strings.HasSuffix(joined, "module list"):
|
|
return listed, nil
|
|
case strings.HasSuffix(joined, "broker accounts"):
|
|
return "accounts {\n MESH { users = [ { user: \"anchor.build-agent\" } ] }\n}\n", nil
|
|
}
|
|
return "", nil
|
|
}
|
|
return runtime, controlPlane{container: "mesh-controller", run: runtime.run, timeout: time.Second}
|
|
}
|
|
|
|
// placedAfterThePush is whether the bus's users were placed — written at the module's bus-users path,
|
|
// the bus told to re-read them — after the push, and how often.
|
|
func placedAfterThePush(t *testing.T, commands []string) int {
|
|
t.Helper()
|
|
pushed, placed := -1, 0
|
|
for i, c := range commands {
|
|
if strings.Contains(c, "/mesh-controller push anchor") && pushed < 0 {
|
|
pushed = i
|
|
}
|
|
if strings.HasPrefix(c, "sh -c") && strings.Contains(c, busAccounts) &&
|
|
strings.Contains(c, "kill -s HUP "+busContainer) {
|
|
if pushed < 0 || i < pushed {
|
|
t.Fatalf("the bus's users were placed before the push that carries the account: %v", commands)
|
|
}
|
|
placed++
|
|
}
|
|
}
|
|
if pushed < 0 {
|
|
t.Fatalf("nothing was pushed: %v", commands)
|
|
}
|
|
return placed
|
|
}
|
|
|
|
func TestTheBusHearsTheBuildAgentsAccountAfterItsPush(t *testing.T) {
|
|
t.Setenv("TMPDIR", t.TempDir())
|
|
runtime, control := placing(false)
|
|
o := Options{Node: "anchor", Wait: time.Second}
|
|
if _, err := installModule(context.Background(), o, control, BuilderModule, []byte(`{}`), quietly); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n := placedAfterThePush(t, runtime.commands); n != 1 {
|
|
t.Fatalf("the bus's users were placed %d time(s) after the build agent's push; once: %v", n, runtime.commands)
|
|
}
|
|
}
|
|
|
|
func TestTheGenesisDocumentsPushIsHeardByTheBusToo(t *testing.T) {
|
|
t.Setenv("TMPDIR", t.TempDir())
|
|
runtime, control := placing(false)
|
|
o := Options{Node: "anchor", Wait: time.Second}
|
|
if err := deliverGenesisRegistry(context.Background(), o, control, genesisRegistry(DefaultPorts(), "pw"), quietly); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n := placedAfterThePush(t, runtime.commands); n != 1 {
|
|
t.Fatalf("the bus's users were placed %d time(s) after the genesis document's push; once: %v", n, runtime.commands)
|
|
}
|
|
}
|
|
|
|
// From the adoption on the nats module writes the list, and the installer does not (ADR 0333 §4).
|
|
func TestOnceTheBusIsTheNatsModulesThePushPlacesNothing(t *testing.T) {
|
|
t.Setenv("TMPDIR", t.TempDir())
|
|
runtime, control := placing(true)
|
|
o := Options{Node: "anchor", Wait: time.Second}
|
|
if _, err := installModule(context.Background(), o, control, BuilderModule, []byte(`{}`), quietly); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n := placedAfterThePush(t, runtime.commands); n != 0 {
|
|
t.Fatalf("the installer placed the bus's users over the nats module's: %v", runtime.commands)
|
|
}
|
|
}
|
|
|
|
// Defends novox/hq issue 615: the packages step's push is waited for until the node applied it, because
|
|
// the sdk step right after asks the build agent for a build that reads the genesis document from the
|
|
// file that apply writes. A push that returned on sending let the ask race the apply.
|
|
func TestThePackagesStepWaitsUntilTheNodeAppliedTheGenesisDocument(t *testing.T) {
|
|
t.Setenv("TMPDIR", t.TempDir())
|
|
runtime, control := placing(false)
|
|
o := Options{Node: "anchor", Wait: 3 * time.Minute}
|
|
if err := deliverGenesisRegistry(context.Background(), o, control, genesisRegistry(DefaultPorts(), "pw"), quietly); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, c := range runtime.commands {
|
|
if strings.Contains(c, "/mesh-controller push anchor") {
|
|
if !strings.HasSuffix(c, "push anchor --wait 3m0s") {
|
|
t.Fatalf("the genesis document's push does not wait for the node to apply it: %s", c)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
t.Fatalf("the genesis document was never pushed: %v", runtime.commands)
|
|
}
|
|
|
|
// Every other push of genesis still returns on sending: only a push whose content the next step reads on
|
|
// the machine waits.
|
|
func TestAnOrdinaryPushDoesNotWait(t *testing.T) {
|
|
t.Setenv("TMPDIR", t.TempDir())
|
|
runtime, control := placing(false)
|
|
o := Options{Node: "anchor", Wait: time.Second}
|
|
if _, err := installModule(context.Background(), o, control, BuilderModule, []byte(`{}`), quietly); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, c := range runtime.commands {
|
|
if strings.Contains(c, "push anchor --wait") {
|
|
t.Fatalf("an ordinary push waits: %s", c)
|
|
}
|
|
}
|
|
}
|