Skip to content

Commit 5ac4e71

Browse files
committed
convert some override webhook logic to CEL & add tests
Signed-off-by: Britania Rodriguez Reyes <[email protected]>
1 parent 7bdf859 commit 5ac4e71

17 files changed

+773
-46
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Breadcrumb for CEL Conversion: ClusterResourceOverride
2+
3+
## Requirements
4+
- Convert webhook validation logic for ClusterResourceOverride to CEL XValidation rules in the CRD.
5+
- Remove/replace webhook logic that is now enforced by CEL.
6+
- Ensure all constraints that can be enforced by CEL are migrated.
7+
- Document what remains in webhook (cross-object checks).
8+
9+
## Additional comments from user
10+
- User approved the plan to proceed with CEL conversion for ClusterResourceOverride.
11+
12+
## Plan
13+
### Phase 1: Analyze and Write CEL Expressions
14+
- [ ] Task 1.1: Identify all webhook validation logic that can be enforced by CEL.
15+
- [ ] Task 1.2: Write CEL XValidation rules for:
16+
- No labelSelector in clusterResourceSelectors
17+
- Name is required in clusterResourceSelectors
18+
- No duplicate clusterResourceSelectors
19+
- [ ] Task 1.3: Add CEL rules to the CRD YAML.
20+
21+
### Phase 2: Remove/Refactor Webhook Logic
22+
- [ ] Task 2.1: Remove/replace Go validation logic now enforced by CEL.
23+
- [ ] Task 2.2: Update webhook to only perform cross-object checks (e.g., uniqueness across objects).
24+
25+
### Phase 3: Test and Document
26+
- [ ] Task 3.1: Test CRD validation with CEL rules.
27+
- [ ] Task 3.2: Update documentation and this breadcrumb with before/after comparison.
28+
29+
## Decisions
30+
- CEL will be used for all per-object validation that does not require access to other objects.
31+
- Webhook will remain for cross-object uniqueness checks.
32+
33+
## Implementation Details
34+
- CEL rules will be added under `x-kubernetes-validations` in the CRD.
35+
- Go code will be refactored to avoid duplicate validation.
36+
37+
## Changes Made
38+
- (To be updated after implementation)
39+
40+
## Before/After Comparison
41+
- (To be updated after implementation)
42+
43+
## References
44+
- `/pkg/utils/validator/clusterresourceoverride.go` (Go validation logic)
45+
- `/config/crd/bases/placement.kubernetes-fleet.io_clusterresourceoverrides.yaml` (CRD definition)
46+
- [Kubernetes CEL docs](https://kubernetes.io/docs/reference/using-api/cel/)

apis/placement/v1alpha1/override_types.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
66
You may obtain a copy of the License at
77
8-
http://www.apache.org/licenses/LICENSE-2.0
8+
http://www.apache.org/licenses/LICENSE-2.0
99
1010
Unless required by applicable law or agreed to in writing, software
1111
distributed under the License is distributed on an "AS IS" BASIS,
@@ -91,6 +91,8 @@ type OverridePolicy struct {
9191
}
9292

9393
// OverrideRule defines how to override the selected resources on the target clusters.
94+
// +kubebuilder:validation:XValidation:rule="!(self.overrideType == 'Delete' && has(self.jsonPatchOverrides) && self.jsonPatchOverrides.size() > 0)", message="jsonPatchOverrides cannot be set when the override type is Delete."
95+
// +kubebuilder:validation:XValidation:rule="!(self.overrideType == 'JSONPatch' && !has(self.jsonPatchOverrides) && self.jsonPatchOverrides.size() == 0)", message="jsonPatchOverrides must be set when the override type is JSONPatch."
9496
type OverrideRule struct {
9597
// ClusterSelectors selects the target clusters.
9698
// The resources will be overridden before applying to the matching clusters.
@@ -198,6 +200,9 @@ type JSONPatchOverride struct {
198200
// Path defines the target location.
199201
// Note: override will fail if the resource path does not exist.
200202
// +required
203+
// +kubebuilder:validation:XValidation:rule="!(self == '/kind' || self == '/apiVersion')",message="Path cannot target typeMeta fields (kind, apiVersion)"
204+
// +kubebuilder:validation:XValidation:rule="!(self.startsWith('/metadata') && !(self.startsWith('/metadata/annotations') || self.startsWith('/metadata/labels')))",message="Path can only target annotations and labels under metadata"
205+
// +kubebuilder:validation:XValidation:rule="!self.startsWith('/status')",message="Path cannot target status fields"
201206
Path string `json:"path"`
202207
// Value defines the content to be applied on the target location.
203208
// Value should be empty when operator is `remove`.

config/crd/bases/placement.kubernetes-fleet.io_clusterresourcebindings.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -542,8 +542,8 @@ spec:
542542
managed by Fleet (i.e., specified in the hub cluster manifest). This is the default
543543
option.
544544
545-
Note that this option would revert any ad-hoc changes made on the member cluster side in
546-
the managed fields; if you would like to make temporary edits on the member cluster side
545+
Note that this option would revert any ad-hoc changes made on the member cluster side in the
546+
managed fields; if you would like to make temporary edits on the member cluster side
547547
in the managed fields, switch to IfNotDrifted option. Note that changes in unmanaged
548548
fields will be left alone; if you use the FullDiff compare option, such changes will
549549
be reported as drifts.

config/crd/bases/placement.kubernetes-fleet.io_clusterresourceoverrides.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,16 @@ spec:
327327
Path defines the target location.
328328
Note: override will fail if the resource path does not exist.
329329
type: string
330+
x-kubernetes-validations:
331+
- message: Path cannot target typeMeta fields (kind,
332+
apiVersion)
333+
rule: '!(self == ''/kind'' || self == ''/apiVersion'')'
334+
- message: Path can only target annotations and labels
335+
under metadata
336+
rule: '!(self.startsWith(''/metadata'') && !(self.startsWith(''/metadata/annotations'')
337+
|| self.startsWith(''/metadata/labels'')))'
338+
- message: Path cannot target status fields
339+
rule: '!self.startsWith(''/status'')'
330340
value:
331341
description: |-
332342
Value defines the content to be applied on the target location.
@@ -352,6 +362,15 @@ spec:
352362
- Delete
353363
type: string
354364
type: object
365+
x-kubernetes-validations:
366+
- message: jsonPatchOverrides cannot be set when the override
367+
type is Delete.
368+
rule: '!(self.overrideType == ''Delete'' && has(self.jsonPatchOverrides)
369+
&& self.jsonPatchOverrides.size() > 0)'
370+
- message: jsonPatchOverrides must be set when the override
371+
type is JSONPatch.
372+
rule: '!(self.overrideType == ''JSONPatch'' && !has(self.jsonPatchOverrides)
373+
&& self.jsonPatchOverrides.size() == 0)'
355374
maxItems: 20
356375
minItems: 1
357376
type: array

