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)
|
"already registered and try the push again", err)
|
||||||
}
|
}
|
||||||
say(" pushed " + o.Node)
|
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
|
return strings.TrimSpace(said), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -413,12 +413,11 @@ func deliverGenesisRegistry(ctx context.Context, o Options, control controlPlane
|
|||||||
if _, err := control.tell(ctx, "secret", "accept", o.Node, BuilderModule, GenesisRegistrySecret, "--from", at); err != nil {
|
if _, err := control.tell(ctx, "secret", "accept", o.Node, BuilderModule, GenesisRegistrySecret, "--from", at); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Push so the sealed document reaches the build agent, which reads it at its next build.
|
// Push so the sealed document reaches the build agent, which reads it at its next build — and,
|
||||||
if _, err := control.tell(ctx, "push", o.Node); err != nil {
|
// like every push before the adoption, the bus hears the users it carries (issue 608).
|
||||||
|
_, err = pushNode(ctx, o, control, say)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PublishTheSDK dispatches a build of the SDK to the build agent. The agent has the genesis document by
|
// PublishTheSDK dispatches a build of the SDK to the build agent. The agent has the genesis document by
|
||||||
// now (novox/hq ADR 0336 §7), so the build's `npm publish` authenticates; the artifact is a `package`, built
|
// now (novox/hq ADR 0336 §7), so the build's `npm publish` authenticates; the artifact is a `package`, built
|
||||||
|
|||||||
@@ -239,6 +239,12 @@ func (c *controlRecorder) run(_ context.Context, name string, args ...string) (s
|
|||||||
}
|
}
|
||||||
if name == "docker" && args[0] == "exec" {
|
if name == "docker" && args[0] == "exec" {
|
||||||
c.told = append(c.told, strings.Join(args[3:], " "))
|
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
|
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 {
|
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":
|
case name != "docker":
|
||||||
return "", fmt.Errorf("unexpected program %q", name)
|
return "", fmt.Errorf("unexpected program %q", name)
|
||||||
|
case strings.HasSuffix(joined, "broker accounts"):
|
||||||
|
return "accounts {}\n", nil
|
||||||
case args[0] == "cp":
|
case args[0] == "cp":
|
||||||
return "", nil
|
return "", nil
|
||||||
case args[0] == "container":
|
case args[0] == "container":
|
||||||
|
|||||||
Reference in New Issue
Block a user