Skip to content

refactor(api)!: move the API group to blockstor.org - #221

Draft
Andrei Kvapil (kvaps) wants to merge 67 commits into
mainfrom
refactor/api-group-blockstor-org
Draft

Andrei Kvapil (kvaps) wants to merge 67 commits into
mainfrom
refactor/api-group-blockstor-org

Conversation

@kvaps

Copy link
Copy Markdown
Member

Moves the API group from blockstor.cozystack.io to blockstor.org, and the label and annotation keys from blockstor.io/ to blockstor.org/. The project is meant to stand on its own, under the domain it publishes its site on, and the group is the one name that cannot change cheaply once clusters carry objects under it. Nothing runs on it yet, so it moves now, with no migration.

Merge this last. It is stacked on #190, #200, #206, #215, #216, #217, #218, #219 and #220: the branch merges all of them and then renames, so the conflicts between them are resolved here once. After they land I'll rebase it onto main and redo the rename on the result, which is mechanical.

What moves:

  • every kind, to blockstor.org/v1alpha1: the CRDs (regenerated, one file per kind under the new name), the RBAC markers and roles, the samples and the stand manifests;
  • the finalizers, the resize-pending annotation, and the scrubber that keeps the group off the LINSTOR wire;
  • the e2e and cli-matrix scripts, the replay harness and the docs;
  • the controller's leader-election lease;
  • in a second commit, every label and annotation key: the original-name annotation, the replica and pool label index, the controller's bookkeeping annotations, the unapplied-group-layer-stack annotation and the topology.blockstor.org/ labels synced from Kubernetes nodes.

The LINSTOR migrator stamps apiVersion from the scheme, so the objects it writes come out under the new group; its golden manifests say so.

Two strings keep the old spelling on purpose: the seed of the stable node UUIDs, which is a hash input, not the group, and must not change, and the history in CHANGELOG.md and dated audit notes.

cozystack/cozystack#2942 follows the group in its chart and tests.

Breaking change

The API group is blockstor.org, and so are the label and annotation keys. A cluster running a build from before this has to be reinstalled: objects under blockstor.cozystack.io and keys under blockstor.io/ are not read.

Testing

  • The regenerated CRDs differ from the old ones in the group alone.
  • go test ./... with envtest assets, including the migrator's golden manifests: green apart from the known pkg/rest shutdown flake.
  • golangci-lint: clean.
  • Not run on a stand.

@coderabbitai

coderabbitai Bot commented Oct 24, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@kvaps
Andrei Kvapil (kvaps) force-pushed the refactor/api-group-blockstor-org branch 3 times, most recently from 4d7fdd0 to bf67ca6 Compare October 24, 2026 22:38
@kvaps
Andrei Kvapil (kvaps) force-pushed the refactor/api-group-blockstor-org branch 2 times, most recently from cfe04c2 to 3d93cb0 Compare October 26, 2026 09:53
A clone of a source with volumes takes `clone-<target>` on the source
and restores from it, and nothing ever deleted that snapshot. Deleting
the clone left it behind, and the source could then never be deleted
again through the API, which refuses a definition that has snapshots.
Every CSI clone goes through this path.

`rd d` of a clone now reaps that snapshot, on both the REST and the
CLI door. Ownership is a prop the clone path stamps on the snapshot,
never its name, so an operator's snapshot called `clone-<target>` is
never touched.

The snapshot is visible in `s l`, so another definition can have been
restored from it, and that definition keeps reading it: new replicas
are pinned to its nodes and restored from it by name. A dependent
appears in two steps, the snapshot read and the definition create, so
no list on the reap's side alone can see one between them. The reap
marks the snapshot, then lists dependents past the cache, and only
then deletes; a restore creates its definition, then reads the
snapshot back past the cache and withdraws on a live mark, on a
snapshot whose delete is in flight, or on one that is gone. Whichever
of the list and the create comes second sees the other. A snapshot
the reap keeps has its own mark taken off again, and a mark another
reap wrote since is left alone.

A withdrawal takes the definition the restore created back through
the rollback, with its handshake: one a retry of the same restore
adopted in the meantime is left to that retry. A withdrawal caused
only by a reap's mark answers a retryable 409, since the reap may yet
keep the snapshot; a snapshot that is gone, or whose delete was
issued, answers 404 as before. The CLI checks the snapshot back the
same way before it places replicas on a target it marked, a prepared
one or its own leftover.

