Adversarial-hardening release. A red/blue agent loop attacked the control-plane / API / store / controller surface through the public REST + linstor CLI interfaces (concurrency, cache-lag, autoplace/tiebreaker/quorum, VolumeNumber races, snapshot bookkeeping, node evict/restore/lost/drain, input-boundary fuzzing, reconciler races). Two adversarial passes across the V1–V5 sweep, snapshot-restore, resource-connection-path, pagination-under-mutation and every CRD↔wire transcode surfaced THREE real breaks — all the same class: a modify path skipping the validation or field-carry-across the create path enforces — fixed below (that seam is now mined dry and guarded by a carry-across tripwire). DRBD-kernel scenarios (satellite mkfs/attach, split-brain, real sync) and two parity/product questions were deferred to the live stand (see "Deferred").
Fixed
- VD-scoped modify no longer drops the per-volume DRBD minor / device identity (#177, Bug 433) — a legal
vd set-sizegrow orvd set-propertysilently droppedRD.Spec.VolumeDefinitions[].DRBDMinor(the/dev/drbd<N>identity):PatchVolumeDefinitionSpecrebuilt the entry throughwireToCRDVD, whose wire type carries no minor field. It self-heals in isolation (the allocator re-picks the same value) but once a lower minor is freed by routine RD churn the allocator re-stamps a DIFFERENT minor — a permanent device-identity change on a live resized volume, violating the CRD's own carry-across invariant (ResourceDefinitionVolume.DRBDMinor— "never overwritten … preserved through a REST modify"). Same wire-rebuild-drops-operator-only-field class as Bug 206/208. Fix:wireToCRDVDPreservingcarries the minor across both write-backs, andResourceDefinitionVolumeis now registered in the carry-across tripwire test so the class can't silently ship again. Pinned at L1 (store) + integration fail-on-bug, with L6 cli-matrix + L7 replay (newdrbd_minorawait kind) authored (on-stand run deferred). rg modifyno longer accepts an invalid layer_stack that a resource then inherits (#178, Bug 434) —linstor resource-group modify --layer-liststored aselect_filter.layer_stackthe CREATE path refuses, and an RD created against that RG inherited it unvalidated (handleRDCreatevalidated BEFOREinheritLayerStackFromRG), persisting an unmaterializable layer stack in the RD spec even though a direct rd-create 400s the same stack. Same modify-bypasses-create-validation class as the resize-bounds fix. Fix:rgUpdateWireGatevalidates the layer_stack on rg-modify, plus a defense-in-depth re-validation of the inherited stack afterinheritLayerStackFromRG. Pinned at L1 (rest) + integration fail-on-bug, with L6 cli-matrix + L7 replay authored (on-stand run deferred).vd set-sizeno longer stores an unmaterializable out-of-range volume size (#175) — the CREATE path refusessize_kiboutside[4 MiB, 16 TiB]viavalidateVDSize(Bug 155) so the satellite never hot-loops ondrbdadm create-md, but the RESIZE path (PUT .../volume-definitions/{vn},linstor vd set-size) skipped that gate: a below-floor force-shrink (e.g. 3 MiB) or an over-ceiling grow (> 16 TiB) returned 200 and stored the size verbatim, reproducing the exact Bug-155 hot-loop through the resize verb — and it did not self-heal.rejectVDPatchSizenow runs the create path's bounds gate (validateVDSize+ byte-identicalwriteVDSizeRejectionFAIL_INVLD_VLM_SIZE envelope) BEFORE the shrink-vs-force gate;forcewaives the shrink-direction opt-in, never the physical floor/ceiling. In-bounds resizes (incl. in-bounds force-shrink) are unaffected. Pinned at L1 (handler) + integration (envtest) fail-on-bug, with the L6 cli-matrixvd-resize-bounds-rejectedcell + L7 replay authored (on-stand run deferred, stand-pending task).
Testing & infrastructure
- Integration lane no longer rotate-flakes on the heaviest autoplace-convergence cases (#176) — the per-group Eventually budgets were already scaled ×3 on CI (v0.1.16), but
TestGroupFRToggleDiskful2DisklessReapsTieBreakerandTestGroupJ/CSICreateVolumeFromEmptystill timed out at exactly 90s under full-suite runner contention while completing in ~8s locally — placer/mock-satellite starvation, not a hang. The CI scale is now ×5 (30s → 150s); fail-safe (green runs return the instant the predicate passes; a genuine hang still fails at the job-timeout=15mceiling).
Deferred (pending the live dev-kvaps DRBD stand + LINSTOR oracle)
- DRBD-kernel tier — satellite mkfs/attach, split-brain, real sync, finalizer drain: unexercisable by the mock satellite; the real remaining production risk.
- N3 (product decision) — a transient diskful-node drain-and-return permanently over-replicates (place_count=2 ends at 3 diskful + 0 witness): evict backfills the witness→diskful to keep redundancy and blockstor has no excess-replica trim, diverging from upstream
evacuate's net-neutral migrate. No data loss / wedge. - N4 (parity decision) —
linstor r td --disklesson the last diskful replica is accepted, leaving a diskless-only (dataless) resource. blockstor deliberately allows deleting the last diskful (bug-hunt #6); whether the toggle should be refused needs the LINSTOR oracle (cli-parity-refresh) to settle. Proposed fix: force-gated guard mirroring the U130 delete escape.