Skip to content

Commit 57464c9

Browse files
authored
interface: add v1beta1 API for ClusterResourceOverride & ResourceOverride (#145)
1 parent e935c34 commit 57464c9

File tree

49 files changed

+4565
-2439
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+4565
-2439
lines changed

apis/placement/v1alpha1/common.go

Lines changed: 0 additions & 29 deletions
This file was deleted.

apis/placement/v1alpha1/overridesnapshot_types.go

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,6 @@ package v1alpha1
1818

1919
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2020

21-
const (
22-
23-
// OverrideIndexLabel is the label that indicate the policy snapshot index of a cluster policy.
24-
OverrideIndexLabel = fleetPrefix + "override-index"
25-
26-
// OverrideSnapshotNameFmt is clusterResourceOverrideSnapshot name format: {CROName}-{OverrideSnapshotIndex}.
27-
OverrideSnapshotNameFmt = "%s-%d"
28-
29-
// OverrideTrackingLabel is the label that points to the cluster resource override that creates a resource snapshot.
30-
OverrideTrackingLabel = fleetPrefix + "parent-resource-override"
31-
32-
// OverrideFinalizer is a finalizer added by the override controllers to all override, to make sure
33-
// that the override controller can react to override deletions if necessary.
34-
OverrideFinalizer = fleetPrefix + "override-cleanup"
35-
)
36-
3721
// +genclient
3822
// +genclient:nonNamespaced
3923
// +kubebuilder:object:root=true

apis/placement/v1beta1/commons.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,32 @@ const (
148148
ApprovalTaskNameFmt = "%s-%s"
149149
)
150150

151+
var (
152+
// ClusterResourceOverrideKind is the kind of the ClusterResourceOverride.
153+
ClusterResourceOverrideKind = "ClusterResourceOverride"
154+
155+
// ClusterResourceOverrideSnapshotKind is the kind of the ClusterResourceOverrideSnapshot.
156+
ClusterResourceOverrideSnapshotKind = "ClusterResourceOverrideSnapshot"
157+
158+
// ResourceOverrideKind is the kind of the ResourceOverride.
159+
ResourceOverrideKind = "ResourceOverride"
160+
161+
// ResourceOverrideSnapshotKind is the kind of the ResourceOverrideSnapshot.
162+
ResourceOverrideSnapshotKind = "ResourceOverrideSnapshot"
163+
164+
// OverrideClusterNameVariable is the reserved variable in the override value that will be replaced by the actual cluster name.
165+
OverrideClusterNameVariable = "${MEMBER-CLUSTER-NAME}"
166+
167+
// OverrideClusterLabelKeyVariablePrefix is a reserved variable in the override expression.
168+
// We use this variable to find the associated the key following the prefix.
169+
// The key name ends with a "}" character (but not include it).
170+
// The key name must be a valid Kubernetes label name and case-sensitive.
171+
// The content of the string containing this variable will be replaced by the actual label value on the member cluster.
172+
// For example, if the string is "${MEMBER-CLUSTER-LABEL-KEY-kube-fleet.io/region}" then the key name is "kube-fleet.io/region".
173+
// If there is a label "kube-fleet.io/region": "us-west-1" on the member cluster, this string will be replaced by "us-west-1".
174+
OverrideClusterLabelKeyVariablePrefix = "${MEMBER-CLUSTER-LABEL-KEY-"
175+
)
176+
151177
// NamespacedName comprises a resource name, with a mandatory namespace.
152178
type NamespacedName struct {
153179
// Name is the name of the namespaced scope resource.
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
/*
2+
Copyright 2025 The KubeFleet Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1beta1
18+
19+
import (
20+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
21+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22+
)
23+
24+
// +genclient
25+
// +genclient:nonNamespaced
26+
// +kubebuilder:object:root=true
27+
// +kubebuilder:storageversion
28+
// +kubebuilder:resource:scope="Cluster",categories={fleet,fleet-placement}
29+
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
30+
31+
// ClusterResourceOverride defines a group of override policies about how to override the selected cluster scope resources
32+
// to target clusters.
33+
type ClusterResourceOverride struct {
34+
metav1.TypeMeta `json:",inline"`
35+
metav1.ObjectMeta `json:"metadata,omitempty"`
36+
37+
// The desired state of ClusterResourceOverrideSpec.
38+
// +required
39+
Spec ClusterResourceOverrideSpec `json:"spec"`
40+
}
41+
42+
// ClusterResourceOverrideSpec defines the desired state of the Override.
43+
// The ClusterResourceOverride create or update will fail when the resource has been selected by the existing ClusterResourceOverride.
44+
// If the resource is selected by both ClusterResourceOverride and ResourceOverride, ResourceOverride will win when resolving
45+
// conflicts.
46+
type ClusterResourceOverrideSpec struct {
47+
// Placement defines whether the override is applied to a specific placement or not.
48+
// If set, the override will trigger the placement rollout immediately when the rollout strategy type is RollingUpdate.
49+
// Otherwise, it will be applied to the next rollout.
50+
// The recommended way is to set the placement so that the override can be rolled out immediately.
51+
// +optional
52+
Placement *PlacementRef `json:"placement,omitempty"`
53+
54+
// ClusterResourceSelectors is an array of selectors used to select cluster scoped resources. The selectors are `ORed`.
55+
// If a namespace is selected, ALL the resources under the namespace are selected automatically.
56+
// LabelSelector is not supported.
57+
// You can have 1-20 selectors.
58+
// We only support Name selector for now.
59+
// +kubebuilder:validation:Required
60+
// +kubebuilder:validation:MinItems=1
61+
// +kubebuilder:validation:MaxItems=20
62+
// +required
63+
ClusterResourceSelectors []ClusterResourceSelector `json:"clusterResourceSelectors"`
64+
65+
// Policy defines how to override the selected resources on the target clusters.
66+
// +required
67+
Policy *OverridePolicy `json:"policy"`
68+
}
69+
70+
// PlacementRef is the reference to a placement.
71+
// For now, we only support ClusterResourcePlacement.
72+
type PlacementRef struct {
73+
// Name is the reference to the name of placement.
74+
// +required
75+
Name string `json:"name"`
76+
}
77+
78+
// OverridePolicy defines how to override the selected resources on the target clusters.
79+
// More is to be added.
80+
type OverridePolicy struct {
81+
// OverrideRules defines an array of override rules to be applied on the selected resources.
82+
// The order of the rules determines the override order.
83+
// When there are two rules selecting the same fields on the target cluster, the last one will win.
84+
// You can have 1-20 rules.
85+
// +kubebuilder:validation:Required
86+
// +kubebuilder:validation:MinItems=1
87+
// +kubebuilder:validation:MaxItems=20
88+
// +required
89+
OverrideRules []OverrideRule `json:"overrideRules"`
90+
}
91+
92+
// OverrideRule defines how to override the selected resources on the target clusters.
93+
type OverrideRule struct {
94+
// ClusterSelectors selects the target clusters.
95+
// The resources will be overridden before applying to the matching clusters.
96+
// An empty clusterSelector selects ALL the member clusters.
97+
// A nil clusterSelector selects NO member clusters.
98+
// For now, only labelSelector is supported.
99+
// +optional
100+
ClusterSelector *ClusterSelector `json:"clusterSelector,omitempty"`
101+
102+
// OverrideType defines the type of the override rules.
103+
// +kubebuilder:validation:Enum=JSONPatch;Delete
104+
// +kubebuilder:default=JSONPatch
105+
// +optional
106+
OverrideType OverrideType `json:"overrideType,omitempty"`
107+
108+
// JSONPatchOverrides defines a list of JSON patch override rules.
109+
// This field is only allowed when OverrideType is JSONPatch.
110+
// +kubebuilder:validation:MinItems=1
111+
// +kubebuilder:validation:MaxItems=20
112+
// +optional
113+
JSONPatchOverrides []JSONPatchOverride `json:"jsonPatchOverrides,omitempty"`
114+
}
115+
116+
// OverrideType defines the type of Override
117+
type OverrideType string
118+
119+
const (
120+
// JSONPatchOverrideType applies a JSON patch on the selected resources following [RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902).
121+
JSONPatchOverrideType OverrideType = "JSONPatch"
122+
123+
// DeleteOverrideType deletes the selected resources on the target clusters.
124+
DeleteOverrideType OverrideType = "Delete"
125+
)
126+
127+
// +genclient
128+
// +genclient:Namespaced
129+
// +kubebuilder:object:root=true
130+
// +kubebuilder:storageversion
131+
// +kubebuilder:resource:scope="Namespaced",categories={fleet,fleet-placement}
132+
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
133+
134+
// ResourceOverride defines a group of override policies about how to override the selected namespaced scope resources
135+
// to target clusters.
136+
type ResourceOverride struct {
137+
metav1.TypeMeta `json:",inline"`
138+
metav1.ObjectMeta `json:"metadata,omitempty"`
139+
140+
// The desired state of ResourceOverrideSpec.
141+
// +required
142+
Spec ResourceOverrideSpec `json:"spec"`
143+
}
144+
145+
// ResourceOverrideSpec defines the desired state of the Override.
146+
// The ResourceOverride create or update will fail when the resource has been selected by the existing ResourceOverride.
147+
// If the resource is selected by both ClusterResourceOverride and ResourceOverride, ResourceOverride will win when resolving
148+
// conflicts.
149+
type ResourceOverrideSpec struct {
150+
// Placement defines whether the override is applied to a specific placement or not.
151+
// If set, the override will trigger the placement rollout immediately when the rollout strategy type is RollingUpdate.
152+
// Otherwise, it will be applied to the next rollout.
153+
// The recommended way is to set the placement so that the override can be rolled out immediately.
154+
// +optional
155+
Placement *PlacementRef `json:"placement,omitempty"`
156+
157+
// ResourceSelectors is an array of selectors used to select namespace scoped resources. The selectors are `ORed`.
158+
// You can have 1-20 selectors.
159+
// +kubebuilder:validation:Required
160+
// +kubebuilder:validation:MinItems=1
161+
// +kubebuilder:validation:MaxItems=20
162+
// +required
163+
ResourceSelectors []ResourceSelector `json:"resourceSelectors"`
164+
165+
// Policy defines how to override the selected resources on the target clusters.
166+
// +required
167+
Policy *OverridePolicy `json:"policy"`
168+
}
169+
170+
// ResourceSelector is used to select namespace scoped resources as the target resources to be placed.
171+
// All the fields are `ANDed`. In other words, a resource must match all the fields to be selected.
172+
// The resource namespace will inherit from the parent object scope.
173+
type ResourceSelector struct {
174+
// Group name of the namespace-scoped resource.
175+
// Use an empty string to select resources under the core API group (e.g., services).
176+
// +required
177+
Group string `json:"group"`
178+
179+
// Version of the namespace-scoped resource.
180+
// +required
181+
Version string `json:"version"`
182+
183+
// Kind of the namespace-scoped resource.
184+
// +required
185+
Kind string `json:"kind"`
186+
187+
// Name of the namespace-scoped resource.
188+
// +required
189+
Name string `json:"name"`
190+
}
191+
192+
// JSONPatchOverride applies a JSON patch on the selected resources following [RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902).
193+
type JSONPatchOverride struct {
194+
// Operator defines the operation on the target field.
195+
// +kubebuilder:validation:Enum=add;remove;replace
196+
// +required
197+
Operator JSONPatchOverrideOperator `json:"op"`
198+
// Path defines the target location.
199+
// Note: override will fail if the resource path does not exist.
200+
// +required
201+
Path string `json:"path"`
202+
// Value defines the content to be applied on the target location.
203+
// Value should be empty when operator is `remove`.
204+
// We have reserved a few variables in this field that will be replaced by the actual values.
205+
// Those variables all start with `$` and are case sensitive.
206+
// Here is the list of currently supported variables:
207+
// `${MEMBER-CLUSTER-NAME}`: this will be replaced by the name of the memberCluster CR that represents this cluster.
208+
// +optional
209+
Value apiextensionsv1.JSON `json:"value,omitempty"`
210+
}
211+
212+
// JSONPatchOverrideOperator defines the supported JSON patch operator.
213+
type JSONPatchOverrideOperator string
214+
215+
const (
216+
// JSONPatchOverrideOpAdd adds the value to the target location.
217+
// An example target JSON document:
218+
//
219+
// { "foo": [ "bar", "baz" ] }
220+
//
221+
// A JSON Patch override:
222+
//
223+
// [
224+
// { "op": "add", "path": "/foo/1", "value": "qux" }
225+
// ]
226+
//
227+
// The resulting JSON document:
228+
//
229+
// { "foo": [ "bar", "qux", "baz" ] }
230+
JSONPatchOverrideOpAdd JSONPatchOverrideOperator = "add"
231+
// JSONPatchOverrideOpRemove removes the value from the target location.
232+
// An example target JSON document:
233+
//
234+
// {
235+
// "baz": "qux",
236+
// "foo": "bar"
237+
// }
238+
// A JSON Patch override:
239+
//
240+
// [
241+
// { "op": "remove", "path": "/baz" }
242+
// ]
243+
//
244+
// The resulting JSON document:
245+
//
246+
// { "foo": "bar" }
247+
JSONPatchOverrideOpRemove JSONPatchOverrideOperator = "remove"
248+
// JSONPatchOverrideOpReplace replaces the value at the target location with a new value.
249+
// An example target JSON document:
250+
//
251+
// {
252+
// "baz": "qux",
253+
// "foo": "bar"
254+
// }
255+
//
256+
// A JSON Patch override:
257+
//
258+
// [
259+
// { "op": "replace", "path": "/baz", "value": "boo" }
260+
// ]
261+
//
262+
// The resulting JSON document:
263+
//
264+
// {
265+
// "baz": "boo",
266+
// "foo": "bar"
267+
// }
268+
JSONPatchOverrideOpReplace JSONPatchOverrideOperator = "replace"
269+
)
270+
271+
// ClusterResourceOverrideList contains a list of ClusterResourceOverride.
272+
// +kubebuilder:resource:scope="Cluster"
273+
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
274+
type ClusterResourceOverrideList struct {
275+
metav1.TypeMeta `json:",inline"`
276+
metav1.ListMeta `json:"metadata,omitempty"`
277+
Items []ClusterResourceOverride `json:"items"`
278+
}
279+
280+
// ResourceOverrideList contains a list of ResourceOverride.
281+
// +kubebuilder:resource:scope="Namespaced"
282+
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
283+
type ResourceOverrideList struct {
284+
metav1.TypeMeta `json:",inline"`
285+
metav1.ListMeta `json:"metadata,omitempty"`
286+
Items []ResourceOverride `json:"items"`
287+
}
288+
289+
func init() {
290+
SchemeBuilder.Register(
291+
&ClusterResourceOverride{}, &ClusterResourceOverrideList{},
292+
&ResourceOverride{}, &ResourceOverrideList{},
293+
)
294+
}

0 commit comments

Comments
 (0)