The reap runs after the definition is deleted and before the delete
is answered, detached from its caller on a 30s budget, and a mark
left by a process that died expires. A kept or failed reap is logged
and carried in the answer to the delete as a warning; the delete
stands either way. A delete that was issued is read off the snapshot
itself: the store now surfaces DELETE on a snapshot a finalizer still
holds, the way it does for resources, and the snapshot list no longer
stamps SUCCESSFUL beside it.

A later `rd d` of the source names what is still there: a snapshot
safe to delete, one being deleted that needs only a retry shortly,
one kept because a named definition restores from it, and one that
looks like a clone's but was never stamped.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
A definition that leaves its layer stack empty and sits in a group that
sets one was read two ways: the REST and witness gates resolved the
group's stack, while the satellite built the volume from the empty one,
which is the default stack. In a LUKS group that put plaintext on disk
under a definition every gate called encrypted.

The stack is now decided once, by the controller, and written onto the
definition, as upstream decides it at resource creation. A definition
with nothing on disk takes the group's stack. One that already holds
data keeps the stack its storage was built with: giving it the group's
now would have the satellite build a different stack over that data. It
is annotated with the group's stack it did not get, and a warning event
names it, so such a volume reads as what it is and can be found. The
group is looked up under its folded name, since a definition keeps the
group's name as the operator spelled it.

Once set, the stack is never emptied again. Both stores keep a decided
stack when an update carries none, as a caller writing back a copy read
before the pin does, and the CRD refuses an update that empties it, so
the controller never decides a second time over a volume that may by
then hold data. A stack can still be replaced by another one. The
spec.layerStack description says what an empty stack now means.

The controller-side resolver that walked the group on read had no
callers left and is removed.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
The satellite read a definition's empty layer stack as the default and
built the volume from it, even when the definition's group sets another
stack. It now waits, on a short requeue, until the controller has
written the group's stack onto the definition, and the intent builder
refuses such a definition outright, so no path turns the empty stack
into storage. The group is looked up under its folded name, so a
definition naming it in another case waits too rather than being taken
for one outside any group. A definition in no group, or in a group
without a stack, keeps the default as before; a group that cannot be
read applies nothing.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
Format took "not a LUKS device" to mean "empty" and ran luksFormat. A
volume that went to disk without LUKS and is later asked for it, by a
layer stack that changed under it or a restore that brought data in,
is not LUKS either, and luksFormat destroys it.

Format now probes the device with `blkid -p` first and formats only one
that carries no signature at all. DRBD internal metadata, a filesystem
or a partition table refuses it with ErrDeviceHoldsData, naming what
was found; a probe that cannot say the device is empty refuses it too.
blkid exits 2 both for "nothing found" and for a device it could not
open, so exit 2 counts as empty only without an "error:" line on
stderr and for a path that stats as a block device; a missing path or
a regular file is refused. The apply of that replica fails with the
reason, and nothing is written. The satellite also records the
refusal as a LUKSDeviceHoldsData warning Event on the replica, through
events.k8s.io, for which its role gains the events rule. A signature
left on reused extents is cleared with wipefs; the message says so.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
The layer stack pin looked only at what replicas had reported. A
definition can hold data before any of them has: linstor-migrate adopts
volumes that are already full and latches Initialized on them, and the
latch is set once the controller has seen a real write. Either way the
storage was built under the stack the definition carried, so the pin
keeps it.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
The converter copied the resource definition's layer_stack column as
is. LINSTOR resolves the stack per resource and often leaves that
column empty, so the stack those volumes were built with lives only in
LAYER_RESOURCE_IDS. A converted definition with an empty stack then
took whatever blockstor resolved for it, not what is on disk: a LUKS
volume lost its LUKS layer, and a definition in a group with another
stack was read through that group's.

An empty stack is now read off the replicas' layer chains. Replicas
built in different ways leave it unset and are reported, as is a
replica built otherwise than the stack its definition names, since the
satellite would not bring it up as it is.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
The page said an RD with an empty stack inherits its group's at read
time, which is what put plaintext on disk under encrypted definitions.
It now says the stack is fixed once on the definition, by the REST
create path or by the controller's pin, what happens to a definition
that already holds data, what the operator sees, and how to find such
definitions. The migration guide and the REST create path's comment
say the same.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
A clone or restore wrote the source's stack onto the new definition
as stored, reading an empty one as the default. Under a group that
sets a stack, an empty one is not decided yet: the controller pins it
to the group's stack or to the default depending on whether data is
already there, and the satellite builds nothing until it does. The
copy took the default regardless and kept it for good, so a shell of
an empty definition in a LUKS group stayed plaintext with no data
forcing it.

