|
74 | 74 | - [Scale-Type Subresources](#scale-type-subresources)
|
75 | 75 | - [Streaming Subresources](#streaming-subresources)
|
76 | 76 | - [Ratcheting](#ratcheting)
|
| 77 | + - [Core Principles](#core-principles) |
| 78 | + - [Default Ratcheting Behavior](#default-ratcheting-behavior) |
| 79 | + - [Definition of Semantic Equivalence](#definition-of-semantic-equivalence) |
| 80 | + - [Policy on Order-Sensitive List Validation](#policy-on-order-sensitive-list-validation) |
| 81 | + - [Ratcheting and Cross-Field Validation](#ratcheting-and-cross-field-validation) |
77 | 82 | - [Test Plan](#test-plan)
|
78 | 83 | - [Prerequisite testing updates](#prerequisite-testing-updates)
|
79 | 84 | - [Unit tests](#unit-tests)
|
@@ -1421,10 +1426,57 @@ The streamed data does not require declarative validation, as it is not structur
|
1421 | 1426 |
|
1422 | 1427 | ### Ratcheting
|
1423 | 1428 |
|
1424 |
| -TODO: Document and explain how: |
| 1429 | +As Kubernetes APIs evolve, validation rules change. To minimize disruption for users with existing objects created under older rules, declarative validation will incorporate **Validation Ratcheting**. This mechanism aims to selectively bypass new or changed validation rules during object updates (`UPDATE`, `PATCH`, `APPLY`) for fields that have not been modified from their previously persisted state. |
1425 | 1430 |
|
1426 |
| -- Add general purpose ratcheting to automatically skip validation of unchanged fields |
1427 |
| -- Catalog and handle complex cases where strict equality checks are not sufficient (lots of non-trivial cases exist today) |
| 1431 | +#### Core Principles |
| 1432 | + |
| 1433 | +The design adheres to the following core principles: |
| 1434 | + |
| 1435 | +1. **Stored data is considered valid:** Any object successfully persisted was once considered valid. Subsequent apiservers must not retroactively invalidate stored objects. (Implication: fixing validation bugs post-release is challenging). |
| 1436 | +2. **Unchanged fields do not cause update rejections:** An `UPDATE` operation must not fail validation due to fields that were not modified in that operation. (Rationale: HTTP 4xx errors imply a client request problem). |
| 1437 | +3. **Semantic deep-equal is always sufficient to elide re-validation:** Kubernetes API objects adhere to canonical semantic equivalence rules (`equality.Semantic.DeepEqual`). If a deserialized object satisfies that equivalence with its prior state, re-validation can be bypassed. |
| 1438 | + * **Subtle:** List elements might individually bypass re-validation, but the list itself might still be re-validated (e.g., if reordered). |
| 1439 | + |
| 1440 | +Ratcheting is the **default behavior** during `UPDATE` operations. |
| 1441 | + |
| 1442 | +#### Default Ratcheting Behavior |
| 1443 | + |
| 1444 | +The default mechanism handles common cases by skipping re-validation if a field hasn't changed based on semantic equivalence. |
| 1445 | + |
| 1446 | +##### Definition of Semantic Equivalence |
| 1447 | + |
| 1448 | +"Semantic equivalence" builds on `k8s.io/apimachinery/pkg/api/equality.Semantic.DeepEqual` (similar to `reflect.DeepEqual` but `nil` and empty slices/maps are equivalent). The table below outlines the behavior: |
| 1449 | + |
| 1450 | +| Value type | Semantic Equivalence | Ratcheting | CRD Comparison (KEP-4008) | |
| 1451 | +| :---------------------------------- | :------------------------------------------------------------- | :----------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------ | |
| 1452 | +| Scalars (int, string, etc.) | direct equality of the value | revalidate the value if changed | same | |
| 1453 | +| Pointers | equivalence of the pointee | revalidate the value if changed | same | |
| 1454 | +| Struct | all fields are equivalent | revalidate the struct if any field changed | same | |
| 1455 | +| Struct fields<br/>`structType=granular` | - | revalidate changed fields | same | |
| 1456 | +| Struct fields<br/>`structType=atomic` | - | revalidate changed fields | [Issue #131566](https://github.com/kubernetes/kubernetes/issues/131566) (Alignment needed) | |
| 1457 | +| Slices | all elements are equivalent and the is order unchanged | revalidate the slice if any element changed or order changed | `listType=map`: no validate when re-order<br/>`listType=set`: re-validate when re-order<br/>`listType=atomic`: re-validate when re-order | |
| 1458 | +| Slice values<br/>`listType=atomic` | - | validate items which are not found (by value) in the old slice | Validate all elements (CRDs ratcheting may be expanded to match in-tree ratcheting) | |
| 1459 | +| Slice values<br/>`listType=map` | - | (re)validate items which are not found (by key) in the old slice or are changed | same | |
| 1460 | +| Slice values<br/>`listType=set` | - | validate items which are not found (by value) in the old slice | Validate all elements (CRDs ratcheting may be expanded to match in-tree ratcheting) | |
| 1461 | +| Maps | all elements are equivalent | revalidate the map if any element changed | same | |
| 1462 | +| Map values<br/>`mapType=granular` | - | (re)validate items which are not found (by key) in the old map or are changed | same | |
| 1463 | +| Map values<br/>`mapType=atomic` | - | (re)validate items which are not found (by key) in the old map or are changed | [Issue #131566](https://github.com/kubernetes/kubernetes/issues/131566) (Alignment needed) | |
| 1464 | + |
| 1465 | +**Note on Atomic Types:** The behavior for `structType=atomic` and `mapType=atomic` intentionally deviates from strict atomic re-validation. Only the specific sub-fields or key-value pairs *that were actually modified* are re-validated. This prioritizes user experience but requires alignment with CRD behavior (tracked in Issue #131566). |
| 1466 | + |
| 1467 | +#### Policy on Order-Sensitive List Validation |
| 1468 | + |
| 1469 | +The handling of `listtype=map` requires specific clarification. While its name implies order is irrelevant, the validation mechanism for native types re-validates a list if its elements are reordered. |
| 1470 | + |
| 1471 | +To ensure predictable behavior, the following rule applies: **all new declarative validation rules for list-type=map must be order-insensitive**. |
| 1472 | + |
| 1473 | +This requirement is enforced through vigilant code review to reject any proposed rule that violates this principle. |
| 1474 | + |
| 1475 | +#### Ratcheting and Cross-Field Validation |
| 1476 | + |
| 1477 | +A challenge arises if a cross-field validation rule (e.g. `X < Y`) is defined on a common ancestor struct, and an unrelated field (e.g. `Z`) within that same ancestor is modified. This change to `Z` makes the common ancestor “changed” overall, triggering re-validation of the `X < Y` rule. If this rule was recently evolved (e.g., made stricter), it might now fail even if `X` and `Y` themselves are not modified by the user’s update. This could violate the principle “Unchanged fields do not cause update rejections.” |
| 1478 | + |
| 1479 | +For the initial implementation, this behavior will be documented. Furthermore, cross-field validation rules themselves must incorporate internal ratcheting logic. For instance, generated code for dedicated cross-field tags (like `+k8s:unionMember`) will be designed to only act upon changes to the specific fields they govern and were designed to validate. |
1428 | 1480 |
|
1429 | 1481 | ### Test Plan
|
1430 | 1482 |
|
|
0 commit comments