Address comments
This commit is contained in:
+30
-22
@@ -716,7 +716,7 @@ CustomResourceDefinition schemas using the `x-kubernetes-validations` extension.
|
|||||||
The Rule is scoped to the location of the `x-kubernetes-validations` extension in the schema.
|
The Rule is scoped to the location of the `x-kubernetes-validations` extension in the schema.
|
||||||
And `self` variable in the CEL expression is bound to the scoped value.
|
And `self` variable in the CEL expression is bound to the scoped value.
|
||||||
|
|
||||||
Note all the validation rules are scoped to the current object, no cross-object or stateful validation rules are supported.
|
All validation rules are scoped to the current object: no cross-object or stateful validation rules are supported.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
@@ -996,18 +996,18 @@ Here is the declarations type mapping between OpenAPIv3 and CEL type:
|
|||||||
xref: [CEL types](https://github.com/google/cel-spec/blob/v0.6.0/doc/langdef.md#values), [OpenAPI
|
xref: [CEL types](https://github.com/google/cel-spec/blob/v0.6.0/doc/langdef.md#values), [OpenAPI
|
||||||
types](https://swagger.io/specification/#data-types), [Kubernetes Structural Schemas](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#specifying-a-structural-schema).
|
types](https://swagger.io/specification/#data-types), [Kubernetes Structural Schemas](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#specifying-a-structural-schema).
|
||||||
|
|
||||||
#### Function Library
|
#### Validation functions {#available-validation-functions}
|
||||||
|
|
||||||
Functions available include:
|
Functions available include:
|
||||||
- CEL standard functions, defined in the[list of standard definitions](https://github.com/google/cel-spec/blob/v0.7.0/doc/langdef.md#list-of-standard-definitions)
|
- CEL standard functions, defined in the [list of standard definitions](https://github.com/google/cel-spec/blob/v0.7.0/doc/langdef.md#list-of-standard-definitions)
|
||||||
- CEL standard [macros](https://github.com/google/cel-spec/blob/v0.7.0/doc/langdef.md#macros)
|
- CEL standard [macros](https://github.com/google/cel-spec/blob/v0.7.0/doc/langdef.md#macros)
|
||||||
- CEL [extended string function library](https://pkg.go.dev/github.com/google/cel-go@v0.11.2/ext#Strings)
|
- CEL [extended string function library](https://pkg.go.dev/github.com/google/cel-go@v0.11.2/ext#Strings)
|
||||||
- Kubernetes [CEL extension library](https://pkg.go.dev/k8s.io/apiextensions-apiserver@v0.24.0-alpha.4/pkg/apiserver/schema/cel/library#pkg-functions)
|
- Kubernetes [CEL extension library](https://pkg.go.dev/k8s.io/apiextensions-apiserver@v0.24.0/pkg/apiserver/schema/cel/library#pkg-functions)
|
||||||
|
|
||||||
#### Transition Rules
|
#### Transition rules
|
||||||
|
|
||||||
A rule that contains an expression referencing the identifier `oldSelf` is implicitly considered a
|
A rule that contains an expression referencing the identifier `oldSelf` is implicitly considered a
|
||||||
"transition rule". Transition rules allow schema authors to prevent certain transitions between two
|
_transition rule_. Transition rules allow schema authors to prevent certain transitions between two
|
||||||
otherwise valid states. For example:
|
otherwise valid states. For example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
@@ -1036,33 +1036,40 @@ Errors will be generated on CRD writes if a schema node contains a transition ru
|
|||||||
applied, e.g. "*path*: update rule *rule* cannot be set on schema because the schema or its parent
|
applied, e.g. "*path*: update rule *rule* cannot be set on schema because the schema or its parent
|
||||||
schema is not mergeable".
|
schema is not mergeable".
|
||||||
|
|
||||||
Transition rules are only allowed on "correlatable" portions of a schema.
|
Transition rules are only allowed on _correlatable portions_ of a schema.
|
||||||
A portion of the schema is correlatable if all `array` parent schemas are of type `x-kubernetes-list-type=map`; any `set`or `atomic`array parent schemas make it impossible to unambiguously correlate a `self` with `oldSelf`.
|
A portion of the schema is correlatable if all `array` parent schemas are of type `x-kubernetes-list-type=map`; any `set`or `atomic`array parent schemas make it impossible to unambiguously correlate a `self` with `oldSelf`.
|
||||||
|
|
||||||
##### Use Cases
|
Here are some examples for transition rules:
|
||||||
|
|
||||||
|
{{< table caption="Transition rules examples" >}}
|
||||||
| Use Case | Rule
|
| Use Case | Rule
|
||||||
| -------- | --------
|
| -------- | --------
|
||||||
| Immutability | `self.foo == oldSelf.foo`
|
| Immutability | `self.foo == oldSelf.foo`
|
||||||
| Prevent modification/removal once assigned | `oldSelf != 'bar' \|\| self == 'bar'` or `!has(oldSelf.field) \|\| has(self.field)`
|
| Prevent modification/removal once assigned | `oldSelf != 'bar' \|\| self == 'bar'` or `!has(oldSelf.field) \|\| has(self.field)`
|
||||||
| Append-only set | `self.all(element, element in oldSelf)`
|
| Append-only set | `self.all(element, element in oldSelf)`
|
||||||
| If previous value was X, new value can only be A or B, not Y or Z | `oldSelf != 'X' \|\| self in ['A', 'B']`
|
| If previous value was X, new value can only be A or B, not Y or Z | `oldSelf != 'X' \|\| self in ['A', 'B']`
|
||||||
| Nondecreasing counters | `self >= oldSelf`
|
| Monotonic (non-decreasing) counters | `self >= oldSelf`
|
||||||
|
{{< /table >}}
|
||||||
|
|
||||||
#### Resource Constraints
|
#### Resource use by validation functions
|
||||||
|
|
||||||
Resource consumption of validation rules is checked at CustomResourceDefinition creation and update time. If a rule is estimated to be prohibitively expensive to execute, it will result in a validation error. A similar
|
When you create or update a CustomResourceDefinition that uses validation rules,
|
||||||
system is used at runtime that observes the actions the interpreter takes. If the interpreter executes
|
the API server checks the likely impact of running those validation rules. If a rule is
|
||||||
|
estimated to be prohibitively expensive to execute, the API server rejects the create
|
||||||
|
or update operation, and returns an error message.
|
||||||
|
A similar system is used at runtime that observes the actions the interpreter takes. If the interpreter executes
|
||||||
too many instructions, execution of the rule will be halted, and an error will result.
|
too many instructions, execution of the rule will be halted, and an error will result.
|
||||||
Each CustomResourceDefinition is also allowed a certain amount of resources to finish executing all of
|
Each CustomResourceDefinition is also allowed a certain amount of resources to finish executing all of
|
||||||
its validation rules. If the sum total of its rules are estimated at creation time to go over that limit,
|
its validation rules. If the sum total of its rules are estimated at creation time to go over that limit,
|
||||||
then a validation error will also occur.
|
then a validation error will also occur.
|
||||||
|
|
||||||
In general, both systems will allow rules that do not need to iterate; these rules will
|
You are unlikely to encounter issues with the resource budget for validation if you only
|
||||||
always take the same amount of time regardless of how large their input is. `self.foo == 1` will be allowed.
|
specify rules that always take the same amount of time regardless of how large their input is.
|
||||||
But if `foo` is a string and we instead have `self.foo.contains("someString")`, our rule will take
|
For example, a rule that asserts that `self.foo == 1` does not by itself have any
|
||||||
longer to execute depending on how long `foo` is. Another example would be if `foo` was an array, and we
|
risk of rejection on validation resource budget groups.
|
||||||
had a rule `self.foo.all(x, x > 5)`. The cost system will always assume the worst-case scenario if
|
But if `foo` is a string and you define a validation rule `self.foo.contains("someString")`, that rule takes
|
||||||
|
longer to execute depending on how long `foo` is.
|
||||||
|
Another example would be if `foo` were an array, and you specified a validation rule `self.foo.all(x, x > 5)`. The cost system always assumes the worst-case scenario if
|
||||||
a limit on the length of `foo` is not given, and this will happen for anything that can be iterated
|
a limit on the length of `foo` is not given, and this will happen for anything that can be iterated
|
||||||
over (lists, maps, etc.).
|
over (lists, maps, etc.).
|
||||||
|
|
||||||
@@ -1081,17 +1088,18 @@ openAPIV3Schema:
|
|||||||
- rule: "self.all(x, x.contains('a string'))"
|
- rule: "self.all(x, x.contains('a string'))"
|
||||||
```
|
```
|
||||||
|
|
||||||
The cost system will not allow this rule. Using `self.all` means calling `contains` on every string in `foo`,
|
then the API server rejects this rule on validation budget grounds with error:
|
||||||
which in turn will check the given string to see if it contains `'a string'`. Without limits, this is a very
|
|
||||||
expensive rule:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
spec.validation.openAPIV3Schema.properties[spec].properties[foo].x-kubernetes-validations[0].rule: Forbidden:
|
spec.validation.openAPIV3Schema.properties[spec].properties[foo].x-kubernetes-validations[0].rule: Forbidden:
|
||||||
CEL rule exceeded budget by more than 100x (try simplifying the rule, or adding maxItems, maxProperties, and
|
CEL rule exceeded budget by more than 100x (try simplifying the rule, or adding maxItems, maxProperties, and
|
||||||
maxLength where arrays, maps, and strings are used)
|
maxLength where arrays, maps, and strings are used)
|
||||||
```
|
```
|
||||||
|
|
||||||
Without limits being set, the estimated cost of this rule will exceed the per-rule cost limit. But if we
|
The rejection happens because `self.all` implies calling `contains()` on every string in `foo`,
|
||||||
|
which in turn will check the given string to see if it contains `'a string'`. Without limits, this is a very
|
||||||
|
expensive rule.
|
||||||
|
|
||||||
|
If you do not specify any validation limit, the estimated cost of this rule will exceed the per-rule cost limit. But if you
|
||||||
add limits in the appropriate places, the rule will be allowed:
|
add limits in the appropriate places, the rule will be allowed:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
Reference in New Issue
Block a user