The stack is now resolved through the group. An undecided source,
leftover or prepared target is answered 503 (the CLI exits with an
error) before anything is written, and a group that cannot be read is
an error rather than the default. A volume-less clone copies no bytes,
so it takes only a stack the source chose and is otherwise left for
its own group to decide. A copy short of what its group asks for
carries the unapplied-group-layer-stack annotation the controller puts
on a definition pinned over data. The restore marker goes onto a
prepared target only if, in the patch that writes it, the target's
stack still has the source's layers, so a stack rewritten after the
target was judged is refused rather than restored into.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
The pin compared the stack it applied with the group's in order and
case, while clone and restore compared them as sets. A group spelling
its stack in another order or case flagged every definition holding
data as short of it. Both now share one comparison.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
The recorder writes events.k8s.io events, which the stand's controller
role did not grant, so the layer-stack warning was dropped.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
The unapplied-group-layer-stack annotation was set on any difference
from the group's stack, so an encrypted copy in a plaintext group was
flagged as if it were the plaintext volume the annotation exists to
find. It is now set only when a layer the group asks for is missing.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
LINSTOR builds a diskless replica as DRBD over STORAGE whatever the
definition's stack, so counting it made the replicas of every LUKS
definition with a tiebreaker look divergent and left its stack unset.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
…group

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
A thick LV is allocated with -Wn -Zn, without zeroing, and nothing
wiped a volume's signatures when it was deleted, so a new LV starts
with whatever its extents held. When that was a deleted LUKS volume's
header, the LUKS layer found a header, skipped the format and opened
it with the cluster passphrase every volume shares: the new volume
showed the old one's contents. Any other leftover, a filesystem or
DRBD metadata, made the LUKS layer refuse to format a volume that
holds nothing.

CreateVolume now creates the LV under a temporary name, the volume's
name with a .bsnew suffix, runs wipefs over it and only then renames
it to the volume's name, so nothing under a volume's name carries a
signature another volume left. wipefs erases the signatures libblkid
recognises, the LUKS header magic among them, and nothing else: the
rest of the old volume's blocks stays on the extents, and a plaintext
volume can read it back from the raw device. Zeroing the LV would
close that, at the cost of writing a large thick LV in full before it
can be used. An LV already under the volume's name is
the volume and is never wiped. A temporary LV left by a run that
stopped before the rename is wiped and renamed rather than created
again. A failed listing, wipe or rename fails CreateVolume, and a
failed wipe or rename leaves nothing under the volume's name. The
orphan sweeper only knows LVs named like a volume, so it never sees a
temporary one; DeleteVolume removes a leftover one along with the
volume.

DeleteVolume wipes nothing: lvremove refuses an LV that is still
open, and that LV has to keep its data. The next CreateVolume over the
freed extents wipes their signatures anyway.

Thin LVM, ZFS and the file provider do not need this: a fresh thin LV,
zvol or backing file reads zeroes where nothing has been written.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
… name

A definition keeps its group's name as the operator spelled it
(`--resource-group DfltRscGrp`), while the group's CR is stored under
the lowercased name. The witness gate and the effective-props resolver
(which the satellite, the dispatcher and the controller render DRBD
options with) looked the group up by the spelled name, missed it, and
went on as if there were no group: a STORAGE-only group got a DRBD
witness, and the group's DRBD options were left out of the rendered
config. Both now look the group up under the folded name, as the
store does.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
An L6 cell and an L7 replay drive the layer-stack pin through the
linstor CLI: a definition with nothing on disk, created in a group
without a stack, has none, and takes the LUKS group's stack once rd
modify moves it there; the REST create copies a group's stack itself,
so only the move shows the controller's pin. One with data keeps
DRBD,STORAGE when its group gains LUKS, is annotated with the group's
stack, and gets the LayerStackNotInherited Event; a clone taken
straight after the group change is refused as undecided until the pin
and then succeeds with the source's stack. The cell also checks the
data reads back unchanged.

The replay runner gains two await kinds to read what the CLI does not
show: rd_layer_stack (spec.layerStack) and rd_annotation.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
… to its group

The mark recorded the group's stack at pin time and was never looked
at again, so a definition whose group later dropped the layer it
lacks still read as short of its group. Each reconcile now re-checks a
flagged definition: the mark follows the group's current stack, and
goes once the group no longer asks for a missing layer or is deleted.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
…refusal

The hint suggested wipefs for any signature, including DRBD metadata
on a volume that is already in use, where wiping destroys the volume.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
The refresh looked only at a definition that already carried the
mark, and could now remove it, so a group that dropped a layer and
took it back left the definition unflagged for good. Every definition
with a decided stack is now checked against its group's current
stack: flagged, with a warning Event, while it lacks a layer of it,
and cleared once it does not. That also flags one whose group gains a
layer after the pin.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
…g it

