Merge pull request 'Place the bus's users after every push until the bus is adopted (issue 608)' (#103) from fix/608-place-the-bus-users-after-every-push into main
This commit was merged in pull request #103.
This commit is contained in:
@@ -146,6 +146,15 @@ func pushNode(ctx context.Context, o Options, control controlPlane, say func(str
|
||||
"already registered and try the push again", err)
|
||||
}
|
||||
say(" pushed " + o.Node)
|
||||
// **Every push may carry a new bus user, and until the adoption the bus hears of it only from here**
|
||||
// (novox/hq issue 608, ADR 0331 §5, ADR 0333 §4). A module's account is minted into the mesh's
|
||||
// records and sealed to this node with the push; the bus reads its user list from a file, and until
|
||||
// the broker step adopts the bus as the nats module nothing but this installer writes that file. The
|
||||
// list was placed only when the node enrolled, so the build agent's account (step 13) never reached
|
||||
// the bus and the agent was refused ("Authorization Violation") until the sdk step gave up.
|
||||
if err := placeUntilAdopted(ctx, control, say); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(said), nil
|
||||
}
|
||||
|
||||
|
||||
@@ -413,11 +413,10 @@ func deliverGenesisRegistry(ctx context.Context, o Options, control controlPlane
|
||||
if _, err := control.tell(ctx, "secret", "accept", o.Node, BuilderModule, GenesisRegistrySecret, "--from", at); err != nil {
|
||||
return err
|
||||
}
|
||||
// Push so the sealed document reaches the build agent, which reads it at its next build.
|
||||
if _, err := control.tell(ctx, "push", o.Node); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
// Push so the sealed document reaches the build agent, which reads it at its next build — and,
|
||||
// like every push before the adoption, the bus hears the users it carries (issue 608).
|
||||
_, err = pushNode(ctx, o, control, say)
|
||||
return err
|
||||
}
|
||||
|
||||
// PublishTheSDK dispatches a build of the SDK to the build agent. The agent has the genesis document by
|
||||
|
||||
@@ -239,6 +239,12 @@ func (c *controlRecorder) run(_ context.Context, name string, args ...string) (s
|
||||
}
|
||||
if name == "docker" && args[0] == "exec" {
|
||||
c.told = append(c.told, strings.Join(args[3:], " "))
|
||||
if strings.Join(args[3:], " ") == "broker accounts" {
|
||||
return "accounts {}\n", nil // the composed user list, placed after every push (issue 608)
|
||||
}
|
||||
}
|
||||
if name == "sh" {
|
||||
c.told = append(c.told, "placed the bus's users")
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
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.HasSuffix(c, "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)
|
||||
}
|
||||
}
|
||||
@@ -58,8 +58,12 @@ func aMeshThatAgrees(answers map[string]string) func(string, []string) (string,
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case name == "sh" && len(args) == 2 && strings.Contains(args[1], "accounts.conf"):
|
||||
return "", nil // the bus's users placed after a push (issue 608)
|
||||
case name != "docker":
|
||||
return "", fmt.Errorf("unexpected program %q", name)
|
||||
case strings.HasSuffix(joined, "broker accounts"):
|
||||
return "accounts {}\n", nil
|
||||
case args[0] == "cp":
|
||||
return "", nil
|
||||
case args[0] == "container":
|
||||
|
||||
Reference in New Issue
Block a user