Skip to content

Commit 5b023a2

Browse files
committed
ProgressionProbes
Provides an API which allows custom probe definitions to determine readiness of the CER phases. Objects can be selected for in one of two ways: by GroupKind, or by Label (matchLabels and matchExpressions). They can then be tested via any of: ConditionEqual, FieldsEqual, and FieldValue. ConditionEqual checks that the object has a condition matching the type and status provided. FieldsEqual uses two provided field paths and checks for equality. FieldValue uses a provided field path and checks that the value is equal to the provided expected value. Signed-off-by: Daniel Franz <dfranz@redhat.com>
1 parent 6f23a79 commit 5b023a2

20 files changed

Lines changed: 2091 additions & 35 deletions

api/v1/clusterextensionrevision_types.go

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,25 @@ type ClusterExtensionRevisionSpec struct {
106106
// <opcon:experimental>
107107
ProgressDeadlineMinutes int32 `json:"progressDeadlineMinutes,omitempty"`
108108

109+
// progressionProbes is an optional field which provides the ability to define custom readiness probes
110+
// for objects defined within spec.phases. As documented in that field, most kubernetes-native objects
111+
// within the phases already have some kind of readiness check built-in, but this field allows for checks
112+
// which are tailored to the objects being rolled out - particularly custom resources.
113+
//
114+
// Probes defined within the progressionProbes list will apply to every phase in the revision. However, the probes will only
115+
// execute against phase objects which are a match for the provided selector type. For instance, a probe using a GroupKind selector
116+
// for ConfigMaps will automatically be considered to have passed for any non-ConfigMap object, but will halt any phase containing
117+
// a ConfigMap if that particular object does not pass the probe check.
118+
//
119+
// The maximum number of probes is 20.
120+
//
121+
// +kubebuilder:validation:MinItems=1
122+
// +kubebuilder:validation:MaxItems=20
123+
// +listType=atomic
124+
// +optional
125+
// <opcon:experimental>
126+
ProgressionProbes []ProgressionProbe `json:"progressionProbes,omitempty"`
127+
109128
// collisionProtection specifies the default collision protection strategy for all objects
110129
// in this revision. Individual phases or objects can override this value.
111130
//
@@ -120,6 +139,205 @@ type ClusterExtensionRevisionSpec struct {
120139
CollisionProtection CollisionProtection `json:"collisionProtection,omitempty"`
121140
}
122141

142+
// ProgressionProbe provides a custom probe definition, consisting of an object selection method and assertions.
143+
type ProgressionProbe struct {
144+
// selector is a required field which defines the method by which we select objects to apply the below
145+
// assertions to. Any object which matches the defined selector will have all the associated assertions
146+
// applied against it.
147+
//
148+
// If no objects within a phase are selected by the provided selector, then all assertions defined here
149+
// are considered to have succeeded.
150+
//
151+
// +required
152+
// <opcon:experimental>
153+
Selector ObjectSelector `json:"selector,omitzero"`
154+
155+
// assertions is a required list of checks which will run against the objects selected by the selector. If
156+
// one or more assertions fail then the phase within which the object lives will be not be considered
157+
// 'Ready', blocking rollout of all subsequent phases.
158+
//
159+
// +kubebuilder:validation:MinItems=1
160+
// +kubebuilder:validation:MaxItems=20
161+
// +listType=atomic
162+
// +required
163+
// <opcon:experimental>
164+
Assertions []Assertion `json:"assertions,omitempty"`
165+
}
166+
167+
// ObjectSelector is a discriminated union which defines the method by which we select objects to make assertions against.
168+
// +union
169+
// +kubebuilder:validation:XValidation:rule="self.type == 'GroupKind' ?has(self.groupKind) : !has(self.groupKind)",message="groupKind is required when type is GroupKind, and forbidden otherwise"
170+
// +kubebuilder:validation:XValidation:rule="self.type == 'Label' ?has(self.label) : !has(self.label)",message="label is required when type is Label, and forbidden otherwise"
171+
type ObjectSelector struct {
172+
// type is a required field which specifies the type of selector to use.
173+
//
174+
// The allowed selector types are "GroupKind" and "Label".
175+
//
176+
// When set to "GroupKind", all objects which match the specified group and kind will be selected.
177+
// When set to "Label", all objects which match the specified labels and/or expressions will be selected.
178+
//
179+
// +unionDiscriminator
180+
// +kubebuilder:validation:Enum=GroupKind;Label
181+
// +required
182+
// <opcon:experimental>
183+
Type SelectorType `json:"type,omitempty"`
184+
185+
// groupKind specifies the group and kind of objects to select.
186+
//
187+
// Required when type is "GroupKind".
188+
//
189+
// Uses the Kubernetes format specified here:
190+
// https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#GroupKind
191+
//
192+
// +optional
193+
// +unionMember
194+
// <opcon:experimental>
195+
GroupKind metav1.GroupKind `json:"groupKind,omitempty,omitzero"`
196+
197+
// label is the label selector definition.
198+
//
199+
// Required when type is "Label".
200+
//
201+
// A probe using a Label selector will be executed against every object matching the labels or expressions; you must use care
202+
// when using this type of selector. For example, if multiple Kind objects are selected via labels then the probe is
203+
// likely to fail because the values of different Kind objects rarely share the same schema.
204+
//
205+
// The LabelSelector field uses the following Kubernetes format:
206+
// https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#LabelSelector
207+
// Requires exactly one of matchLabels or matchExpressions.
208+
//
209+
// +optional
210+
// +unionMember
211+
// +kubebuilder:validation:XValidation:rule="(has(self.matchExpressions) && !has(self.matchLabels)) || (!has(self.matchExpressions) && has(self.matchLabels))",message="exactly one of matchLabels or matchExpressions must be set"
212+
// <opcon:experimental>
213+
Label metav1.LabelSelector `json:"label,omitempty,omitzero"`
214+
}
215+
216+
// SelectorType defines the type of selector used for progressionProbes.
217+
// +enum
218+
type SelectorType string
219+
220+
const (
221+
SelectorTypeGroupKind SelectorType = "GroupKind"
222+
SelectorTypeLabel SelectorType = "Label"
223+
)
224+
225+
// ProbeType defines the type of probe used as an assertion.
226+
// +enum
227+
type ProbeType string
228+
229+
const (
230+
ProbeTypeFieldCondition ProbeType = "ConditionEqual"
231+
ProbeTypeFieldEqual ProbeType = "FieldsEqual"
232+
ProbeTypeFieldValue ProbeType = "FieldValue"
233+
)
234+
235+
// Assertion is a discriminated union which defines the probe type and definition used as an assertion.
236+
// +union
237+
// +kubebuilder:validation:XValidation:rule="self.type == 'ConditionEqual' ?has(self.conditionEqual) : !has(self.conditionEqual)",message="conditionEqual is required when type is ConditionEqual, and forbidden otherwise"
238+
// +kubebuilder:validation:XValidation:rule="self.type == 'FieldsEqual' ?has(self.fieldsEqual) : !has(self.fieldsEqual)",message="fieldsEqual is required when type is FieldsEqual, and forbidden otherwise"
239+
// +kubebuilder:validation:XValidation:rule="self.type == 'FieldValue' ?has(self.fieldValue) : !has(self.fieldValue)",message="fieldValue is required when type is FieldValue, and forbidden otherwise"
240+
type Assertion struct {
241+
// type is a required field which specifies the type of probe to use.
242+
//
243+
// The allowed probe types are "ConditionEqual", "FieldsEqual", and "FieldValue".
244+
//
245+
// When set to "ConditionEqual", the probe checks objects that have reached a condition of specified type and status.
246+
// When set to "FieldsEqual", the probe checks that the values found at two provided field paths are matching.
247+
// When set to "FieldValue", the probe checks that the value found at the provided field path matches what was specified.
248+
//
249+
// +unionDiscriminator
250+
// +kubebuilder:validation:Enum=ConditionEqual;FieldsEqual;FieldValue
251+
// +required
252+
// <opcon:experimental>
253+
Type ProbeType `json:"type,omitempty"`
254+
255+
// conditionEqual contains the expected condition type and status.
256+
//
257+
// +unionMember
258+
// +optional
259+
// <opcon:experimental>
260+
ConditionEqual ConditionEqualProbe `json:"conditionEqual,omitzero"`
261+
262+
// fieldsEqual contains the two field paths whose values are expected to match.
263+
//
264+
// +unionMember
265+
// +optional
266+
// <opcon:experimental>
267+
FieldsEqual FieldsEqualProbe `json:"fieldsEqual,omitzero"`
268+
269+
// fieldValue contains the expected field path and value found within.
270+
//
271+
// +unionMember
272+
// +optional
273+
// <opcon:experimental>
274+
FieldValue FieldValueProbe `json:"fieldValue,omitzero"`
275+
}
276+
277+
// ConditionEqualProbe defines the condition type and status required for the probe to succeed.
278+
type ConditionEqualProbe struct {
279+
// type sets the expected condition type, i.e. "Ready".
280+
//
281+
// +kubebuilder:validation:MinLength=1
282+
// +kubebuilder:validation:MaxLength=200
283+
// +required
284+
// <opcon:experimental>
285+
Type string `json:"type,omitempty"`
286+
287+
// status sets the expected condition status.
288+
//
289+
// Allowed values are "True" and "False".
290+
//
291+
// +kubebuilder:validation:Enum=True;False
292+
// +required
293+
// <opcon:experimental>
294+
Status string `json:"status,omitempty"`
295+
}
296+
297+
// FieldsEqualProbe defines the paths of the two fields required to match for the probe to succeed.
298+
type FieldsEqualProbe struct {
299+
// fieldA sets the field path for the first field, i.e. "spec.replicas". The probe will fail
300+
// if the path does not exist.
301+
//
302+
// +kubebuilder:validation:MinLength=1
303+
// +kubebuilder:validation:MaxLength=200
304+
// +kubebuilder:validation:XValidation:rule="self.matches('^[a-zA-Z0-9]+(?:\\\\.[a-zA-Z0-9]+)*$')",message="must contain a valid field path. valid fields contain upper or lower-case alphanumeric characters separated by the \".\" character."
305+
// +required
306+
// <opcon:experimental>
307+
FieldA string `json:"fieldA,omitempty"`
308+
309+
// fieldB sets the field path for the second field, i.e. "status.readyReplicas". The probe will fail
310+
// if the path does not exist.
311+
//
312+
// +kubebuilder:validation:MinLength=1
313+
// +kubebuilder:validation:MaxLength=200
314+
// +kubebuilder:validation:XValidation:rule="self.matches('^[a-zA-Z0-9]+(?:\\\\.[a-zA-Z0-9]+)*$')",message="must contain a valid field path. valid fields contain upper or lower-case alphanumeric characters separated by the \".\" character."
315+
// +required
316+
// <opcon:experimental>
317+
FieldB string `json:"fieldB,omitempty"`
318+
}
319+
320+
// FieldValueProbe defines the path and value expected within for the probe to succeed.
321+
type FieldValueProbe struct {
322+
// fieldPath sets the field path for the field to check, i.e. "status.phase". The probe will fail
323+
// if the path does not exist.
324+
//
325+
// +kubebuilder:validation:MinLength=1
326+
// +kubebuilder:validation:MaxLength=200
327+
// +kubebuilder:validation:XValidation:rule="self.matches('^[a-zA-Z0-9]+(?:\\\\.[a-zA-Z0-9]+)*$')",message="must contain a valid field path. valid fields contain upper or lower-case alphanumeric characters separated by the \".\" character."
328+
// +required
329+
// <opcon:experimental>
330+
FieldPath string `json:"fieldPath,omitempty"`
331+
332+
// value sets the expected value found at fieldPath, i.e. "Bound".
333+
//
334+
// +kubebuilder:validation:MinLength=1
335+
// +kubebuilder:validation:MaxLength=200
336+
// +required
337+
// <opcon:experimental>
338+
Value string `json:"value,omitempty"`
339+
}
340+
123341
// ClusterExtensionRevisionLifecycleState specifies the lifecycle state of the ClusterExtensionRevision.
124342
type ClusterExtensionRevisionLifecycleState string
125343

api/v1/zz_generated.deepcopy.go

Lines changed: 108 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)