Skip to content

Commit 020fe69

Browse files
authored
Merge pull request #5290 from aaron-prindle/kep-5073-update-cross-field
KEP-5073: Declarative Validation: Explain and update document with cross-field validation information
2 parents fc707dc + ff2a6da commit 020fe69

File tree

1 file changed

+71
-57
lines changed
  • keps/sig-api-machinery/5073-declarative-validation-with-validation-gen

1 file changed

+71
-57
lines changed

keps/sig-api-machinery/5073-declarative-validation-with-validation-gen/README.md

Lines changed: 71 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
- [Proposal](#proposal)
99
- [Overview](#overview)
1010
- [Introduce <code>validation-gen</code>](#introduce-validation-gen)
11-
- [<code>validation-gen</code> Has No Plans To Use CEL Validation Directly](#validation-gen-has-no-plans-to-use-cel-validation-directly)
11+
- [<code>validation-gen</code>'s Approach to Dedicated Tags and Escape Hatch Tags](#validation-gens-approach-to-dedicated-tags-and-escape-hatch-tags)
1212
- [IDL Tag Authoring DevEx and User Error Messaging](#idl-tag-authoring-devex-and-user-error-messaging)
1313
- [Introduce new validation tests and test framework](#introduce-new-validation-tests-and-test-framework)
1414
- [New Validations Vs Migrating Validations](#new-validations-vs-migrating-validations)
@@ -62,6 +62,8 @@
6262
- [List Operations](#list-operations)
6363
- [Conditional Immutability](#conditional-immutability)
6464
- [Subresource-Specific Immutability](#subresource-specific-immutability)
65+
- [Cross-Field Validation](#cross-field-validation)
66+
- [Handling Ratcheting In Cross-Field Validation Tags](#handling-ratcheting-in-cross-field-validation-tags)
6567
- [Referencing Fields in Validation-Gen For Cross-Field Validation Rules](#referencing-fields-in-validation-gen-for-cross-field-validation-rules)
6668
- [Field Reference Strategies](#field-reference-strategies)
6769
- [Field Path Strategy](#field-path-strategy)
@@ -276,9 +278,8 @@ Please feel free to try out the [prototype](https://github.com/jpbetz/kubernetes
276278

277279
`validation-gen` will parse structured comments (IDL tags) within Kubernetes API type definitions (types.go files) and generate corresponding Go validation functions. `validation-gen` will be built as an extensible framework allowing new "Validators" to be added by describing what IDL tags they parse, the constraints on the IDL tags (for UX error messaging), the format of the IDL tag + how it is used (for documentation), and what actual validation logic will be for the generated code given the tagged field and associated args. The generators validators will be registered with the scheme in a similar way to generated conversion and defaulting.
278280

279-
#### `validation-gen` Has No Plans To Use CEL Validation Directly
280-
281-
The previous Declarative Validation proposal ([KEP-4153](https://github.com/kubernetes/enhancements/tree/master/keps/sig-api-machinery/4153-declarative-validation)) proposed using CEL for a number of the complex validations present in the current Kubernetes validation logic for native types (cross-field, transition, etc.). The `validation-gen` solution presented here uses go code directly for the validations which means we do not plan on evaluating CEL server side as we can write arbitrary go code to perform server side validations. This allows the `validation-gen` solution to be highly flexible and performant. If we reach a point where CEL makes sense, we can evaluate it at that time.
281+
#### `validation-gen`'s Approach to Dedicated Tags and Escape Hatch Tags
282+
The development and use of an "escape hatch" tag (CEL expression based tag, etc.) is to only be attempted after a rigorous attempt to use, enhance, or propose a dedicated tag. The goal is to use dedicated IDL tags for the vast majority of validations, ensuring that CEL is reserved for exceptional cases if used at all. Over time, if CEL appears necessary for a validation, additional discussion will occur to prioritize creating a dedicated CEL expression tag. Currently the aim is to get as far as possible with no CEL for declarative validation tags. This principal mitigates concerns about the potential for overuse of a CEL escape hatch tag and the associated review complexity for common validation rules.
282283

283284
#### IDL Tag Authoring DevEx and User Error Messaging
284285

@@ -606,13 +607,13 @@ The below rules are currently implemented or are very similar to an existing val
606607
</tr>
607608
<tr>
608609
<td>
609-
numeric limits
610+
numeric limits (constant or field refs capable)
610611
</td>
611612
<td>
612613
`+k8s:minimum`, `+k8s:maximum`, `+k8s:exclusiveMinimum`, `+k8s:exclusiveMaximum`
613614
</td>
614615
<td>
615-
`minimum`, `maximum`, `exclusiveMinimum`, `exclusiveMaximum`
616+
`minimum`, `maximum`, `exclusiveMinimum`, `exclusiveMaximum` (for constants); x-kubernetes-validations (for field refs)
616617
</td>
617618
</tr>
618619
<tr>
@@ -727,7 +728,29 @@ The below rules are currently implemented or are very similar to an existing val
727728
N/A
728729
</td>
729730
</tr>
730-
<tr>
731+
<tr>
732+
<td>
733+
union member (Discriminated/Non-Discriminated)
734+
</td>
735+
<td>
736+
+k8s:unionMember={"union": ""}
737+
</td>
738+
<td>
739+
x-kubernetes-unions
740+
</td>
741+
</tr>
742+
<tr>
743+
<td>
744+
union discriminator
745+
</td>
746+
<td>
747+
+k8s:unionDiscriminator={"union": ""}
748+
</td>
749+
<td>
750+
x-kubernetes-unions
751+
</td>
752+
</tr>
753+
<tr>
731754
<td style="background-color: null">
732755
immutable after set
733756
</td>
@@ -784,56 +807,6 @@ The below rules are currently implemented or are very similar to an existing val
784807
</tr>
785808
</table>
786809

787-
The below rules are not currently implemented in the [validation-gen prototype](https://github.com/jpbetz/kubernetes/tree/validation-gen) so the exact syntax is still WIP
788-
789-
<table>
790-
<tr>
791-
<td>
792-
<strong>Type of validation</strong>
793-
</td>
794-
<td>
795-
<strong>IDL tag</strong>
796-
</td>
797-
<td>
798-
<strong>Relative OpenAPI validation field</strong>
799-
</td>
800-
</tr>
801-
<tr>
802-
<td>
803-
regex matches
804-
</td>
805-
<td>
806-
`+k8s:pattern`
807-
</td>
808-
<td>
809-
`pattern`
810-
</td>
811-
</tr>
812-
<tr>
813-
<td>
814-
cross field validation
815-
</td>
816-
<td>
817-
`TBD
818-
</td>
819-
<td>
820-
`x-kubernetes-validations`
821-
</td>
822-
</tr>
823-
<tr>
824-
<td>
825-
<a href="https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#transition-rules">transition rules</a>
826-
</td>
827-
<td>
828-
`TBD
829-
</td>
830-
<td>
831-
`x-kubernetes-validations`
832-
</td>
833-
</tr>
834-
</table>
835-
836-
837810
### Supporting Declarative Validation IDL tags On Shared Struct Fields
838811

839812
IDL tags may be used directly on type declarations and indirectly on field and type aliases. For example:
@@ -1205,6 +1178,47 @@ type PodSpec struct {
12051178
NodeName string `json:"nodeName,omitempty"`
12061179
}
12071180
```
1181+
1182+
### Cross-Field Validation
1183+
1184+
A cross-field validation refers to any validation rule whose outcome depends on the value of more than one field within a given Kubernetes API object, potentially including fields from the previous version of the object (`oldSelf`) or external options (namely feature gates).
1185+
This differs from single-field validation which only considers the value of the field where the validation tag is placed.
1186+
1187+
These types of validations often have more complex logic and can be more difficult UX-wise to create a dedicated tag for as there are more options for representing them (tag directly on N fields, tag on one of the fields with args for the other fields, on the parent struct with args for all fields, etc.).
1188+
From an analysis of current validation logic in `kubernetes/kubernetes` across native types in `pkg/apis`, a number of validation categories were identified:
1189+
1190+
* Conditional Requirement/Validation
1191+
* Non-Discriminated Unions
1192+
* Discriminated Unions
1193+
* List/Map Integrity
1194+
* Comparison
1195+
* Status Condition Validation
1196+
* Format/Value Dependencies
1197+
* Rules where the validity or format of one field depends directly on the value of another field (or a value calculated from other fields). Includes: Checking if a generated string (like hostname + index) forms a valid DNS label, validating a field based on a prefix derived from another (like metadata name), or validating a field against a calculated aggregate value (like commonEncodingVersion).
1198+
* Transition Rules (Immutability, Ratcheting, etc.)
1199+
* At least "oneOf" Required
1200+
* Co-occurrence Requirements
1201+
* Rules defining relationships where fields must appear together, be consistent if both present, or satisfy a bi-directional implication (A if and only if B).
1202+
* Complex/Custom Logic
1203+
1204+
From this list of categories, the goal for Declarative Validation is to create dedicated tags capable of handling these categories similarly/identically to the current validation logic.
1205+
The table in "Catalog of Supported Validation Rules & Associated IDL Tags" includes a number of these cross-field validation tags targeting the above categories including:
1206+
1207+
* Non-Discriminated & Discriminated Unions: `+k8s:union[Member|Discriminator]`
1208+
* Comparison: `+k8s:[minimum|maximum]` w/ field ref support
1209+
* Transition Rules - Immutability: `+k8s:immutable`.
1210+
1211+
1212+
#### Handling Ratcheting In Cross-Field Validation Tags
1213+
For cross-field validations, the validation logic is evaluated at the common ancestor of the fields involved.
1214+
This approach is necessary for supporting ratcheting. While validation tags (eg: +k8s:maximum=siblingField, +k8s:unionMember , etc.) may be placed on an individual field for clarity, the tag and its associated validation logic will be "hoisted" to the parent struct during code generation.
1215+
This "hoisting" means the validation is treated as if it were defined on the common ancestor.
1216+
By anchoring the cross-field validation logic at the common ancestor, regardless of tag placement, the ratcheting design can more reliably determine how to perform equality checks across the relevant type nodes and decide if re-validation is necessary.
1217+
1218+
As noted in the "Ratcheting and Cross-Field Validation" section there is an additional challenge that arises if a cross-field validation rule (e.g. X < Y) is defined on a common ancestor struct/field, and an unrelated field (e.g. Z) within that same ancestor is modified (see section for more information).
1219+
In practice this means that the validation rules (or validation-gen generally) need to be more explicit where each validation rule explains “I only use these fields as inputs" for ratcheting.
1220+
This means that in the initial implementation of the cross-field dedicated tags referenced in the document (+k8s:unionMember, etc.), they will handle ratcheting of the fields they operate on directly.
1221+
12081222
### Referencing Fields in Validation-Gen For Cross-Field Validation Rules
12091223

12101224
Cross-field validation refers to validation rules that depend on the values of multiple fields within a struct or across nested structs. Cross-field validations require a method for referencing additional fields from validation-gen IDL tags.

0 commit comments

Comments
 (0)