Adopt genesis's bus through the bus's planned step, its streams snapshotted first (hq issue 590, ADR 0333 §3)
mesh/merge-gate head cdcfcd938016 merged into trunk 130c5d98a601 — pass: the change touches no module of the mesh's graph
mesh/repo-check head cdcfcd938016 merged into trunk 130c5d98a601 — pass: its merge-check.sh passed
mesh/delivery delivered

The broker step sent the nats module with a plain push, and the controller refuses a push that would
replace the bus: that is a planned step, bus upgrade, which snapshots the streams first and checks
them after (ADR 0236). Genesis stopped at step 18.

The step now snapshots every stream with the module's own snapshot program (no backup holder exists
yet to be asked), asks bus upgrade with that snapshot and why, says the one restart, and waits until
the bus runs the module's image, answers, and the controller ends the step done. The bundle's bus runs
the release the module's image is built on (2.11.17, by the same digest, where it ran 2.10.29), so the
step changes no server release and is said reversible.
This commit is contained in:
jochen
2026-10-13 11:47:10 +02:00
parent 130c5d98a6
commit cdcfcd9380
6 changed files with 374 additions and 19 deletions
+30 -2
View File
@@ -69,6 +69,7 @@ func aBusThatAdopts() (*asked, func(context.Context, string) (int, string, error
func busStepWith(t *testing.T, o Options, runtime *asked,
fetch func(context.Context, string) (int, string, error)) ([]string, error) {
t.Helper()
aGenesisBusOnDisk(t)
rewritten, err := Rewrite(theRealBundle(t), held)
if err != nil {
t.Fatal(err)
@@ -222,7 +223,7 @@ func TestTheBrokerStepSaysTheRestartAndWaitsForTheBusToAnswer(t *testing.T) {
runtime.answer = func(name string, args []string) (string, error) {
joined := strings.Join(args, " ")
switch {
case strings.Contains(joined, "push anchor"):
case strings.Contains(joined, "bus upgrade --why"):
pushed = true
case strings.Contains(joined, "inspect --format {{.Config.Image}} mesh-broker-nats"):
if !pushed {
@@ -304,7 +305,7 @@ func TestARerunAfterTheAdoptionSaysTheBusIsTheModulesAlready(t *testing.T) {
t.Fatal(err)
}
text := strings.Join(said, "\n")
if strings.Contains(text, "restarting") || !strings.Contains(text, "already restarted mesh-broker-nats") {
if strings.Contains(text, "restarting") || !strings.Contains(text, "already adopted mesh-broker-nats") {
t.Errorf("a re-run announced a restart, or did not say the bus is the module's already:\n%s", text)
}
}
@@ -417,3 +418,30 @@ func TestTheBundlesBusFilesAreSeeds(t *testing.T) {
}
}
}
// aGenesisBusOnDisk puts what the adoption reads on the machine in a test's own directory: the bus's
// certificate, the controller's bus credential, and where the snapshot goes. The step's wait is short.
func aGenesisBusOnDisk(t *testing.T) {
t.Helper()
dir := t.TempDir()
cert := filepath.Join(dir, "tls.crt")
if err := os.WriteFile(cert, []byte(aCertificatePEM), 0o644); err != nil {
t.Fatal(err)
}
secret := filepath.Join(dir, "bus-controller.secret")
if err := os.WriteFile(secret, []byte("BUS-PW"), 0o600); err != nil {
t.Fatal(err)
}
was := []string{busSnapshotFile, busSnapshotCredential, busCertificateFile, busControllerSecret}
wasWithin := busStepWithin
busSnapshotFile, busSnapshotCredential = filepath.Join(dir, "bus.tar"), filepath.Join(dir, "bus.credential")
busCertificateFile, busControllerSecret = cert, secret
busStepWithin = 50 * time.Millisecond
t.Cleanup(func() {
busSnapshotFile, busSnapshotCredential, busCertificateFile, busControllerSecret = was[0], was[1], was[2], was[3]
busStepWithin = wasWithin
})
}
// aCertificatePEM is a PEM block standing for the bus's certificate; its pin is the sha256 of these bytes.
const aCertificatePEM = "-----BEGIN CERTIFICATE-----\nAAECAwQFBgcICQ==\n-----END CERTIFICATE-----\n"