The console Containerfile ran `corepack prepare pnpm@latest`, so the image build could drift to a newer pnpm major than the `pnpm@11.1.2` pinned in package.json and used by CI. `--frozen-lockfile` guards the lockfile, but pinning the builder makes the image build reproducible and consistent with CI. Also repoints docs/storage-immutability.md off the now-vendored cozystack-ui#6 link to the in-tree console path (follow-up from #2963). Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2.6 KiB
storageClass is immutable after creation
Every stateful Cozystack application that exposes a storageClass parameter declares it immutable in the chart schema (x-kubernetes-validations: [{rule: "self == oldSelf"}]). This document explains the contract and where it is enforced.
Why
storageClass binds the StatefulSet's PVC template. Kubernetes fixes PersistentVolumeClaim.spec.storageClassName at PVC creation time — editing volumeClaimTemplates[].spec.storageClassName on an existing StatefulSet does not retroactively migrate any data. A user who edits storageClass on an existing resource may believe their data is being moved to the new class; it is not. Locking the field at the schema layer makes that contract explicit.
What enforces it
| Consumer | Behavior |
|---|---|
Cozystack UI (packages/system/dashboard/images/console) |
Reads the CEL rule from the chart's openAPISchema and renders storageClass as a disabled, helper-text-annotated field on edit forms. Save-time overlay reinstates the original value before PUT. |
kubectl edit / kubectl patch against the cozystack aggregated apiserver |
Currently accepted — the apiserver does not yet evaluate CEL rules embedded in ApplicationDefinition.openAPISchema. The change passes through but does not propagate to existing PVCs (see "Why" above), so no data corruption is possible. Apiserver enforcement is tracked in cozystack/cozystack#2657. |
kubectl edit / kubectl patch against native CRDs |
Enforced today by the standard apiextensions apiserver. |
Apps covered
The following charts annotate at least one storageClass field as immutable:
clickhouse,foundationdb,harbor,http-cache,kafka(Kafka + ZooKeeper),kubernetes(top-level only — see below),mariadb,mongodb,nats,openbao,opensearch,postgres,qdrant,rabbitmq,redis,vm-disk.
The per-node-group kubernetes.nodeGroups[name].storageClass field is intentionally NOT annotated immutable: it is optional and undefaulted, so a strict self == oldSelf rule would block any future attempt to set it on an existing node group. If a richer ratcheting form (!has(oldSelf.storageClass) || self.storageClass == oldSelf.storageClass) becomes available, the annotation should be added back.
If you add a new stateful chart that exposes storageClass, annotate it the same way:
## @param {string} storageClass - StorageClass used to store the data.
## @immutable
storageClass: ""
For fields declared inside a @typedef, place ## @immutable on the line below the ## @field it should attach to.