Skip to content

CEL Validation: Add 'optionalSelf: true' config for consistency with 'optionalOldSelf: true' #132510

Open
@lalitc375

Description

@lalitc375

Add 'optionalSelf: true' config for consistency with 'optionalOldSelf: true'

Labels:

/kind feature
/sig api-machinery

why we need it?

This is a feature request to add a new optionalSelf boolean field to the x-kubernetes-validations rule definition.

When set to true, this configuration would change the type of the self variable within the Common Expression Language (CEL) expression to be an "optional". This would mirror the existing optionalOldSelf: true configuration, which does the same for the oldSelf variable.

This addition would provide a consistent and more readable syntax for writing rules that need to check for the presence of fields, especially for rules that must work across both CREATE and UPDATE operations.

Motivation:

A common validation use case is to prevent a field, once set, from being unset. For example, a user may set an optional field like spec.replicas. Future updates should not be allowed to remove this field from the object.

With the current tooling, this rule is written using the

// Prevent spec.replicas from being unset
'!has(oldSelf.replicas) || has(self.replicas)'

This rule correctly implements the logic: "either the old object did not have the field, or the new object must have the field".

This rule needs to define on the parent object, far from the field is defined.

Proposal:

Introduce a new boolean configuration field, optionalSelf, to the validation rule definition. When optionalSelf is set to true, the self variable in the CEL expression will be treated as an optional, gaining the same hasValue() and value() helper methods that oldSelf gets via optionalOldSelf: true.

With this change, the rule above could be present on the field itself.
// The proposed, more readable syntax.
// This would require both optionalOldSelf: true and the new optionalSelf: true.
oldSelf.hasValue() == self.hasValue()

Advantages:

  • Improved Readability and Symmetry: The proposed syntax is cleaner and more self-documenting. The parallel structure of oldSelf.hasValue() and self.hasValue() makes the rule's intent immediately obvious.
  • Consistency: It provides a single, consistent mechanism for checking field presence on both object states (oldSelf and self), removing the need to mix methods and global functions.

Example Use Case
Consider a CRD where the replicas field is optional but should be immutable in its presence after being set.

Current Implementation:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: mycrds.example.com
spec:
  # ...
  versions:
  - name: v1
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            x-kubernetes-validations:
            - rule: "!has(oldSelf.replicas) || has(self.replicas)"
               message: "replicas can't be unset"
            properties:
              replicas:
                type: integer
                format: int32

Proposed Implementation with optionalSelf:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: mycrds.example.com
spec:
  # ...
  versions:
  - name: v1
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              replicas:
                type: integer
                format: int32
                 x-kubernetes-validations:
                 - rule: "!oldSelf.hasValue()  || oldSelf.hasValue()  == self.hasValue()"
                   message: ".spec.replicas cannot be unset once it has been set."
                  optionalOldSelf: true
                  optionalSelf: true

Metadata

Metadata

Assignees

Labels

kind/featureCategorizes issue or PR as related to a new feature.sig/api-machineryCategorizes an issue or PR as relevant to SIG API Machinery.triage/acceptedIndicates an issue or PR 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