Description
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