Skip to content

Enforce x-kubernetes-validations CEL rules in the aggregated apiserver #2657

Open

Description

Context

cozystack/cozyvalues-gen#24 introduces a ## @immutable annotation that lands as a CEL x-kubernetes-validations: [{rule: "self == oldSelf"}] entry in the chart's values.schema.json and in the openAPISchema field on the matching ApplicationDefinition. #2639 applies it to MariaDB.spec.storageClass as the first user.

The schema annotation is consumed today by:

It is NOT consumed by the cozystack aggregated apiserver. MariaDB is not a native CRD; it is served by pkg/registry/apps/application/rest.go. The Update() method there does not invoke any structural-schema or CEL validation against (newObj, oldObj) — the specSchema built from openAPISchema (lines 100-120) is used only by applySpecDefaults in pkg/registry/apps/application/rest_defaulting.go, never for CEL rules. grep -rn 'XValidation\|cel.NewValidator' pkg/ returns nothing.

Consequence

A kubectl patch or kubectl edit that changes an annotated-as-immutable field is accepted by the apiserver today. The change has no effect on the underlying StatefulSet PVC template (so no data is moved), but the apiserver does not reject the request. The schema's immutability claim is honoured only by clients that opt in to reading x-kubernetes-validations (the UI).

Goal

Wire CEL evaluation into the aggregated apiserver's REST.Update for application-backed resources so that any rule in openAPISchema.x-kubernetes-validations is evaluated against (oldSelf, self) and the update is rejected when the rule returns false.

Suggested approach

  • Cache a compiled validator per ApplicationDefinition version (the same place specSchema is cached today).
  • In REST.Update, after the existing defaulting + admission, run the validator with the old and new spec; on failure return apierrors.NewInvalid carrying the field and CEL message.
  • Cover with a unit test that constructs an Application with an immutable field, submits an update that changes it, and asserts IsInvalid with the expected message.

Out of scope

  • The CRD path (BackupJob, Plan, BackupClass etc.) is already enforced by the regular apiextensions apiserver — no change needed there.
  • Adding more @immutable annotations to other chart values.yaml files. That is a separate sweep (feat(apps): mark stateful-app storageClass fields as immutable #2639 covers MariaDB only) and is unblocked once enforcement lands.

Related

Activity

  1. added
    kind/featureCategorizes issue or PR as related to a new feature
    area/apiIssues or PRs related to the cozystack-api aggregated API server
    on Jun 4, 2026
  2. added
    triage/acceptedIndicates an issue is ready to be actively worked on
    priority/important-longtermImportant over the long term, but may not be staffed and/or may need multiple releases to complete
    on Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/apiIssues or PRs related to the cozystack-api aggregated API serverkind/featureCategorizes issue or PR as related to a new featurepriority/important-longtermImportant over the long term, but may not be staffed and/or may need multiple releases to completetriage/acceptedIndicates an issue is ready to be actively worked on

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions