Merge pull request 'A machine moves to the bus its declaration tells it to' (#34) from feat/a-machine-moves-to-the-bus-it-is-told into main
This commit was merged in pull request #34.
This commit is contained in:
+60
-1
@@ -869,6 +869,7 @@ func overlayCommand(ctx context.Context, opts options) error {
|
|||||||
if err := link.Publish(ctx, link.Membership{
|
if err := link.Publish(ctx, link.Membership{
|
||||||
Node: mine.Node, Broker: mine.Membership.Broker, Fingerprint: mine.Membership.Fingerprint,
|
Node: mine.Node, Broker: mine.Membership.Broker, Fingerprint: mine.Membership.Fingerprint,
|
||||||
Password: mine.Membership.Password, Signer: mine.Membership.Signer,
|
Password: mine.Membership.Password, Signer: mine.Membership.Signer,
|
||||||
|
Transport: mine.Membership.Transport,
|
||||||
}, link.Report{Node: mine.Node, Rekey: &rekey}, opts.timeout); err != nil {
|
}, link.Report{Node: mine.Node, Rekey: &rekey}, opts.timeout); err != nil {
|
||||||
return fmt.Errorf("the mesh could not be told; nothing was written here: %w", err)
|
return fmt.Errorf("the mesh could not be told; nothing was written here: %w", err)
|
||||||
}
|
}
|
||||||
@@ -973,7 +974,12 @@ func runLink(ctx context.Context, opts options) error {
|
|||||||
go sched.Run(ctx)
|
go sched.Run(ctx)
|
||||||
|
|
||||||
applier := func(ctx context.Context, raw, signature []byte) link.Report {
|
applier := func(ctx context.Context, raw, signature []byte) link.Report {
|
||||||
return applyAndKeep(ctx, opts, raw, &store.Declared{Declaration: raw, Signature: signature}, sched, say)
|
report := applyAndKeep(ctx, opts, raw, &store.Declared{Declaration: raw, Signature: signature}, sched, say)
|
||||||
|
// **A declaration may carry this machine's membership for another bus.** It arrives as a
|
||||||
|
// sealed file like any secret, and is read after the rest has applied so the bus it names is
|
||||||
|
// standing before this machine leaves the one it is on (novox/hq design 28, task 5.2).
|
||||||
|
adoptDeliveredMembership(identity.Path(opts.state), &mine, say)
|
||||||
|
return report
|
||||||
}
|
}
|
||||||
|
|
||||||
// Two things at once, and the second is what makes disconnection ordinary. The link brings
|
// Two things at once, and the second is what makes disconnection ordinary. The link brings
|
||||||
@@ -1008,6 +1014,7 @@ func runLink(ctx context.Context, opts options) error {
|
|||||||
Broker: mine.Membership.Broker,
|
Broker: mine.Membership.Broker,
|
||||||
Fingerprint: mine.Membership.Fingerprint,
|
Fingerprint: mine.Membership.Fingerprint,
|
||||||
Password: mine.Membership.Password,
|
Password: mine.Membership.Password,
|
||||||
|
Transport: mine.Membership.Transport,
|
||||||
Signer: mine.Membership.Signer,
|
Signer: mine.Membership.Signer,
|
||||||
}, applier, say, opts.timeout, rousedBySignal(ctx), outbox)
|
}, applier, say, opts.timeout, rousedBySignal(ctx), outbox)
|
||||||
}
|
}
|
||||||
@@ -1376,3 +1383,55 @@ func carriedPorts(state store.State) []int {
|
|||||||
sort.Ints(out)
|
sort.Ints(out)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MembershipNextPath is where the mesh delivers this machine's membership for the bus it is moving
|
||||||
|
// to: a sealed file in a declaration, written by the host like any secret, read here after the
|
||||||
|
// declaration has applied.
|
||||||
|
const MembershipNextPath = "/var/lib/mesh/membership-next.json"
|
||||||
|
|
||||||
|
// adoptDeliveredMembership moves this machine to the bus a delivered membership names.
|
||||||
|
//
|
||||||
|
// **Saved, then restarted — not swapped in place.** The link holds one membership for the life of
|
||||||
|
// the process, and every reconnect path assumes the bus did not change under it; a process that
|
||||||
|
// found itself half on one bus and half on another would be a new kind of state nothing was written
|
||||||
|
// for. Exiting cleanly hands the machine to the service manager's restart, and the process that
|
||||||
|
// comes back reads the identity file the way it always has and dials the bus it names. That is the
|
||||||
|
// same path a machine takes after a reboot, which is why nothing new has to be right for it to work.
|
||||||
|
//
|
||||||
|
// A membership identical to the one held is nothing: the file stays and is read again next time.
|
||||||
|
func adoptDeliveredMembership(identityPath string, mine *identity.Identity, say func(string)) {
|
||||||
|
raw, err := os.ReadFile(MembershipNextPath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var next identity.Membership
|
||||||
|
if err := json.Unmarshal(raw, &next); err != nil {
|
||||||
|
say(fmt.Sprintf("a membership was delivered at %s and could not be read: %v", MembershipNextPath, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if next.Broker == "" || next.Password == "" || next.Fingerprint == "" {
|
||||||
|
say(fmt.Sprintf("a membership was delivered at %s with no broker, fingerprint or password; ignored", MembershipNextPath))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
same := next.Broker == mine.Membership.Broker && next.Fingerprint == mine.Membership.Fingerprint &&
|
||||||
|
next.Password == mine.Membership.Password && next.Transport == mine.Membership.Transport
|
||||||
|
if same {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// The signer is the mesh's, not the bus's: a delivered membership that names none keeps the one
|
||||||
|
// this machine already trusts, because a change of bus is not a change of who signs declarations.
|
||||||
|
if len(next.Signer) == 0 {
|
||||||
|
next.Signer = mine.Membership.Signer
|
||||||
|
}
|
||||||
|
mine.Membership = next
|
||||||
|
if err := identity.Save(identityPath, *mine); err != nil {
|
||||||
|
say(fmt.Sprintf("a membership for another bus was delivered and could not be saved: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
transport := next.Transport
|
||||||
|
if transport == "" {
|
||||||
|
transport = "the current"
|
||||||
|
}
|
||||||
|
say(fmt.Sprintf("moving to %s bus at %s — restarting to dial it", transport, next.Broker))
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|||||||
@@ -76,6 +76,11 @@ type Membership struct {
|
|||||||
// Not the token's secret: that is spent, and a credential that lives for ever should not be
|
// Not the token's secret: that is spent, and a credential that lives for ever should not be
|
||||||
// the same string as one that was meant to be used once.
|
// the same string as one that was meant to be used once.
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
|
|
||||||
|
// Transport is which bus this membership is for. Empty is the bus the mesh ran on before
|
||||||
|
// the move — so every membership written before this field existed reads as correct, not as
|
||||||
|
// unset — and "nats" is the one being moved to (novox/hq design 28, task 5.2).
|
||||||
|
Transport string `json:"transport,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Queue is where this node listens. Its account may read this and nothing else.
|
// Queue is where this node listens. Its account may read this and nothing else.
|
||||||
|
|||||||
Reference in New Issue
Block a user