agent v0.33.0: C1 net — pre-start self-heal hook + decommission mp-delete
Pre-start PVE hookscript (internal/guesthook) creates host-root placeholders for absent bind-mount sources so the guest always boots (fail-closed); decommission now pct set --delete's the dead mp (GuestBinder.DetachBind) so a missing source can't brick the next reboot (B3 C1 bug). Non-hollow tests + companions. Installed + registered per-guest by the provision back-half. Transitional ahead of the intermediary-mount re-architecture which makes C1 structural. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -124,6 +124,36 @@ func TestDecommission_Effects(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestDecommission_DeletesGuestMount is the C1-fix regression: decommission must `--delete` the guest
|
||||
// mountpoint slot that binds the drive, so its now-missing source can't brick the next boot. The guest
|
||||
// has two binds (bootstrap mp9 + the data drive mp1); only mp1 (the one targeting /mnt/bulk) may be
|
||||
// detached.
|
||||
//
|
||||
// COMPANION GUARD: the pre-fix handler (the B3 bug) never called DetachBind → detachCount()==0 → this
|
||||
// test FAILS on it. A trivial impl deleting the WRONG/first slot is caught by the slot==mp1 assertion.
|
||||
func TestDecommission_DeletesGuestMount(t *testing.T) {
|
||||
d := &fakeDiskOps{}
|
||||
intent := newFakeIntent()
|
||||
intent.SetEnrolled("uuid:usb-1")
|
||||
ga := &fakeGuestAttacher{}
|
||||
srv := decommServer(t, d, userDataAndProtected(), fakeGuestList{}, intent, tempBindStore(t), ga, map[int]map[string]string{
|
||||
8200: {
|
||||
"mp9": "/var/lib/.../bootstrap,mp=/etc/felhom-bootstrap,ro=1",
|
||||
"mp1": "/mnt/bulk/felhom-data,mp=/mnt/bulk",
|
||||
},
|
||||
})
|
||||
w := do(t, srv.Handler(), "POST", "/disks/decommission", "A", `{"where":"/mnt/bulk"}`)
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("decommission: got %d want 200 (%s)", w.Code, w.Body.String())
|
||||
}
|
||||
if ga.detachCount() != 1 {
|
||||
t.Fatalf("DetachBind called %d times, want 1 (C1 fix: the dead mp must be deleted)", ga.detachCount())
|
||||
}
|
||||
if got := ga.detaches[0]; got.vmid != 8200 || got.slot != "mp1" {
|
||||
t.Fatalf("DetachBind(vmid=%d, slot=%q), want (8200, mp1) — wrong slot deleted", got.vmid, got.slot)
|
||||
}
|
||||
}
|
||||
|
||||
// TestReassertGuestBinds_SkipsDecommissioned is the load-bearing F9-reconnect invariant: a
|
||||
// decommissioned-but-present drive still recorded in the bind store must NOT auto-rebind on agent
|
||||
// restart. Companion: with intent=enrolled the SAME setup DOES rebind — proving the intent gate is
|
||||
|
||||
Reference in New Issue
Block a user