The next create renamed the leftover whatever its size, so a volume
grown in between came up at the size the crashed call asked for. The
leftover was never exposed under the volume's name, so it is removed
and the LV is created again at the volume's current size.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
…al case

rd create, rg create and rg modify validated a layer stack
case-insensitively and stored it as the caller spelled it, and so did
the CLI. Some readers compare the stored value exactly: the satellite
controller's DRBD check read `drbd,storage` as a stack without DRBD,
so allocation waited and requeued while the apply path brought DRBD
up.

Every write path now stores the canonical spelling, and the satellite
check folds case for objects written before this or applied by hand.

Fixes #204

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
…source

Since the mark also flags a stack set by hand that lacks a layer of the
group, a volume-less clone dropped such a stack, LUKS,STORAGE in a
DRBD,STORAGE group say, and the controller pinned the clone to the
group's plain stack. A stack pinned over data is always the default
one, so only a flagged source with the default stack hands the clone
nothing now. The annotation's comment and the docs describe its wider
meaning.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
…al LUKS step

The converter copied every property, so a passphrase LINSTOR kept in
one landed in plaintext in YAML that is applied past the REST guard.
The props index now leaves those keys out. The LUKS warning sent
operators to spec.encryption, which nothing reads; it now says to add
the cluster passphrase to each volume as a key slot.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
A restore or clone into a name with no definition but with a diskless
replica or a witness under it created the definition over that
replica. The stamp on its node then failed, and the rollback of the
definition cascaded over every replica under the name, deleting one
the request had not made.

The materialisation now refuses such a name with 409 before it writes
anything, and names the replicas. A replica holding data is still
taken as placed, as before, and one already being deleted is no
obstacle.

Fixes #203

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
…-initialising it

A replica that goes down while Primary and writing leaves its activity
log open, and drbdmeta answers the metadata probe with "please apply-al
first". The satellite read that as "no metadata" and ran create-md
--force, which destroys the replica's generation identifiers and dirty
bitmap; if it held the only up-to-date copy, that copy came back as
something to sync into. It treated a crash the same as a foreign
superblock, because it had no per-volume record to tell them apart.

The record is the set of GIs this replica's volume was seen to carry.
The observer writes every GI it reads into a per-resource history file
in the satellite's state directory, which outlives the status field
(that one is dropped whenever the volume leaves a data-bearing state)
and a restart; the GI in the status counts too. A dirty log on a volume
with no record is initialised as before: a clone, a receive, a volume
added to a live resource. One with a record is repaired with apply-al,
and some UUID then in its metadata, current, a peer slot's bitmap UUID
or history, has to be one it was seen with, compared without the
Primary bit; otherwise the superblock is not this replica's and it gets
metadata of its own. A Primary that lost a peer keeps its old current
UUID only in that peer's bitmap slot, which get-gi never shows, so the
check reads dump-md. A failed apply-al or metadata read is an error
rather than a re-initialisation.

Fixes #184

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
The L6 cell and the L7 replay now assert both refusals, and the cell
checks that the passphrase still opens the volume afterwards. The vd
door is named modify-passphrase, as python-linstor spells it.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
A clone or restore rollback marks the definition in-progress while it
runs, and every retry over that mark answers 409. An apiserver replica
OOM-killed or lost between writing the mark and taking it off left it
for good, and linstor-csi retried CreateVolume against the 409 until
an operator cleared the prop by hand.

The mark now carries the time it was written. A retry, on the REST
doors and in the CLI, takes over a mark older than two minutes, well
past the rollback's own budget, and resumes the leftover. The mark
only ever stands over a definition left whole, and a rollback still
alive past its budget finds the mark gone at its next step and stands
down without deleting.

Fixes #213

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
… record whole

drbdmeta prints a v09 volume's history UUIDs as a block over several
lines, and v08's as one uuid block. The parser read neither, so a GI
that a resync had moved into the history was never found and the
replica was initialised after all. It now reads both blocks and only
bare UUID statements, so the bitmap and the device UUID stay out.

External metadata takes the same check as internal: dump-md finds the
meta disk from the .res. Deleting a resource now drops its cached last
GIs together with the file, under the lock the record is written
under, so a replica created again records its first GI. The record is
fsynced before it replaces the old one, since it exists for a node
that went down hard. The observer is built by a constructor a test can
drive, which pins the wiring into the reconciler.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
…finition

