The installer lets the first node onto the bus it raised
At genesis the bus's users reach it in no declaration, because the machine running it has not enrolled. The installer, which raised the bus from its bundle, places the control plane's composed list beside it and makes it re-read it: before the machine enrols, for the token's account, and after, for the node's own (novox/hq issue 146).
This commit is contained in:
@@ -119,6 +119,11 @@ func Enrol(ctx context.Context, o Options, sys system.System, control controlPla
|
|||||||
// its ports and range on and not another that came up since.
|
// its ports and range on and not another that came up since.
|
||||||
args = append(args, "--tunnel", o.Tunnel)
|
args = append(args, "--tunnel", o.Tunnel)
|
||||||
}
|
}
|
||||||
|
// The enrolment account the token's secret is the password of exists in the mesh's records
|
||||||
|
// and nowhere on the bus yet (novox/hq 04-ISSUES/146): placed before the machine presents it.
|
||||||
|
if err := placeTheBusUsers(ctx, control, say); err != nil {
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
joined, err := control.run(joining, o.Host, args...)
|
joined, err := control.run(joining, o.Host, args...)
|
||||||
cancel()
|
cancel()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -137,6 +142,10 @@ func Enrol(ctx context.Context, o Options, sys system.System, control controlPla
|
|||||||
}
|
}
|
||||||
out.Joined = true
|
out.Joined = true
|
||||||
say(" enrolled as " + o.Node)
|
say(" enrolled as " + o.Node)
|
||||||
|
// And the node's own account, minted as it enrolled, before its agent connects as it.
|
||||||
|
if err := placeTheBusUsers(ctx, control, say); err != nil {
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. The agent.
|
// 4. The agent.
|
||||||
@@ -148,6 +157,40 @@ func Enrol(ctx context.Context, o Options, sys system.System, control controlPla
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// busAccounts and busContainer are where the installer's bundle raises the bus: the file its
|
||||||
|
// configuration includes, and the container that reads it. The installer raised them, so it is the
|
||||||
|
// one that knows them (examples/foundation-first-node-nats.lock).
|
||||||
|
const (
|
||||||
|
busAccounts = "/var/lib/mesh-bus-conf/accounts.conf"
|
||||||
|
busContainer = "mesh-broker"
|
||||||
|
)
|
||||||
|
|
||||||
|
// placeTheBusUsers writes the mesh's composed user list beside the bus the installer raised, and makes
|
||||||
|
// the bus re-read it.
|
||||||
|
//
|
||||||
|
// **Genesis's own step** (novox/hq 04-ISSUES/146). Every account on the bus reaches it in the
|
||||||
|
// declaration of the machine that runs it — which needs that machine to be an enrolled node, and at
|
||||||
|
// genesis it is not. The control plane composes the list and says it; whoever raised the bus places
|
||||||
|
// it. That is this installer: it carried the bus in its bundle, so it knows where the bus reads it,
|
||||||
|
// and the control plane never has to.
|
||||||
|
func placeTheBusUsers(ctx context.Context, control controlPlane, say func(string)) error {
|
||||||
|
users, err := control.tell(ctx, "broker", "accounts")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("the control plane would not say the bus's users, so no machine could "+
|
||||||
|
"join it: %w", err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(users, "accounts") {
|
||||||
|
return fmt.Errorf("the control plane's account of the bus's users is not one:\n%s", indent(users))
|
||||||
|
}
|
||||||
|
script := "umask 077 && cat > " + busAccounts + ".next <<'MESHBUSUSERS'\n" + users + "\nMESHBUSUSERS\n" +
|
||||||
|
"mv " + busAccounts + ".next " + busAccounts + " && docker kill -s HUP " + busContainer + " >/dev/null"
|
||||||
|
if out, err := control.run(ctx, "sh", "-c", script); err != nil {
|
||||||
|
return fmt.Errorf("the bus's users could not be placed at %s: %w\n%s", busAccounts, err, indent(out))
|
||||||
|
}
|
||||||
|
say(" bus users placed")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// runTheHost makes sure something on this machine is listening to the mesh, and proves it.
|
// runTheHost makes sure something on this machine is listening to the mesh, and proves it.
|
||||||
//
|
//
|
||||||
// **The installer does not install the service, and says so.** A unit file is a packaging decision
|
// **The installer does not install the service, and says so.** A unit file is a packaging decision
|
||||||
|
|||||||
@@ -261,3 +261,56 @@ func TestAListingIsMatchedByNameAndNotBySubstring(t *testing.T) {
|
|||||||
t.Error("registry-mirror was not found")
|
t.Error("registry-mirror was not found")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// **Genesis places the bus's users, before the machine enrols and again after** (novox/hq
|
||||||
|
// 04-ISSUES/146). The enrolment account exists only in the mesh's records until somebody writes it
|
||||||
|
// beside the bus; so does the node's own, minted as it enrols. Without the first, the machine is
|
||||||
|
// refused by the bus it just raised; without the second, its agent is.
|
||||||
|
func TestAFirstNodeIsLetOntoTheBusItRaised(t *testing.T) {
|
||||||
|
enrolled := false
|
||||||
|
runtime := &asked{answer: func(name string, args []string) (string, error) {
|
||||||
|
joined := strings.Join(args, " ")
|
||||||
|
switch {
|
||||||
|
case strings.Contains(joined, "node list"):
|
||||||
|
if enrolled {
|
||||||
|
return "anchor here 01J0\n", nil
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
case strings.Contains(joined, "node add"):
|
||||||
|
return "added anchor\n", nil
|
||||||
|
case strings.Contains(joined, "token issue"):
|
||||||
|
return "a token for anchor, good once:\n\n " + strings.Repeat("t", 240) + "\n\n", nil
|
||||||
|
case strings.Contains(joined, "broker accounts"):
|
||||||
|
return "accounts {\n MESH { users = [] }\n}\n", nil
|
||||||
|
case name == "/usr/local/bin/mesh-host":
|
||||||
|
enrolled = true
|
||||||
|
return "enrolled as anchor\n", nil
|
||||||
|
case name == "pgrep", name == "sh":
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("unexpected: %s %v", name, args)
|
||||||
|
}}
|
||||||
|
|
||||||
|
if _, err := Enrol(context.Background(), Options{
|
||||||
|
Node: "anchor", State: filepath.Join(t.TempDir(), "state.json"), Timeout: time.Second,
|
||||||
|
Host: "/usr/local/bin/mesh-host", HostInBackground: true,
|
||||||
|
}, arch(t), controlPlane{container: "temp-mesh-controller", run: runtime.run, timeout: time.Second},
|
||||||
|
func(string) {}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
placed, enrol := []int{}, -1
|
||||||
|
for i, c := range runtime.commands {
|
||||||
|
if strings.HasPrefix(c, "sh -c") && strings.Contains(c, "kill -s HUP mesh-broker") &&
|
||||||
|
strings.Contains(c, "/var/lib/mesh-bus-conf/accounts.conf") {
|
||||||
|
placed = append(placed, i)
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(c, "/usr/local/bin/mesh-host enrol") {
|
||||||
|
enrol = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if enrol < 0 || len(placed) != 2 || placed[0] > enrol || placed[1] < enrol {
|
||||||
|
t.Fatalf("the bus's users were not placed before the machine enrolled and again after "+
|
||||||
|
"(placed at %v, enrolled at %d):\n%s", placed, enrol, strings.Join(runtime.commands, "\n"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user