config/crd/bases/placement.kubernetes-fleet.io_clusterresourceoverridesnapshots.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,16 @@ spec:
341341
Path defines the target location.
342342
Note: override will fail if the resource path does not exist.
343343
type: string
344+
x-kubernetes-validations:
345+
- message: Path cannot target typeMeta fields
346+
(kind, apiVersion)
347+
rule: '!(self == ''/kind'' || self == ''/apiVersion'')'
348+
- message: Path can only target annotations and
349+
labels under metadata
350+
rule: '!(self.startsWith(''/metadata'') && !(self.startsWith(''/metadata/annotations'')
351+
|| self.startsWith(''/metadata/labels'')))'
352+
- message: Path cannot target status fields
353+
rule: '!self.startsWith(''/status'')'
344354
value:
345355
description: |-
346356
Value defines the content to be applied on the target location.
@@ -366,6 +376,15 @@ spec:
366376
- Delete
367377
type: string
368378
type: object
379+
x-kubernetes-validations:
380+
- message: jsonPatchOverrides cannot be set when the override
381+
type is Delete.
382+
rule: '!(self.overrideType == ''Delete'' && has(self.jsonPatchOverrides)
383+
&& self.jsonPatchOverrides.size() > 0)'
384+
- message: jsonPatchOverrides must be set when the override
385+
type is JSONPatch.
386+
rule: '!(self.overrideType == ''JSONPatch'' && !has(self.jsonPatchOverrides)
387+
&& self.jsonPatchOverrides.size() == 0)'
369388
maxItems: 20
370389
minItems: 1
371390
type: array

config/crd/bases/placement.kubernetes-fleet.io_clusterresourceplacements.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1807,8 +1807,8 @@ spec:
18071807
managed by Fleet (i.e., specified in the hub cluster manifest). This is the default
18081808
option.
18091809
1810-
Note that this option would revert any ad-hoc changes made on the member cluster side in
1811-
the managed fields; if you would like to make temporary edits on the member cluster side
1810+
Note that this option would revert any ad-hoc changes made on the member cluster side in the
1811+
managed fields; if you would like to make temporary edits on the member cluster side
18121812
in the managed fields, switch to IfNotDrifted option. Note that changes in unmanaged
18131813
fields will be left alone; if you use the FullDiff compare option, such changes will
18141814
be reported as drifts.