A volume formatted with a property's key fails to open with the
Secret's value, and nothing named the property. The satellite now
logs the keys it dropped, never their values.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
…fixes

- The orphan refusal moves to the store and runs on the CLI restore
  door too, which created the definition over the orphan the same way.
  It refuses diskless replicas and leaves the controller's tie-breaker
  witness to the stamp, which promotes it.
- A CLI rollback now runs on a one-minute budget of its own. The stale
  takeover rests on every rollback being over long before two minutes,
  and the CLI's had no deadline at all.
- A retry that finds the in-progress mark already taken off by another
  retry proceeds instead of answering that a rollback is running.
- Tests for the store-backed prune, which production runs, and for the
  CLI takeover.
- The snapshot-delete refusal sits above handleSnapshotDelete's doc
  comment rather than under it, and the unused RestoreSourceInUse is
  gone.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
The satellite served GET /v1/satellite/snapshots/... on every node
address with no authentication, and the answer is the raw send stream
of a snapshot. Any pod that could reach a node IP could read any volume
with a snapshot on that node; in Cozystack, platform pods without an
egress policy could.

The stream now runs over HTTPS with mutual TLS. Every satellite
presents a certificate for the fixed name blockstor-satellite, signed
by a stream CA, and verifies the peer's against it in both directions.
The files are read on every handshake, so a rotated certificate is
picked up without a restart. A satellite started without the three
--stream-tls-* flags serves and fetches nothing, and cross-node clone
and restore fall back to a DRBD initial sync; a partial set refuses to
start.

The stand issues the certificate from a CA of its own rather than
blockstor-api-ca, since a stream certificate is also a client
certificate and the apiserver accepts any from its CA.

Fixes #208

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
…ap fixture

Remove the temporary file when the rename fails, state the window the
delete lock leaves open, and give the dump fixtures the bitmap header
drbdmeta prints for v09.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
The mark was the clone and the time to the second, so two reaps of one
clone started in the same second wrote the same mark, and a reap that
kept the snapshot took off the other's. The CLI can run two such reaps.
A restore then found no reap under way while the other one went on to
delete its source. The mark now carries a random token per reap.

The owner check reads the snapshot past the cache too, so a clone
deleted right after it was created does not skip the reap.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
Copied onto a snapshot an operator takes, the owner mark would make
that snapshot claim the clone path took it. Both marks are dropped
from the props a snapshot takes and refused on definition edits.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
… base

# Conflicts:
#	docs/cli-parity-known-deltas.md
#	internal/cli/snapshot.go
…ame base

# Conflicts:
#	pkg/rest/rd_clone.go
…ame base

# Conflicts:
#	docs/linstor-migration.md
#	pkg/linstormigrate/convert.go
#	pkg/rest/vd_passphrase_bug_233.go
The CRDs lived under blockstor.cozystack.io. The project is meant to
stand on its own, under the blockstor.org domain it publishes its site
on, and the group is the one name that cannot change cheaply once
clusters carry objects under it. Nothing is deployed on it yet, so it
moves now, with no migration: clusters running a build from before
this have to be reinstalled.

Every kind moves to blockstor.org/v1alpha1: the CRDs (regenerated, one
file per kind under the new name), the RBAC markers and roles, the
finalizers, the resize-pending annotation, the scrubber that keeps the
group off the LINSTOR wire, the samples, the stand manifests, the e2e
and cli-matrix scripts and the docs. The LINSTOR migrator stamps
apiVersion from the scheme, so the objects it writes come out under
the new group; its golden manifests say so. The controller's
leader-election lease moves with it.

Two strings keep the old spelling on purpose: the seed of the stable
node UUIDs, which is a hash input rather than the group and must not
change, and the history in CHANGELOG.md and dated audit notes.

BREAKING CHANGE: the API group is blockstor.org; objects under
blockstor.cozystack.io are not read.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
The labels and annotations blockstor writes lived under blockstor.io/
and topology.blockstor.io/, a domain the project does not hold. They
move to blockstor.org/ and topology.blockstor.org/ with the API group,
for the same reason and at the same moment: nothing is deployed on
them yet.

Every key moves, the original-name annotation, the label index of
replicas and pools, the controller's bookkeeping annotations, the
unapplied-group-layer-stack annotation, the topology labels synced
from Kubernetes nodes, and the wire scrubber that keeps them off REST
reads. The CRD descriptions are regenerated, and the scripts and docs
follow.

BREAKING CHANGE: labels and annotations under blockstor.io/ are not
read.

Assisted-by: LLM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant