slice 7 Phase 1: unified bring-up reconcile job (provision + guest-loss DR) (v0.8.0)

The shared front half of provision and guest-loss DR as a journaled reconcile job
(internal/reconcile/bringup.go), mirroring the restore-test's crash-safety but keeping
the guest on success and applying a scenario-specific identity policy. Agent-only; no
hub/wire change. Grounded by the slice-7 bring-up spike (commit 3342993): F1/F3/F4.

- RunBringUp: restore -> reset identity -> size -> attach mounts -> start link-up;
  verdict is liveness (waitRunning), success KEEPS the guest.
- identity policy: provision = fresh MAC (net0 sans hwaddr -> PVE regen) + hostname,
  host-side; machine-id/host-keys regenerate guest-side (systemd + baked golden unit).
  dr_guest_loss = preserve continuity (keep hostname; keep MAC unless KeepMAC=false).
- compensating rollback: mid-flight failure destroys the just-created guest
  (SameTxnCreated provenance, gated); new Rollback journal flag + Recover.recoverBringUp
  reap a half-built guest from a crash.
- F4: coalesced config PUT + bounded retry on the transient PVE config-lock 500 only.
- --selftest=bring-up (mode/archive/vmid/hostname/keep).
- configs/build-golden.sh: validated golden recipe incl. the F3 first-boot host-key unit.
- doc-03 §9 + identity-reset settled/implemented.

Deferred (stated): provisioning back half -> slice 8; host-loss DR + escrow consumption
and the BringUpSpec source (hub desired-state) -> slice 10.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-09 21:27:49 +02:00
parent 9f6753de0f
commit 57405c1a99
10 changed files with 1184 additions and 73 deletions
+50
View File
@@ -3,6 +3,56 @@
All notable changes to **felhom-agent** are recorded here. Update on every code
change that gets pushed.
## v0.8.0 — slice 7 Phase 1: unified bring-up reconcile job (provision + guest-loss DR) (2026-06-09)
The shared FRONT HALF of provision and guest-loss DR, as a journaled reconcile job mirroring the
slice-6 restore-test's crash-safety — but it KEEPS the guest on success and applies a
scenario-specific identity policy. Agent-only; no hub/wire change (the new guest auto-appears in
the host-report via `ListLXC`). Grounded by the slice-7 bring-up spike findings (commit `3342993`):
F1 (restore preserves the archived MAC → provision reset is unconditional), F3 (SSH host keys do
not auto-regenerate → a baked golden first-boot unit, not an agent guest-internal op), F4 (the
transient PVE config-lock 500 → bounded retry).
### Added
- **`reconcile.RunBringUp`** (`bringup.go`) — `BringUpSpec` (Mode `provision`|`dr_guest_loss`,
Archive, VMID, RestoreStorage, Hostname, Cores/MemoryMB, RootfsGrowGB, Mounts, KeepMAC,
BootTimeout) → `BringUpResult` (VMID, AssignedMAC, Pass, Verified, StartWarnings/Recognized).
Sequence (each mutation preceded by journaling the owning entry): restore → identity reset →
size → attach mounts → start LINK-UP. **Verdict is liveness (`waitRunning`), never the start
exitstatus** (reuses the v0.7.0 WARNINGS surface). **Success KEEPS the guest** (no teardown).
- **Scenario-specific identity reset** (doc 03 §9): *provision* → fresh MAC unconditionally
(`PUT net0` with `hwaddr` omitted → PVE regenerates, F1) + hostname; machine-id + SSH host keys
regenerate guest-side on first boot (golden bake + the new unit) — the agent does NOT touch
guest internals. *dr_guest_loss* → preserve continuity (keep hostname; keep MAC unless
`KeepMAC=false`); never resets restic/tunnel/hub identity.
- **Compensating rollback** — any mid-flight failure destroys the just-created guest
(`ClassGuestDestroy`, benign via `Provenance{SameTxnCreated:true}`, gated); on teardown failure
the entry is left in-flight for `Recover`. New journal flag **`Rollback`** + `Recover`'s
`recoverBringUp` reap a half-built guest left by a mid-job crash (idempotent, via `ListLXC`).
- **F4 config-lock retry** — steps 3+5 coalesced into ONE `PUT config` (net0+hostname+cores+
memory+mpN); rootfs grow stays its own call. `setConfigWithLockRetry` retries ONLY the transient
PVE config-lock 500 (`pveConfigLock`: 500 + "can't lock file"/"got timeout"); any other error
fails immediately — never retried.
- **`--selftest=bring-up`** (`-mode provision|dr -archive -vmid -hostname [-keep]`) — runs the real
journaled job (after a `Recover`), then tears the guest down unless `-keep`.
- **`configs/build-golden.sh`** — the validated golden recipe as a script, incl. the F3
first-boot `felhom-regen-hostkeys.service` unit (Condition-gated: fires on provision, no-ops on
DR). The slice-7 spike archive (which lacks the unit) is superseded.
### Deferred (stated, not built)
- Provisioning BACK HALF (controller deploy, bootstrap, per-guest token mint) → **slice 8**.
- Host-loss DR + PBS escrow consumption → **slice 10**.
- The SOURCE of a `BringUpSpec` (hub desired-state: which archive/VMID/mounts) → **slice 10**;
this job takes the spec as input. `GuestMount` is defined minimally (no hub coupling).
### Tests
- provision happy path (fresh MAC = net0 without hwaddr, hostname, coalesced sizing+mount, rootfs
grow separate, started, **guest NOT destroyed**); compensating rollback at each step (restore /
config / start-task / waitRunning — asserts the guest WAS destroyed); DR continuity (MAC kept,
hostname not reset) + DR `KeepMAC=false` resets MAC; liveness verdict (warnings+running pass /
not-running fail); F4 (lock-500→retry→proceed; non-lock-500→fail without retry); owning entry
journaled BEFORE restore; reserved/existing VMID refused; `Recover` rolls back / clean.
## v0.7.0 — restore-test: verdict is liveness, not start-task exitstatus (2026-06-09)
Fixes a correctness bug found by the live hub-enrollment runbook: the self-restore-test reported