Releases: cozystack/blockstor
v0.1.12
Bugfix release. Two operator-CLI parity fixes mined against the upstream LINSTOR 1.33.2 oracle, both validated on the live Talos+QEMU stand.
Fixed
- Auto-tiebreaker no longer kept below 2 diskful (#129) — blockstor kept (and in a post-toggle race re-created) an auto-managed
TIE_BREAKERwitness for the "1 diskful + 1 diskless" shape, which upstream LINSTOR never does. At 1 diskfulquorumPolicyreturnsquorum=off, so there is no majority to freeze and the witness only occupied a node for no benefit. The keep and race-repair branches rested on the false premise that "1 diskful + 1 diskless freezes quorum:majority" and are removed together; the auto-witness now lives iff there are exactly 2 diskful replicas, matching upstream'sshouldTieBreakerExist. Subsumes the former single-diskful witness-collapse carve-out. Pinned at L1, L6 cli-matrix (r-td-diskless-reaps-tiebreaker), and L7 replay. linstor r listCreatedOn was blank (#128) — upstream fills thecreate_timestampwire field on every resource (rendered as theCreatedOncolumn); blockstor left it unset, verified divergent against the 1.33.2 oracle. Now sourced, persistence-free, from the backing Resource CRD'smetadata.creationTimestamp(per replica) incrdToWireResource— read path only, ignored on writes. Pinned at L1 + L6 cli-matrix (r-l-created-on).
v0.1.11
Campaign-2 release. 48 corner cases mined from user-reported bugs in the LINBIT/linstor-server GitHub issue tracker were reproduced and validated on the live Talos+QEMU stand, ⚖️-ambiguous cases compared against the upstream LINSTOR oracle. This release also restores day0 skip-initial-sync on the default FILE_THIN pool — a performance regression introduced in v0.1.10 — and fixes a spawn-size unit bug.
Fixed
- day0 skip-initial-sync restored on FILE_THIN pools (#121) — v0.1.10's #112 set an explicit
discard-zeroes-if-aligned=noon FILE_THIN to avoid a loop-backing bitmap-dirtying wedge, but that also defeated DRBD's day0 skip-initial-sync, so every freshresource createon the default (FILE_THIN) pool did a full whole-device resync (minutes for a 512 MiB volume) instead of coming up instantly UpToDate. Restored via three coordinated changes:discard-zeroes-if-alignedis back toyeson FILE_THIN (the kernel treats the whole fresh device as an assumed-zeroed new region — Path A of the attach clean-bitmap logic — so the loop-wedge no longer occurs); the non-winner replica is GI-seededWasUpToDatewithoutConsistent, so it carries no authority yet still skips the sync; and theRD.Spec.Initializedlatch is gated on a proven observed GI to close a mid-create stamping race. Bundled with two related satellite fixes the same full-lifecycle gate surfaced: a mid-delete promote against a DELETE-flagged tiebreaker row is now routed into the retry loop instead of silently promoting a dying witness (Bug 359), and a healthy SyncSource/WFBitMapS peer is no longer force-promoted by the recovery-promote scan (Bug 366). The fullresource createlifecycle now converges in ~45 s with zero resync. rg spawn-resources <rg> <rd> 32Mcreated a 32 KiB volume (#124, Bug 391) — the spawn handler divided everyvolume_sizesentry by 1024 (treating the field as bytes), but it is KiB: the python linstor client encodes the operator's size withparse_volume_size_to_kibbefore POSTing (32M→32768), and the REST spec documents the field as KiB. Each entry is now stamped directly assize_kib, matching thevd cpath; everyrg spawn-resourceswith a human size was previously provisioned 1024× too small.node evacuate/ eviction never drops the last diskful copy (#114) — evacuate and evict guards aligned with upstream so the final diskful replica is preserved; node-lifecycle and tiebreaker-reliability corner cases pinned (U18–U427).- A lone, peerless diskful replica wedged below UpToDate is force-promoted (#120) — a single diskful replica with no peers that comes up Consistent-but-not-UpToDate is promoted to UpToDate, matching upstream's single-node behavior, instead of staying unpromotable.
- Mid-sync delete of the last diskful replica is guarded; DELETING stays idempotent and relocation-safe (#115, U130) — deleting the last diskful while a sync is in flight no longer risks data, and the DELETING flag survives repeated delete calls and relocation.
Corner-case parity coverage (mined from upstream user-reported issues)
48 issue-mined corner cases were validated on the live stand (⚖️-ambiguous cases against the oracle) and pinned at L1 / L6 cli-matrix / L7 replay:
- Sync correctness (#122) — adding a diskful replica over written data syncs rather than silently coming up empty (U145); add-peer regenerates the connection mesh with no StandAlone (U216); an Inconsistent replica with no source is never classified SyncTarget (U203); a rejoined node's resyncs drain cleanly with no stuck done-% (U251); bulk create converges (U268).
- Snapshot robustness (#113) — IO-unwind on snapshot failure, delete-retry idempotency, and related guards (U138/U52/U258/U32/U282/U290/U464/U318).
- Resize family (#119) — volume-size / resize cases vs user reports (U48/U329/U389/U204/U388/U421/U360).
- Placement family (#117) and props / envelopes / scale (#118) — placement and property-surface cases (upstream-issues U6; U337/U302/U222/U64/U110/U187).
- Residual quorum / lifecycle (#123) — quorum held across a
toggle-disk --migrate-frommigration with no transient quorum loss (U341);node lostwith live resources prunes cleanly with no dangling refs (U173); the migrate source is pruned as diskful while an auto-quorum tiebreaker legitimately re-occupies the vacated node (U435); redundancy is auto-restored after a node failure (U236).
Testing & infrastructure
- vd-resize CSI leg now runs end-to-end (#126) — the resize lifecycle's pod-attach cross-check (in-pod block-device growth + PVC capacity propagation + md5 data preservation across
vd s1G→2G→4G) previously SKIPped because it bound through a non-existentblockstor.io/existing-rdprovisioner. It now attaches the pod to the CLI-created resource via a static pre-provisioned PV on stock linstor-csi (pre-formatting the device, since linstor-csi onlyfscks a static volume, nevermkfss it), validated on both lvm-thin and zfs-thin. - E2E tolerance for python-linstor's blind POST-resend (#125) — when a dropped read makes the python client re-send a
resource create, the server's correct409 already-existsis now tolerated in the harness only when the step expected success. The upstream-faithful 409 is unchanged: the only production consumer, linstor-csi, is already idempotent (FindByID-first) and uses the Go client, not the python blind-resend path. - Replay harness gained a
quorumawait kind and ashow_defaultsoption ondrbd_option.
v0.1.10
Corner-case parity campaign release. Every behavior in this release was validated on a live Talos+QEMU stand, with the ⚖️-ambiguous cases compared against an upstream LINSTOR 1.33.2 oracle cluster (controller + 3 satellites) running side-by-side on the same DRBD kernel. 61 corner-case plan items closed; 15 new rows added to the known-deltas whitelist.
Fixed
-
DrbdOptions/auto-quorum disabledwas silently ignored (#97, #105) — the opt-out gate read the camelCaseDrbdOptions/AutoQuorumkey that no production path writes (#97), and after the key fix it still readSpec.Propswhile the store transcoder routes the kebab key intoSpec.ExtraProps(#105) — so the reconciler kept re-stampingquorum=majorityover an operator's manualquorum off. Both layers fixed; the gate now honors the canonical key in both property bags. Caught at operator-CLI level by the L7 replay — twice — after unit fixtures passed. -
Empty
set-propertyvalue now deletes the key everywhere (#97, #107) — upstream semantics ("empty value = delete the property") were implemented for RD/RG modify first (#97) and then rolled out to all remaining CLI-reachable handlers: node, storage-pool, controller, resource, volume-definition, volume-group, storage-pool-definition (#107). KV-store and log-level handlers are deliberately exempt (empty is real data there). -
Controller-tier DRBD options no longer beat closer scopes (#98) —
linstor controller drbd-optionsvalues used to override RD-level overrides; the effective-properties resolver now applies the upstream precedence (controller < resource-group < resource-definition < resource) uniformly. The rewrite initially dropped non-DRBD upper-scope properties (FileSystem/Typenever reached the satellite, breaking mkfs seeding) — caught by 3× CI failures and an isolated-stand A/B repro, fixed in the same PR. -
Autoplace now upgrades a tiebreaker witness in place (#111) —
resource create --auto-place +1(and the absolute form) on a 2-diskful + witness shape failed with "Not enough nodes": the placer counted the witness-holding node as taken. It is now an upgrade candidate, promoted via the same flag transition the explicitr c --storage-poolpath uses; wire result matches upstream (in-place witness upgrade, no fourth node). -
node deleteon an EVICTED node is rejected;AutoplaceTarget=falseexcludes a node from autoplace (#102) — both upstream-documented guards were missing. -
Deprecated
--disklesswire alias accepted (#103) —DRBD_DISKLESSis normalized toDISKLESSat the resource-create boundary, so older clients and scripts behave identically to upstream. -
Snapshot edge guards (#100) — in-place
snapshot rollbackis rejected with an actionable pointer to the safesnapshot resource restorepath (upstream's rollback both destroys newer snapshots and, on ≥1.31.2, silently resurrects deleted replicas — verified live against the oracle); restore into an RD that already has volume definitions returns the upstream-typedFAIL_EXISTS_VLM_DFNenvelope;AutoSnapshot/Keep ≤ 0falls back to 10; snapshots on thick-LVM pools are rejected with the upstream envelope. -
Finalizer-blocked deletions surface the
DELETEflag (#94) — a resource-definition held by node teardown now showsDELETINGinrd l, matching upstream's two-phase deletion visibility. -
--layer-listduplicate-layer rejection reports the real fault (#108) — duplicate detection now runs before the position check, sodrbd,drbd,storagesays "appears more than once" instead of a misleading ordering error. -
Resync transfers only written data on real-block thin pools (#112) — the rendered
disk {}section now includesrs-discard-granularityon LVM-thin/ZFS pools (matching upstream), so DRBD discards provably-zero ranges during resync instead of byte-copying them — measured ~2x faster recovery of partially-written volumes. FILE_THIN (loop-backed) pools deliberately omit the option: a full-device mkfs discard on loop backing dirties the bitmap against the day0-seeded peers and wedges fresh-create convergence (found by CI, isolated on the stand, recorded as a known delta).
Parity pins and recorded divergences
- Volume-number allocation (smallest-free reuse after
vd d, explicit--vlmnrgap fill) pinned oracle-identical; multi-volume RDs render as one DRBD resource with nestedvolume {}blocks (#96). - Deletion semantics pinned byte-identical to upstream:
rd dblocked by snapshots whiler dis not;rg deletewith live RDs rejected with the upstream envelope (#94). - Placement contracts pinned: unsatisfiable place-count accepted at
rg cand failing only at spawn (FAIL_NOT_ENOUGH_NODES);--x-replicas-on-differentempty-bucket semantics; bare-flag autoplace property reset;--providersorder-independence (#99). The plan's assumption thatrg c --place-count +1is rejected upstream was disproven by the oracle and documented. - BalanceResources: blockstor's
RGRebalanceReconcileralready provides the equivalent of upstream's periodic balancer and honorsBalanceResourcesEnabled=false; residual divergences recorded (#108). - Quorum behavior pinned at the DRBD-kernel level: a diskless tiebreaker can hold but never return quorum — a severed node stays UpToDate yet unpromotable (
drbdadm primary→ "No quorum"), exactly per the DRBD documentation (#106). - New known-deltas rows for: shrink rejection (BS stricter,
force=trueescape), defaulton-no-quorum=suspend-ioseed (data-safety choice vs upstream's unset), quorum-property strip-vs-stamp onr d, permissive property-value validation, resource-connection/node-connection peer-options surfaces, per-object option-class enforcement, StorPoolName resolution chain, autoplacer weight defaults, DRBD port/minor base ranges (20000+ to coexist with upstream on shared kernels),rd lpinherited-property inlining, layer-list error envelope shape.
Testing & infrastructure
- Upstream LINSTOR 1.33.2 oracle cluster install script for the dev stand: controller + 3 satellites with disjoint DRBD port/minor ranges, enabling live A/B parity validation (#95).
state-standalone-partitionhardened against its dominant CI flake modes: the partition rule is verified applied before the detect wait, transient empty status reads are retried instead of sampled, and an evidence dump precedes any failure (#104).- Replay harness:
hold_sawait option — an assertion must stay true for N consecutive seconds, catching value-flapping that a first-match await false-passes (#105);prop_valueawait extended to node/controller objects (#107);vd_size_kibawait fixed (an environment-variable-across-pipe bug made it pass-proof since introduction) and{{rg}}/{{device}}substitutions added (#110); fixture-gated replays now SKIP cleanly when the stand lacks the fixture (#110). - Replay assertions made auto-tiebreaker-aware: after operations that leave two diskful replicas, the witness legitimately re-occupies the vacated node —
resource_absentassertions replaced withreplica_diskless/tiebreaker_present(#103, #109, #110). stand/up.sh: backticks in heredoc comments no longer execute as command substitution on hosts that have the named binaries; respawn-wedge latch wait widened for loaded CI runners (#101).- Slow-stand resync headroom on large-volume replay gates; L4 quorum scenario and corner-case L6/L7 coverage across all campaign groups (#99, #100, #102, #103, #106, #107, #108, #110).
v0.1.9
Patch release with a single operator-reported fix. Versions v0.1.6–v0.1.8 are skipped: those tags pre-exist in the repository from an inherited lineage and do not correspond to blockstor releases.
Fixed
- Resource flaps forever after
vd d(Bug 399, #92) — deleting a volume definition removed the volume from the RD and the DRBD kernel, but two add-only projections never forgot it: the controller's RD →Resource.spec.volumesprojection kept a stale entry, and the satellite observer's volume cache only evicted on the events2destroy deviceframe — which a DISKLESS/tiebreaker replica never receives (it has no local disk to destroy) — so the observer re-emitted a phantomstatus.volumes[n]=Disklessevery kernel tick, oscillating the resource status and PATCH-storming the apiserver ~1/s indefinitely. The controller now prunesspec.volumesentries absent from the RD, and the observer converges its volume cache against the live RD volume set (theblockstor.io/volume-numbersannotation), so both diskful and diskless replicas settle to exactly the remaining volumes. The e2evolumes_settledflap gate was also made kine-safe (volume-set stability across polls instead of resourceVersion equality, which k3s/kine inflates with the global store revision).
v0.1.5
Large bug-fix release. Spans the REST API wire-validation surface (Bugs 356–383: input validation, typed FAIL_* envelopes, idempotency), the satellite DRBD / LUKS / metadata paths, and a multi-round operator-lifecycle bug-hunt that closed the four operator-reported DRBD lifecycle bugs the REST sweep missed plus their adjacent classes (Bugs 384–397). Every operator-facing fix lands with an L1/L2 unit/contract test, an L6 cli-matrix cell, and an L7 operator-replay workflow, validated on the live Talos+DRBD stand.
Fixed
- Late
vd cleaves the new volumeInconsistenton every replica (Bug 384, data integrity, #83) — adding a volume to an already-initialized multi-replica resource ran the seed path withisWinner=falseunconditionally (first-activation election is gated on!rdInitialized), so no replica seeded the new volumeUpToDateand it latchedInconsistentforever. The satellite now re-runs the lowest-node-id winner election per freshly-added volume, so exactly one replica becomes the SyncSource. Class regression of the Bug 79/332 family. node evictdemotes a healthy diskful replica to TieBreaker (Bug 385, #83) —ensureTiebreakercounted a witness stranded on a just-EVICTED node as live, so the witness was never relocated and a healthy diskful drifted into the tiebreaker role. Replicas on EVICTED/LOST nodes are now excluded from the witness/quorum decision and stranded witnesses are reaped.node restoredoes not recreate the auto-TieBreaker (Bug 386, #83) — the RD reconciler did not watchNode, so clearing the EVICTED flag never re-ran the tiebreaker invariant, leaving two diskful UpToDate with no witness (split-brain risk). Adds a Node watch.r dof a diskful on a 2-diskful + 1-INACTIVE resource grows a useless TieBreaker (Bug 387, #83) — an INACTIVE (drbdadm down) replica is not a voting peer but was counted as a diskful, so the delete spuriously converted to a witness. INACTIVE replicas are excluded from the voting set.node evacuatenever prunes the source replica (Bug 389, #81) — evacuate gap-filled a replacement but never deleted the source on the drained node, leaving the resource permanently at place_count+1. Now does strict add-before-drop (prune only after the replacement reaches UpToDate) and derives the redundancy target from the current diskful count, so it works on RDs that inheritplace_count=0fromDfltRscGrp.- auto-diskful ignores EVICTED/LOST nodes and INACTIVE replicas (Bug 390, #82) — the deficit count and promotion-candidate set treated drained-node and deactivated replicas as healthy diskful, masking deficits and promoting onto draining nodes. Both are now filtered.
- Autoplace under-places when an INACTIVE replica is present (Bug 393, #85) —
placer.countDiskfulReplicascounted INACTIVE replicas towardplace_count, so a replacement active replica was never placed. INACTIVE is now excluded, mirroring the auto-diskful and tiebreaker invariants. snapshot createfails on any resource with an INACTIVE replica (Bug 394, #86) — snapshot node-selection and the success denominator included the INACTIVE node, whose down DRBD device cannot ack the suspend-io barrier, aborting the whole group. INACTIVE replicas are excluded from snapshot targets.- Thick-LVM volume resize silently diverges the replicas (Bug 395, data integrity, #87) —
drbdadm resize --assume-cleanran unconditionally; on a thickLVMpool the grown extents hold node-distinct stale content, so replicas disagreed on the grown region with no resync (out-of-sync 0) and a failover changed the bytes an application read. Resize is now provider-aware: zero-on-allocate providers (ZFS, thin, file) keep the--assume-cleanfast path; thick LVM omits it so DRBD resyncs the grown region. Cozystack's default (ZFS) was unaffected. - Snapshot-restore onto a snapshot-less node (Bug 397, #89) — the explicit
--node-namerestore path did not constrain targets to the nodes that hold the snapshot (unlike the auto-place path), so a replica could be placed on a node lacking the data. The restore handler now rejects a snapshot-less target with a typed error, and the seed path refuses the skip-init-sync fast path for a blank-fallback replica so it SyncTargets the real copy; a legitimate all-clone restore keeps the fast path. - Tiebreaker / toggle-disk / LUKS / metadata satellite fixes —
r toggle-disk --diskfulno longer leaves a staleTIE_BREAKERflag on the promoted replica (#54);r d --keep-tiebreakerkeeps the auto-witness instead of collapsing it (#57);r cretries through the tiebreaker-collapse race instead of failing (Bug 359, #61); a TB-relocate that wedgedStandAloneon a both-disks-bitmap state now recovers (#53);r td --disklesscloses the LUKS mapper so the backing zvol can be reclaimed (#55); and per-volumedrbdadm create-mdstopsvd con an existing RD from EBUSY-looping against vol-0's attached minor (Bug 332, #58).
Fixed — REST API wire validation & idempotency
Closes Bugs 356–383: the REST surface now validates operator input at the wire boundary (before any partial state lands) and returns upstream-matching FAIL_* ApiCallRc envelopes instead of bare 200s or generic 500s.
- Name & volume-number validation — RD/RG/Node names are capped at the 48-char k8s-label limit (Bug 360, #59) and invalid names are rejected on
s r rst/rg spawnbefore partial state lands (#56);volume_numberis validated in[0, 65535]at create (#60) and onvd d/vd l/vd m(Bug 365, #62); a non-numeric volume-number in the URL path returns an operator-grade envelope (Bug 380, #73). - Size, type & placement validation — non-positive
volume_sizesin spawn (Bug 381, #74) and non-positivesize_kibon avdPUT regardless of--force(Bug 383, #75) are rejected;select_filter.place_countis validated at RG create + modify (Bug 367 / 361, #64); nodeTypeis validated, defaulting empty toSATELLITE, atPOST /v1/nodes(Bug 370, #65); aPUTresource-definition validates itsresource_group(Bug 372, #66); net-interfacePUTvalidates address + port at the wire (Bug 371 / 368 / 369, #63); the fresh-create pool resolver walks the RGStoragePoolList(Bug 364, #67). - Immutability & idempotency —
StorDriver/*mutation is rejected onPUTstorage-pools (Bug 373, #68) and storage-pool-definitions (Bug 375, #70); five bare-200 write endpoints now emit an ApiCallRc envelope (Bug 374, #69);drop-property(Bug 378, #71) and net-interfaceDELETE(Bug 379, #72) are idempotent on a missing parent.
Test infrastructure
- L7 replay convergence assertions were silent no-ops (Bug 388, #83 / #84) —
all_uptodate/wait_settlefiltered replicas onspec.resourceName, but the CRD field isspec.resourceDefinitionName, so the most-used "did the cluster converge" check passed vacuously across every replay. Fixed the field, tolerate Diskless/TieBreaker rows, and gaveno_orphansa settle window. (This immediately caught a real drop-without-add defect in the first Bug-389 fix.) - e2e flake hardening (Bugs 392 / 396 / 398 — #84 / #88 / #90) —
state-standalone-partitionand siblings flaked under CI on three substrate-level read/scan races, none of them blockstor data bugs (DRBD partition recovery is forensically correct — the writer stays SyncSource, ZFS checksums clean). The connection-state waits now read kernel ground truth instead of the lagging CRD projection; the marker round-trip distinguishes a real (stable) on-disk corruption from a non-deterministic nested-QEMU read-path glitch; and the stand's Talos config narrows the LVMglobal_filterso the node-sidepvscanno longer races the satellite for DRBD/dm/zvol/loop backing devices (open(/dev/loopN): Device or resource busy).
v0.1.4
Bug-hunt + REST safety release. Closes #45 (autoplace capacity gate on the real linstor-csi single-node create path) and re-enables the corresponding e2e scenario.
Fixed
- Autoplace / spawn / single-node-create capacity gate (#45) — when a StorageClass set
placementCount: 1+nodeList, linstor-csi'smanualscheduler bypassed the existing autoplace gate and accepted placement on a 100%-full pool. Capacity check now lives insidecreateOneResource(shared by both/v1/resource-definitions/{rd}/resourcesand the single-node alias/v1/resource-definitions/{rd}/resources/{node}); rejects with 409 +FAIL_NOT_ENOUGH_NODES. 4-tier pool-name resolver honoursRG.SelectFilter.StoragePoolList. - Recovery after operator
drbdadm downno longer reverts —shouldSkipNetOnAdjustnarrowed toStandAlone AND peer-devices-present. - Typed FAIL_* envelopes on Resource / SP / Node DELETE; CSI driver treats
FAIL_EXISTS_SNAPSHOT_DFNas idempotent success. - Duplicate SP POST refused with 409 +
FAIL_EXISTS_STOR_POOLinstead of silently mutating. - Internal annotations stripped from REST reads (
blockstor.io/*,*.blockstor.cozystack.io/*).
Added
- Missing REST routes wired:
/v1/storage-pool-definitions,/v1/migrate-disk,properties/infofamily. - DRBD promotion + node event streams:
GET /v1/events/drbd/promotion,GET /v1/events/nodes.
Test infrastructure
observability-capacity-correlationandcsi-pvc-localrestored to the piraeus-interop lane.- 4 flaky scenarios hardened (
state-offline-unknown,state-auto-resync,recovery-down-reverses,recovery-deleting-convert). stand/up.shported to talosctl 1.13 + skip-list for/24slots inside Talos's10.96.0.0/12service CIDR.
See CHANGELOG.md for the full entry.
v0.1.3
Bug-fix release with one CI test-infrastructure addition.
Fixed
- CSI storage-only auto-mkfs —
localStorageClass (no DRBD, single replica) now reaches Pod-ready end-to-end. The satellite was leaving storage-only resources unformatted because the mkfs path was wired only for the DRBD bring-up sequence; linstor-csi then failedNodeStageVolumewithwrong fs type. The reconciler now formats the backing block device on the storage-only path before exposing it to the kubelet, and thecsi-pvc-locale2e scenario is restored to gate the contract. Also wires the missingPOST /v1/resource-definitions/{rd}/resources/{node}alias (linstor-csi v1.10.1 issues this single-node create — pre-fix the apiserver returned HTTP 405). - Orphan-witness collapse grace removed — the controller used to keep a TieBreaker witness alive for several reconcile cycles after the last diskful peer left, on the theory that a fresh diskful might race in. In practice the grace window only ever surfaced as stuck
Offpeers afterr d+ immediater con the same node. The collapse is now instant: when no diskful peer remains and no fresh placement is pending, the witness is deleted in the same reconcile.
Test infrastructure
tests/e2e/lib.shDS-converge barrier —reset_cluster_statenow waits up to 90s for the satellite DaemonSet to converge (desiredNumberScheduled == numberReady) before declaring the cluster reset. Pre-fix, fast successive scenarios occasionally started against a not-yet-rolled satellite and the failure looked like a flaky test rather than a missed barrier.
Images for this tag are published as 1.33-style tags by docker/metadata-action's semver rules under:
ghcr.io/cozystack/blockstor-controller:0.1.3ghcr.io/cozystack/blockstor-apiserver:0.1.3ghcr.io/cozystack/blockstor-satellite:0.1.3
v0.1.2
Bug-fix and test-coverage release.
Fixed
- TieBreaker remains after
r d(Bug 338 re-regression) — adds the missing e2e regression catcher (tests/e2e/tiebreaker-r-d-cleanup.sh). The controller-side fix landed earlier inresourcedefinition_controller.go(shouldKeepExistingWitness), but the lack of a real-DRBD test let it silently re-regress on the dev stand. Future TieBreaker changes are now gated by an e2e scenario that exerciseslinstor r done-by-one and asserts the witness invariant on the QEMU+Talos lane.
Test infrastructure
- e2e cascade attribution — when a scenario leaves the cluster dirty, the next scenario now is no longer wrongly blamed. New
strict_cleanup_on_exithelper +register_strict_cleanuptrap demote a leaving-PASS to FAIL if the cluster cleanup fails, and a pre-flight check at the top of each scenario rewrites the previous scenario's verdict to FAIL with aLEFTOVERreason when satellite pods or RDs are still present from the prior run. - piraeus interop in CI — the CI matrix now ships an
E2E (piraeus interop)job that installs the upstream piraeus-operator against the blockstor apiserver and runs the linstor-csi scenarios (rwx-ganesha,observability-three-way,observability-capacity-correlation,csi-pvc-replicated-rwo) on a dedicated stand. Main lanes 1-6 keep running bare blockstor; the interop scenarios that need linstor-csi v1.10.1 + LinstorCluster CRD are isolated to the piraeus job. csi-pvc-replicated-rwoe2e — new test pins the linstor-csi DRBD path end-to-end against the user-facingreplicatedStorageClass shape (3 DRBD replicas, fullDrbdOptions/*prop set, write→delete pod→read back on another node).
Refactor
FilesystemFormattedStamper API — addsStampFilesystemObserved(Reason=FilesystemObserved) alongside the existingStampFilesystemFormatted(Reason=MkfsSucceeded), plus a byte-identity SSA-shape test that prevents the PR #32-class.status.volumes:nullregression. The observe call site is intentionally not wired yet — it requires routing from the observer event path rather than the per-RD apply lane and will land in a follow-up.
Known follow-ups (deferred)
- Storage-only PVC auto-mkfs —
csi-pvc-localtest and the satelliterunStorageOnlyMkfshook were reverted from this release after live validation showed linstor-csi tearing down storage-only volumes mid-provisioning. Tracked as a follow-up; the user-facinglocalStorageClass (no DRBD) is not yet provisioned end-to-end through blockstor.
All non-deferred items were validated on a 3-worker Talos+QEMU stand (linstor-dev-1) with DRBD 9.2.14 + linstor-csi v1.10.1.
v0.1.1
Bug-fix release.
Fixed
- Respawn StandAlone wedge — deleting a diskful replica and immediately recreating it on the same node no longer wedges the resource. The recreated replica was force-promoting itself and minting a DRBD Current-UUID unrelated to the surviving peer, which the kernel rejected as
unrelated-data(the connection dropped toStandAloneand never recovered). Auto-primary is now gated on the resource's persistedInitializedlatch, so only a brand-new resource ever seeds a primary. - ZFS clone-source deletion — deleting a volume that still has a dependent ZFS clone no longer hot-loops on
zfs destroy ... volume has dependent clones. Dependent clones are nowzfs promoted before the source is destroyed; the surviving clone keeps its data.
Both fixes were validated on real DRBD/ZFS and ship with regression tests (unit + e2e).
v0.1.0
First public release.
Added
- LINSTOR-compatible REST API, served over mTLS — drives the existing client ecosystem (
linstorCLI,linstor-csi,piraeus-operator,ha-controller,golinstor) unchanged. - DRBD-replicated volumes on LVM, LVM-thin, ZFS, ZFS-thin, and file backends, with autoplacement (zones, node properties, replicas-on-different), TieBreaker + quorum, and online resize.
- Run without DRBD — plain local storage, single-replica diskful or diskless.
- LUKS encryption layer (volume-level, at rest).
- Snapshots — create, restore as a new resource, roll back, and clone; intra-cluster snapshot shipping via
zfs send/recvandthin-send-recv. - Device-pool creation from physical disks (
physical-storage create-device-pool). - Kubernetes-native architecture — all state in CRDs, controller and per-node satellite as
controller-runtimemanagers, no external database. - Multi-arch images (
linux/amd64,linux/arm64) published to GHCR:blockstor-controller,blockstor-apiserver,blockstor-satellite.
Notes
- Default DRBD allocation windows are disjoint from upstream LINSTOR's — TCP ports
20000–20999, minors20000–65535— so blockstor can run alongside a live LINSTOR on the same nodes. Resources adopted from LINSTOR keep their original ports and minors. - Not yet implemented (the API returns
501 Not Implemented): cross-cluster snapshot shipping, backup create/restore/ship, schedules, and remote backends (S3, LINSTOR remotes). See the README for the current roadmap.