feat: add @immutable annotation emitting CEL XValidation rule#24
Conversation
|
Warning Review limit reached
Your plan includes 1 review of capacity. Refill in 10 minutes and 46 seconds. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more review capacity refills, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces support for an Immutable (@immutable) annotation in OpenAPI definitions. It updates the parsing logic to recognize the tag, propagates the immutable state through the internal representation, and generates a Kubernetes XValidation marker (self == oldSelf) to enforce immutability during updates. Unit tests were added to verify both the parsing and the marker generation. I have no feedback to provide.
1f2d405 to
0ce4f39
Compare
0ce4f39 to
e53018b
Compare
A new `## @immutable` flag annotation in values.yaml marks a field as immutable-after-creation. cozyvalues-gen now: - parses the annotation alongside the existing @minimum/@maxLength/etc. constraint family; - carries it on Raw and Node via the new Immutable bool; - emits a kubebuilder validation marker `+kubebuilder:validation:XValidation:rule="self == oldSelf", message="<field> is immutable after creation"` on the generated Go field, which controller-gen translates into the standard x-kubernetes-validations entry on the CRD and, by passthrough, into the chart's values.schema.json. Three new tests pin the behaviour: - TestParseImmutable / TestParseImmutable_AbsentByDefault for the parser path; - TestGenerateImmutableMarker / TestGenerateImmutableMarker_NotEmittedByDefault for the Go-code emitter. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
e53018b to
4af7f4c
Compare
v1.5.0 (cozystack/cozyvalues-gen#24) adds the @immutable annotation support that the storageClass changes in this PR depend on. Without the bump, pre-commit re-runs make generate with the v1.4.0 binary and silently strips the x-kubernetes-validations entries from every regenerated values.schema.json and embedded openAPISchema, producing a drift against what's committed. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
v1.5.0 adds the @immutable annotation support (cozystack/cozyvalues-gen#24). Without the bump, pre-commit re-runs make generate with the v1.4.0 binary, silently strips any x-kubernetes-validations entries that downstream PRs add to values.schema.json / embedded openAPISchema, and reports drift against the committed artefacts. This is a no-op for the current main: no chart currently uses @immutable, so regeneration with v1.5.0 produces identical output to v1.4.0. Unblocks #2639 (apply @immutable to storageClass across stateful apps). Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
## Summary Bump the `cozyvalues-gen` binary pinned in `.github/workflows/pre-commit.yml` from `v1.4.0` to `v1.5.0`. `v1.5.0` (cozystack/cozyvalues-gen#24) adds support for the `@immutable` annotation. Without this bump, pre-commit re-runs `make generate` with the old `v1.4.0` binary on every PR; the old binary silently ignores `## @immutable` directives and regenerates `values.schema.json` / embedded `openAPISchema` without the corresponding `x-kubernetes-validations` entries — which then shows up as committed-vs-regenerated drift on any PR that tries to use the new annotation. ## No-op for main today No chart on main currently uses `@immutable` (zero hits for `## @immutable` under `packages/apps/*/values.yaml`), so this bump regenerates byte-identical output. The change is purely lifting the floor for downstream PRs that will start using the annotation. ## Unblocks - #2639 — apply `@immutable` to `storageClass` across every stateful Cozystack app (currently fails pre-commit on the v1.4.0 pin). ## Release info - v1.5.0 release notes: https://github.com/cozystack/cozyvalues-gen/releases/tag/v1.5.0 - Tag signed by F20B7CCBF00A5CAE8F9F8B8C7988329FDF395282 (Aleksey Sviridkin) - Goreleaser uploaded `cozyvalues-gen-linux-amd64.tar.gz` (the artefact this workflow downloads) along with the rest of the OS/arch matrix. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated development tooling dependencies for build process improvements. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/cozystack/cozystack/pull/2730?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary Annotate `storageClass` as immutable across every stateful chart that exposes one: clickhouse, foundationdb, harbor, http-cache, kafka (Kafka + ZooKeeper), kubernetes (top-level + nodeGroups[]), mariadb, mongodb, nats, openbao, opensearch, postgres, qdrant, rabbitmq, redis, vm-disk. The PVC-pinning argument is identical for every chart — Kubernetes fixes `storageClassName` on each PVC at creation, so editing this field on an existing resource never migrated data; the schema annotation makes that contract explicit and lets schema-aware consumers refuse the edit up-front. Schema-aware consumers today: the Cozystack UI (cozystack/cozystack-ui#6) reads the rule from the chart's openAPISchema and renders the field read-only on edit forms. This PR is **schema-side only**. The cozystack aggregated apiserver does not yet evaluate CEL rules in `openAPISchema` (`MariaDB` / `Postgres` / etc. are application-backed, not CRDs); tracking issue #2657 covers wiring CEL into `pkg/registry/apps/application/rest.go::Update`. Until that lands, an apiserver-level `kubectl patch` that changes `storageClass` is still accepted — but the underlying StatefulSet PVCs do not move, so there's no data-corruption risk, only a confusing UX. The UI catches that case today; the apiserver gate closes it permanently. ## What changes For each app in the list above: - `packages/apps/<app>/values.yaml` — gain a `## @immutable` directive directly above (or below, for fields inside a `@typedef`) the `storageClass` line. - Regenerated via `make generate` in each package: - `api/apps/v1alpha1/<app>/types.go` — kubebuilder `XValidation` marker on the relevant field(s). - `packages/apps/<app>/values.schema.json` — `x-kubernetes-validations: [{rule: "self == oldSelf", message: …}]` on `storageClass`. - `packages/apps/<app>/README.md` — regenerated parameter table. - `packages/system/<app>-rd/cozyrds/<app>.yaml` — the embedded `openAPISchema` on the ApplicationDefinition reflects the change. Refactor: `pkg/registry/apps/application/rest.go` factors the inline schema-build path into a new `buildSpecSchema` helper so it can be unit-tested without standing up a fake apiserver. Three regression tests in `pkg/registry/apps/application/rest_spec_schema_test.go` pin the contract that `x-kubernetes-validations` survives the v1→internal→structural conversion (test loads the actual MariaDB ApplicationDefinition fixture on disk, asserts the rule reaches the structural schema). Empty-input and malformed-JSON paths are also pinned. No e2e admission test is added because the apiserver does not yet enforce the rule — see #2657. No changelog entry: schema-side annotation is invisible at the apiserver layer today (UI-only consumer) and the release-cut PR will add the entry when admission enforcement lands. ## Dependencies Requires cozystack/cozyvalues-gen#24 to be merged and released, and the `cozyvalues-gen` binary pin in `.github/workflows/pre-commit.yml` line 50 bumped to the release that includes that PR. Pre-commit CI will fail until both happen — the stock `v1.3.0` binary silently ignores `## @immutable` and regenerates artefacts without the rule, producing a drift against what's committed. Once that ordering is satisfied, rebase this branch and the pre-commit job will be green. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * storageClass is now treated as immutable after creation for many stateful apps (enforced by chart/CRD schema validation). * **Documentation** * Added a storage immutability guide explaining the contract and enforcement. * Updated chart READMEs and values docs to mark storageClass as immutable and clarified nodeGroup storage remains intentionally mutable. * **Tests** * Added tests validating schema parsing and preservation of immutability rules. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/cozystack/cozystack/pull/2639?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
Summary
A new
## @immutableflag annotation in values.yaml marks a field as immutable-after-creation.cozyvalues-gen now:
@minimum/@maxLength/etc. constraint family.RawandNodevia a newImmutable boolfield.controller-gen translates that marker into the standard
x-kubernetes-validationsentry on the CRD; by passthrough it lands in the chart'svalues.schema.jsonas well.Why
I want the Cozystack UI to be able to mark immutable resource fields as read-only on edit forms. The decision was to use the schema as the source of truth — specifically the standard K8s
x-kubernetes-validationsCEL ruleself == oldSelf— rather than maintaining a hand-rolled mapping on the UI side. cozyvalues-gen is the canonical pipeline that produces that schema, so the annotation belongs here.Tests
Four new tests pin the behaviour:
TestParseImmutable/TestParseImmutable_AbsentByDefaultfor the parser path.TestGenerateImmutableMarker/TestGenerateImmutableMarker_NotEmittedByDefaultfor the Go-code emitter.Full suite is green:
Downstream
Two follow-up PRs depend on this:
storageClassand regenerate.