Skip to content

Commit b4738e0

Browse files
KEP-5073: Declarative Validation: Explain and update document with cross-field field reference validation information (#5363)
* KEP-5073: Declarative Validation: Explain and update document with cross-field field reference validation information * address comments - update examples * address comments - remove +k8s:reference * address comments - add json field tags names to all examples * address comments - fix listMapItem usage and show all type declarations * address comments - remove subfield case from example * address comments - remove bullet for conditional validation preference for virtual fields * fix rebase, add back table entries
1 parent c0886bb commit b4738e0

File tree

1 file changed

+173
-0
lines changed
  • keps/sig-api-machinery/5073-declarative-validation-with-validation-gen

1 file changed

+173
-0
lines changed

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

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@
6262
- [List Operations](#list-operations)
6363
- [Conditional Immutability](#conditional-immutability)
6464
- [Subresource-Specific Immutability](#subresource-specific-immutability)
65+
- [Referencing Fields in Validation-Gen For Cross-Field Validation Rules](#referencing-fields-in-validation-gen-for-cross-field-validation-rules)
66+
- [Field Reference Strategies](#field-reference-strategies)
67+
- [Field Path Strategy](#field-path-strategy)
68+
- [Virtual Field Strategy](#virtual-field-strategy)
69+
- [Choosing Between Field Paths and Virtual Fields](#choosing-between-field-paths-and-virtual-fields)
70+
- [Tag Placement and Hoisting](#tag-placement-and-hoisting)
71+
- [Comprehensive Example:](#comprehensive-example)
6572
- [Subresources](#subresources)
6673
- [Status-Type Subresources](#status-type-subresources)
6774
- [Scale-Type Subresources](#scale-type-subresources)
@@ -748,6 +755,28 @@ The below rules are currently implemented or are very similar to an existing val
748755
N/A
749756
</td>
750757
</tr>
758+
<tr>
759+
<td style="background-color: null">
760+
group membership (virtual field)
761+
</td>
762+
<td style="background-color: null">
763+
`+k8s:memberOf(group: &lt;groupname>)`
764+
</td>
765+
<td style="background-color: null">
766+
N/A
767+
</td>
768+
</tr>
769+
<tr>
770+
<td style="background-color: null">
771+
list map item reference (virtual field)
772+
</td>
773+
<td style="background-color: null">
774+
`+k8s:listMapItem(pairs: [[key,value],...])`
775+
</td>
776+
<td style="background-color: null">
777+
N/A
778+
</td>
779+
</tr>
751780
</table>
752781

753782
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
@@ -1171,6 +1200,150 @@ type PodSpec struct {
11711200
NodeName string `json:"nodeName,omitempty"`
11721201
}
11731202
```
1203+
### Referencing Fields in Validation-Gen For Cross-Field Validation Rules
1204+
1205+
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.
1206+
1207+
#### Field Reference Strategies
1208+
1209+
`validation-gen` supports two strategies for referencing fields in cross-field validations:
1210+
1211+
1. **Field Paths** - Direct references using dot notation
1212+
1. **Virtual Fields** - Identifier-based references for relationships
1213+
1214+
##### Field Path Strategy
1215+
1216+
Field paths use JSON field names with dot notation to reference other fields.
1217+
1218+
**When tags are placed on fields:**
1219+
1220+
- Field paths are relative to the parent typedef (allowing sibling access)
1221+
- No special `self` or `parent` keywords allowed
1222+
- Example: `siblingField` or `siblingStruct.childField`
1223+
1224+
```go
1225+
type Config struct {
1226+
MinValue int32 `json:"minValue"`
1227+
MaxValue int32 `json:"maxValue"`
1228+
1229+
// +k8s:minimum(constraint: minValue)
1230+
// +k8s:maximum(constraint: maxValue)
1231+
Current int32 `json:"current"`
1232+
}
1233+
```
1234+
1235+
**When tags are placed on the common ancestor typedef:**
1236+
1237+
- All references are to child fields
1238+
1239+
```go
1240+
// NOTE: this is illustrative - minimum/maximum tags do not support "target"
1241+
// +k8s:minimum(target: current, constraint: minValue)
1242+
// +k8s:maximum(target: current, constraint: maxValue)
1243+
type Config struct {
1244+
MinValue int32 `json:"minValue"`
1245+
MaxValue int32 `json:"maxValue"`
1246+
Current int32 `json:"current"`
1247+
}
1248+
```
1249+
1250+
##### Virtual Field Strategy
1251+
1252+
Virtual fields provide identifier-based references for cross-field relationships. They are scoped to a GVK (GroupVersionKind) namespace.
1253+
1254+
**Virtual Field Reference Tags:**
1255+
1256+
- `+k8s:memberOf(group: <groupname>)` - Adds field to a group for union/mutual exclusion validation
1257+
- `+k8s:listMapItem(pairs: [[key,value],...])` - References specific items by key value in listType=map lists where the key(s) must include all of the list maps's keys
1258+
1259+
```go
1260+
type Config struct {
1261+
// +k8s:listType=map
1262+
// +k8s:listMapKey=type
1263+
// +k8s:union(union: terminalStatus)
1264+
// +k8s:listMapItem([["type","Succeeded"]])=+k8s:memberOf(group: terminalStatus)
1265+
// +k8s:listMapItem([["type","Failed"]])=+k8s:memberOf(group: terminalStatus)
1266+
Conditions []Condition `json:"conditions"`
1267+
}
1268+
1269+
type Condition struct {
1270+
Type string `json:"type"`
1271+
Status string `json:"status"`
1272+
}
1273+
```
1274+
1275+
**Virtual Field Scope:**
1276+
1277+
- Virtual fields are scoped to the given GVK. The logical namespace for virtual fields is their GVK. For example, the virtual field names in apps/v1/Deployment are internally namespaced as apps/v1/Deployment:minReplicas, apps/v1/Deployment:maxReplicas, etc. However, within the same GVK, these can be referenced using just their simple names (minReplicas, maxReplicas) without the namespace prefix.
1278+
- **Cross-GVK references are NOT supported**
1279+
- This mainly impacts any ObjectMeta name and generateName validation, which MUST be done using subfield and/or field-paths, not with virtual field references.
1280+
1281+
#### Choosing Between Field Paths and Virtual Fields
1282+
1283+
**Use Field Paths when:**
1284+
1285+
- Making simple references to sibling or child fields
1286+
- All referenced fields are accessible via dot notation
1287+
1288+
**Use Virtual Fields when:**
1289+
1290+
- Implementing group-based rules (union validations, etc.)
1291+
- Referencing specific items in listType=map
1292+
1293+
#### Tag Placement and Hoisting
1294+
1295+
Cross-field validation tags can be placed on either tag location depending on what the tag supports:
1296+
1297+
1. **On fields directly** - More intuitive but requires implicit hoisting to common ancestor
1298+
1. **On common ancestor typedef** - Explicit placement where validation actually occurs
1299+
1300+
All cross-field validations are ultimately hoisted to execute at the common ancestor level to ensure proper access to all referenced fields during validation.
1301+
1302+
#### Comprehensive Example:
1303+
1304+
```go
1305+
type Config struct {
1306+
MinValue int `json:"minValue"`
1307+
MaxCpu int `json:"maxCpu"`
1308+
MaxThreshold int `json:"maxThreshold"`
1309+
1310+
// +k8s:minimum(constraint: minValue)
1311+
// +k8s:maximum(constraint: limits.maxValue)
1312+
Current int `json:"current"`
1313+
1314+
Limits LimitConfig `json:"limits"`
1315+
1316+
Settings SettingsConfig `json:"settings"`
1317+
1318+
// +k8s:eachVal=+k8s:maximum(constraint: maxThreshold)
1319+
Thresholds []int `json:"thresholds"`
1320+
1321+
// +k8s:listType=map
1322+
// +k8s:listMapKey=type
1323+
// +k8s:union(union: terminalStatus)
1324+
// +k8s:listMapItem([["type","Succeeded"]])=+k8s:memberOf(group: terminalStatus)
1325+
// +k8s:listMapItem([["type","Failed"]])=+k8s:memberOf(group: terminalStatus)
1326+
Conditions []Condition `json:"conditions"`
1327+
}
1328+
1329+
type LimitConfig struct {
1330+
MaxValue int `json:"maxValue"`
1331+
}
1332+
1333+
type SettingsConfig struct {
1334+
Resources ResourceConfig `json:"resources"`
1335+
}
1336+
1337+
type ResourceConfig struct {
1338+
Cpu int `json:"cpu"`
1339+
}
1340+
1341+
type Condition struct {
1342+
Type string `json:"type"`
1343+
Status string `json:"status"`
1344+
}
1345+
```
1346+
11741347

11751348
### Subresources
11761349

0 commit comments

Comments
 (0)