v0.111.0 — E-2c: the backup-target drive can no longer be ejected
A regression guard on a configuration that is live right now. E-1 moved each
demo box's whole-guest vzdump target onto its secondary drive at that drive's
own mountpoint -- but RoleForStorage types a local-dir on a non-system device as
user-data, so the existing eject role gate PASSED it. POST /disks/eject on
/mnt/nvme-1tb (demo-hp) or /mnt/hdd_1 (demo-felhom) would have SUCCEEDED
silently, taking the only local whole-guest backup with it, with no alarm and
the box still reporting a configured tier. Found by E-2 Phase 0, not by a
failure.
handleDiskEject and handleDiskDecommission now call refuseIfBackupTarget AFTER
the role gate and refuse with 409, naming the storage and the remedy -- the op
is ordered, not forbidden: reassign the target first.
NOT a role reclassification, which is the obvious fix and the wrong one: making
RoleForStorage return RoleBackup would refuse every legitimate eject of the
customer's own data drive, because on both demo boxes that drive IS the target.
That trades a silent failure for a permanent obstruction.
backupTargetAt resolves through the agent's own storage view, never the caller's
claim, and fails OPEN -- safe because it sits behind the role gate, which fails
SAFE on the same error.
Red-proofed both ways, mutations verified to land first:
- removing the eject guard -> "eject of the backup-target drive SUCCEEDED (200)"
- the over-correction (any backup-content dir storage is the target)
-> the gate blocks /mnt/spare, failing TestEjectStillAllowedOnANonTargetDrive
Harness note: normalizeBackupTiers DROPS tiers with a nil Service and falls back
to the legacy empty-TargetID tier -- an earlier version of this test exercised
nothing and reported the production bug as if the fix had failed.
Green gate: build + vet + test rc=0 (29 packages), run separately from this commit.
This commit is contained in:
@@ -1,3 +1,38 @@
|
||||
## v0.111.0 — E-2c: the backup-target drive can no longer be ejected out from under the backup (2026-07-29)
|
||||
|
||||
**A regression guard on a configuration that is live right now.** E-1 (2026-07-28) moved each demo
|
||||
box's whole-guest vzdump target onto its secondary drive, at that drive's own mountpoint. But
|
||||
`RoleForStorage` types a `local-dir` on a non-system device as **user-data** — so the pre-existing
|
||||
eject role gate PASSED it, and `POST /disks/eject` on `/mnt/nvme-1tb` (demo-hp) or `/mnt/hdd_1`
|
||||
(demo-felhom) would have **succeeded silently**, taking the only local whole-guest backup with it.
|
||||
No alarm, no refusal; the box would keep reporting a configured tier while having lost its
|
||||
drive-loss protection. Found by E-2's Phase 0, not by a failure.
|
||||
|
||||
`handleDiskEject` and `handleDiskDecommission` now consult `refuseIfBackupTarget` **after** the role
|
||||
gate and refuse with `409` when the mount backs a configured backup tier, naming the storage and the
|
||||
remedy — the op is ordered, not forbidden: reassign the target first, then the drive is free.
|
||||
|
||||
**Why this is NOT a role reclassification, which is the obvious fix and the wrong one.** Making
|
||||
`RoleForStorage` return `RoleBackup` for the target would refuse every legitimate eject of the
|
||||
customer's own data drive, because on both demo boxes that drive **is** the target (the vzdump
|
||||
target sits beside `felhom-data` on the same mountpoint). That trades a silent failure for a
|
||||
permanent obstruction. The gate is therefore separate and narrow, and the role vocabulary is
|
||||
untouched.
|
||||
|
||||
`backupTargetAt` resolves through the agent's OWN storage view, never the caller's claim, and fails
|
||||
**OPEN** — safe precisely because it sits behind the role gate, which already fails **SAFE** on the
|
||||
same error, so an unresolvable mount is refused before it reaches here.
|
||||
|
||||
**Tests + red-proofs.** Eject refused, decommission refused, and — the one that constrains the
|
||||
design — `TestEjectStillAllowedOnANonTargetDrive` pins that a non-target drive stays ejectable.
|
||||
Red-proofed both ways: removing the eject guard reproduces `eject of the backup-target drive
|
||||
SUCCEEDED (200)`, and implementing the over-correction (treat any backup-content dir storage as the
|
||||
target) fails the non-target test with the gate blocking `/mnt/spare`.
|
||||
|
||||
**Harness note worth keeping:** `normalizeBackupTiers` DROPS any tier with a nil `Service` and falls
|
||||
back to the legacy tier with an empty `TargetID`. An earlier version of this test therefore exercised
|
||||
nothing and reported the production bug as if it were the fix failing.
|
||||
|
||||
## v0.110.0 — F-LEAK, third attempt: the fourth root-fenced exception (2026-07-28)
|
||||
|
||||
**The band-scoped ACL fix (v1.21.0) is durable for exactly ONE use per slot, and the live check caught
|
||||
|
||||
Reference in New Issue
Block a user