config/crd/bases/placement.kubernetes-fleet.io_clusterresourcesnapshots.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ spec:
4040
Each snapshot MUST have the following labels:
4141
- `CRPTrackingLabel` which points to its owner CRP.
4242
- `ResourceIndexLabel` which is the index of the snapshot group.
43+
44+
The first snapshot of the index group MAY have the following labels:
4345
- `IsLatestSnapshotLabel` which indicates whether the snapshot is the latest one.
4446
4547
All the snapshots within the same index group must have the same ResourceIndexLabel.
@@ -50,6 +52,9 @@ spec:
5052
5153
Each snapshot (excluding the first snapshot) MUST have the following annotations:
5254
- `SubindexOfResourceSnapshotAnnotation` to store the subindex of resource snapshot in the group.
55+
56+
Snapshot may have the following annotations to indicate the time of next resourceSnapshot candidate detected by the controller:
57+
- `NextResourceSnapshotCandidateDetectionTimeAnnotation` to store the time of next resourceSnapshot candidate detected by the controller.
5358
properties:
5459
apiVersion:
5560
description: |-
@@ -186,6 +191,9 @@ spec:
186191
187192
Each snapshot (excluding the first snapshot) MUST have the following annotations:
188193
- `SubindexOfResourceSnapshotAnnotation` to store the subindex of resource snapshot in the group.
194+
195+
Snapshot may have the following annotations to indicate the time of next resourceSnapshot candidate detected by the controller:
196+
- `NextResourceSnapshotCandidateDetectionTimeAnnotation` to store the time of next resourceSnapshot candidate detected by the controller.
189197
properties:
190198
apiVersion:
191199
description: |-

config/crd/bases/placement.kubernetes-fleet.io_clusterstagedupdateruns.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,8 @@ spec:
235235
managed by Fleet (i.e., specified in the hub cluster manifest). This is the default
236236
option.
237237
238-
Note that this option would revert any ad-hoc changes made on the member cluster side in
239-
the managed fields; if you would like to make temporary edits on the member cluster side
238+
Note that this option would revert any ad-hoc changes made on the member cluster side in the
239+
managed fields; if you would like to make temporary edits on the member cluster side
240240
in the managed fields, switch to IfNotDrifted option. Note that changes in unmanaged
241241
fields will be left alone; if you use the FullDiff compare option, such changes will
242242
be reported as drifts.
@@ -1315,8 +1315,8 @@ spec:
13151315
managed by Fleet (i.e., specified in the hub cluster manifest). This is the default
13161316
option.
13171317
1318-
Note that this option would revert any ad-hoc changes made on the member cluster side in
1319-
the managed fields; if you would like to make temporary edits on the member cluster side
1318+
Note that this option would revert any ad-hoc changes made on the member cluster side in the
1319+
managed fields; if you would like to make temporary edits on the member cluster side
13201320
in the managed fields, switch to IfNotDrifted option. Note that changes in unmanaged
13211321
fields will be left alone; if you use the FullDiff compare option, such changes will
13221322
be reported as drifts.

config/crd/bases/placement.kubernetes-fleet.io_resourcebindings.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,8 @@ spec:
190190
managed by Fleet (i.e., specified in the hub cluster manifest). This is the default
191191
option.
192192
193-
Note that this option would revert any ad-hoc changes made on the member cluster side in
194-
the managed fields; if you would like to make temporary edits on the member cluster side
193+
Note that this option would revert any ad-hoc changes made on the member cluster side in the
194+
managed fields; if you would like to make temporary edits on the member cluster side
195195
in the managed fields, switch to IfNotDrifted option. Note that changes in unmanaged
196196
fields will be left alone; if you use the FullDiff compare option, such changes will
197197
be reported as drifts.

config/crd/bases/placement.kubernetes-fleet.io_resourceoverrides.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,16 @@ spec:
243243
Path defines the target location.
244244
Note: override will fail if the resource path does not exist.
245245
type: string
246+
x-kubernetes-validations:
247+
- message: Path cannot target typeMeta fields (kind,
248+
apiVersion)
249+
rule: '!(self == ''/kind'' || self == ''/apiVersion'')'
250+
- message: Path can only target annotations and labels
251+
under metadata
252+
rule: '!(self.startsWith(''/metadata'') && !(self.startsWith(''/metadata/annotations'')
253+
|| self.startsWith(''/metadata/labels'')))'
254+
- message: Path cannot target status fields
255+
rule: '!self.startsWith(''/status'')'
246256
value:
247257
description: |-
248258
Value defines the content to be applied on the target location.
@@ -268,6 +278,15 @@ spec:
268278
- Delete
269279
type: string
270280
type: object
281+
x-kubernetes-validations:
282+
- message: jsonPatchOverrides cannot be set when the override
283+
type is Delete.
284+
rule: '!(self.overrideType == ''Delete'' && has(self.jsonPatchOverrides)
285+
&& self.jsonPatchOverrides.size() > 0)'
286+
- message: jsonPatchOverrides must be set when the override
287+
type is JSONPatch.
288+
rule: '!(self.overrideType == ''JSONPatch'' && !has(self.jsonPatchOverrides)
289+
&& self.jsonPatchOverrides.size() == 0)'
271290
maxItems: 20
272291
minItems: 1
273292
type: array

0 commit comments

Comments
 (0)