Skip to content

Commit 8eb532b

Browse files
committed
Default Probes Refactor
Migrates the default progression probes out of the CER controller into the boxcutter applier in order to use the ProgressionProbes API to transparently stamp out the checks. Also adds flag to the API to allow checking status.observedGeneration==metadata.generation before executing probes. Signed-off-by: Daniel Franz <dfranz@redhat.com>
1 parent a307a6d commit 8eb532b

10 files changed

Lines changed: 303 additions & 93 deletions

File tree

api/v1/clusterextensionrevision_types.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,15 @@ type ProgressionProbe struct {
162162
// +required
163163
// <opcon:experimental>
164164
Assertions []Assertion `json:"assertions,omitempty"`
165+
166+
// observedGeneration is a flag which, when true, ensures that status.observedGeneration is equal to
167+
// .metadata.generation before running the given assertions on the selected object(s). If a probed object does
168+
// not contain the status.observedGeneration field, the given prober is executed directly.
169+
//
170+
// +optional
171+
// +kubebuilder:default:=false
172+
// <opcon:experimental>
173+
ObservedGeneration *bool `json:"observedGeneration,omitempty"`
165174
}
166175

167176
// ObjectSelector is a discriminated union which defines the method by which we select objects to make assertions against.
@@ -227,8 +236,8 @@ const (
227236
type ProbeType string
228237

229238
const (
230-
ProbeTypeFieldCondition ProbeType = "ConditionEqual"
231-
ProbeTypeFieldEqual ProbeType = "FieldsEqual"
239+
ProbeTypeConditionEqual ProbeType = "ConditionEqual"
240+
ProbeTypeFieldsEqual ProbeType = "FieldsEqual"
232241
ProbeTypeFieldValue ProbeType = "FieldValue"
233242
)
234243

api/v1/zz_generated.deepcopy.go

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

applyconfigurations/api/v1/progressionprobe.go

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

helm/olmv1/base/operator-controller/crd/experimental/olm.operatorframework.io_clusterextensionrevisions.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,13 @@ spec:
364364
minItems: 1
365365
type: array
366366
x-kubernetes-list-type: atomic
367+
observedGeneration:
368+
default: false
369+
description: |-
370+
observedGeneration is a flag which, when true, ensures that status.observedGeneration is equal to
371+
.metadata.generation before running the given assertions on the selected object(s). If a probed object does
372+
not contain the status.observedGeneration field, the given prober is executed directly.
373+
type: boolean
367374
selector:
368375
description: |-
369376
selector is a required field which defines the method by which we select objects to apply the below

internal/operator-controller/applier/boxcutter.go

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ import (
1414

1515
"helm.sh/helm/v3/pkg/release"
1616
"helm.sh/helm/v3/pkg/storage/driver"
17+
appsv1 "k8s.io/api/apps/v1"
18+
corev1 "k8s.io/api/core/v1"
19+
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
1720
apierrors "k8s.io/apimachinery/pkg/api/errors"
1821
"k8s.io/apimachinery/pkg/api/meta"
1922
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -208,10 +211,12 @@ func (r *SimpleRevisionGenerator) buildClusterExtensionRevision(
208211
annotations[labels.ServiceAccountNamespaceKey] = ext.Spec.Namespace
209212

210213
phases := PhaseSort(objects)
214+
progressionProbes := defaultProgressionProbes()
211215

212216
spec := ocv1ac.ClusterExtensionRevisionSpec().
213217
WithLifecycleState(ocv1.ClusterExtensionRevisionLifecycleStateActive).
214-
WithPhases(phases...)
218+
WithPhases(phases...).
219+
WithProgressionProbes(progressionProbes...)
215220
if p := ext.Spec.ProgressDeadlineMinutes; p > 0 {
216221
spec.WithProgressDeadlineMinutes(p)
217222
}
@@ -602,6 +607,114 @@ func latestRevisionNumber(prevRevisions []ocv1.ClusterExtensionRevision) int64 {
602607
return prevRevisions[len(prevRevisions)-1].Spec.Revision
603608
}
604609

610+
func defaultProgressionProbes() []*ocv1ac.ProgressionProbeApplyConfiguration {
611+
crdProbe := ocv1ac.ProgressionProbe().
612+
WithSelector(ocv1ac.ObjectSelector().
613+
WithType(ocv1.SelectorTypeGroupKind).
614+
WithGroupKind(metav1.GroupKind{
615+
Group: "apiextensions.k8s.io",
616+
Kind: "CustomResourceDefinition",
617+
})).
618+
WithAssertions(ocv1ac.Assertion().
619+
WithType(ocv1.ProbeTypeConditionEqual).
620+
WithConditionEqual(
621+
ocv1ac.ConditionEqualProbe().
622+
WithType(string(apiextensions.Established)).
623+
WithStatus(string(corev1.ConditionTrue))))
624+
625+
// Checks if the Type: "Ready" Condition is "True"
626+
readyConditionAssertion := ocv1ac.Assertion().
627+
WithType(ocv1.ProbeTypeConditionEqual).
628+
WithConditionEqual(
629+
ocv1ac.ConditionEqualProbe().
630+
WithType("Ready").
631+
WithStatus("True"))
632+
633+
certProbe := ocv1ac.ProgressionProbe().
634+
WithSelector(ocv1ac.ObjectSelector().
635+
WithType(ocv1.SelectorTypeGroupKind).
636+
WithGroupKind(metav1.GroupKind{
637+
Group: "acme.cert-manager.io",
638+
Kind: "Certificate",
639+
})).
640+
WithAssertions(readyConditionAssertion).
641+
WithObservedGeneration(true)
642+
issuerProbe := ocv1ac.ProgressionProbe().
643+
WithSelector(ocv1ac.ObjectSelector().
644+
WithType(ocv1.SelectorTypeGroupKind).
645+
WithGroupKind(metav1.GroupKind{
646+
Group: "acme.cert-manager.io",
647+
Kind: "Issuer",
648+
})).
649+
WithAssertions(readyConditionAssertion).
650+
WithObservedGeneration(true)
651+
652+
// namespaceActiveProbe is a probe which asserts that the namespace is in "Active" phase
653+
namespaceActiveProbe := ocv1ac.ProgressionProbe().
654+
WithSelector(ocv1ac.ObjectSelector().
655+
WithType(ocv1.SelectorTypeGroupKind).
656+
WithGroupKind(metav1.GroupKind{
657+
Group: corev1.GroupName,
658+
Kind: "Namespace",
659+
})).
660+
WithAssertions(ocv1ac.Assertion().
661+
WithType(ocv1.ProbeTypeFieldValue).
662+
WithFieldValue(ocv1ac.FieldValueProbe().
663+
WithFieldPath("status.phase").
664+
WithValue(string(corev1.NamespaceActive))))
665+
666+
// pvcBoundProbe is a probe which asserts that the PVC is in "Bound" phase
667+
pvcBoundProbe := ocv1ac.ProgressionProbe().
668+
WithSelector(ocv1ac.ObjectSelector().
669+
WithType(ocv1.SelectorTypeGroupKind).
670+
WithGroupKind(metav1.GroupKind{
671+
Group: corev1.GroupName,
672+
Kind: "PersistentVolumeClaim",
673+
})).
674+
WithAssertions(ocv1ac.Assertion().
675+
WithType(ocv1.ProbeTypeFieldValue).
676+
WithFieldValue(ocv1ac.FieldValueProbe().
677+
WithFieldPath("status.phase").
678+
WithValue(string(corev1.ClaimBound))))
679+
680+
// Checks if the Type: "Available" Condition is "True".
681+
availableConditionAssertion := ocv1ac.Assertion().
682+
WithType(ocv1.ProbeTypeConditionEqual).
683+
WithConditionEqual(ocv1ac.ConditionEqualProbe().
684+
WithType(string(appsv1.DeploymentAvailable)).
685+
WithStatus(string(corev1.ConditionTrue)))
686+
687+
// Checks if .status.updatedReplicas == .status.replicas.
688+
// Works for StatefulSts, Deployments and ReplicaSets.
689+
replicasUpdatedAssertion := ocv1ac.Assertion().
690+
WithType(ocv1.ProbeTypeFieldsEqual).
691+
WithFieldsEqual(ocv1ac.FieldsEqualProbe().
692+
WithFieldA("status.updatedReplicas").
693+
WithFieldB("status.replicas"))
694+
695+
statefulSetProbe := ocv1ac.ProgressionProbe().WithSelector(
696+
ocv1ac.ObjectSelector().WithType(ocv1.SelectorTypeGroupKind).
697+
WithGroupKind(metav1.GroupKind{
698+
Group: appsv1.GroupName,
699+
Kind: "StatefulSet",
700+
}),
701+
).WithAssertions(replicasUpdatedAssertion, availableConditionAssertion).
702+
WithObservedGeneration(true)
703+
704+
deploymentProbe := ocv1ac.ProgressionProbe().WithSelector(
705+
ocv1ac.ObjectSelector().WithType(ocv1.SelectorTypeGroupKind).
706+
WithGroupKind(metav1.GroupKind{
707+
Group: appsv1.GroupName,
708+
Kind: "Deployment",
709+
}),
710+
).WithAssertions(replicasUpdatedAssertion, availableConditionAssertion).
711+
WithObservedGeneration(true)
712+
713+
return []*ocv1ac.ProgressionProbeApplyConfiguration{
714+
deploymentProbe, statefulSetProbe, pvcBoundProbe, namespaceActiveProbe, issuerProbe, certProbe, crdProbe,
715+
}
716+
}
717+
605718
func splitManifestDocuments(file string) []string {
606719
// Estimate: typical manifests have ~50-100 lines per document
607720
// Pre-allocate for reasonable bundle size to reduce allocations

internal/operator-controller/applier/boxcutter_test.go

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
appsv1 "k8s.io/api/apps/v1"
1919
corev1 "k8s.io/api/core/v1"
2020
rbacv1 "k8s.io/api/rbac/v1"
21+
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
2122
apierrors "k8s.io/apimachinery/pkg/api/errors"
2223
apimeta "k8s.io/apimachinery/pkg/api/meta"
2324
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -143,7 +144,109 @@ func Test_SimpleRevisionGenerator_GenerateRevisionFromHelmRelease(t *testing.T)
143144
},
144145
},
145146
}),
146-
),
147+
)).
148+
WithProgressionProbes(
149+
ocv1ac.ProgressionProbe().WithSelector(
150+
ocv1ac.ObjectSelector().WithType(ocv1.SelectorTypeGroupKind).
151+
WithGroupKind(metav1.GroupKind{
152+
Group: appsv1.GroupName,
153+
Kind: "Deployment",
154+
})).
155+
WithAssertions(
156+
ocv1ac.Assertion().
157+
WithType(ocv1.ProbeTypeFieldsEqual).
158+
WithFieldsEqual(ocv1ac.FieldsEqualProbe().
159+
WithFieldA("status.updatedReplicas").
160+
WithFieldB("status.replicas")),
161+
ocv1ac.Assertion().
162+
WithType(ocv1.ProbeTypeConditionEqual).
163+
WithConditionEqual(ocv1ac.ConditionEqualProbe().
164+
WithType(string(appsv1.DeploymentAvailable)).
165+
WithStatus(string(corev1.ConditionTrue)))).
166+
WithObservedGeneration(true),
167+
ocv1ac.ProgressionProbe().WithSelector(
168+
ocv1ac.ObjectSelector().WithType(ocv1.SelectorTypeGroupKind).
169+
WithGroupKind(metav1.GroupKind{
170+
Group: appsv1.GroupName,
171+
Kind: "StatefulSet",
172+
})).
173+
WithAssertions(
174+
ocv1ac.Assertion().
175+
WithType(ocv1.ProbeTypeFieldsEqual).
176+
WithFieldsEqual(ocv1ac.FieldsEqualProbe().
177+
WithFieldA("status.updatedReplicas").
178+
WithFieldB("status.replicas")),
179+
ocv1ac.Assertion().
180+
WithType(ocv1.ProbeTypeConditionEqual).
181+
WithConditionEqual(ocv1ac.ConditionEqualProbe().
182+
WithType(string(appsv1.DeploymentAvailable)).
183+
WithStatus(string(corev1.ConditionTrue)))).
184+
WithObservedGeneration(true),
185+
ocv1ac.ProgressionProbe().
186+
WithSelector(ocv1ac.ObjectSelector().
187+
WithType(ocv1.SelectorTypeGroupKind).
188+
WithGroupKind(metav1.GroupKind{
189+
Group: corev1.GroupName,
190+
Kind: "PersistentVolumeClaim",
191+
})).
192+
WithAssertions(ocv1ac.Assertion().
193+
WithType(ocv1.ProbeTypeFieldValue).
194+
WithFieldValue(ocv1ac.FieldValueProbe().
195+
WithFieldPath("status.phase").
196+
WithValue(string(corev1.ClaimBound)))),
197+
ocv1ac.ProgressionProbe().
198+
WithSelector(ocv1ac.ObjectSelector().
199+
WithType(ocv1.SelectorTypeGroupKind).
200+
WithGroupKind(metav1.GroupKind{
201+
Group: corev1.GroupName,
202+
Kind: "Namespace",
203+
})).
204+
WithAssertions(ocv1ac.Assertion().
205+
WithType(ocv1.ProbeTypeFieldValue).
206+
WithFieldValue(ocv1ac.FieldValueProbe().
207+
WithFieldPath("status.phase").
208+
WithValue(string(corev1.NamespaceActive)))),
209+
ocv1ac.ProgressionProbe().
210+
WithSelector(ocv1ac.ObjectSelector().
211+
WithType(ocv1.SelectorTypeGroupKind).
212+
WithGroupKind(metav1.GroupKind{
213+
Group: "acme.cert-manager.io",
214+
Kind: "Issuer",
215+
})).
216+
WithAssertions(ocv1ac.Assertion().
217+
WithType(ocv1.ProbeTypeConditionEqual).
218+
WithConditionEqual(
219+
ocv1ac.ConditionEqualProbe().
220+
WithType("Ready").
221+
WithStatus("True"))).
222+
WithObservedGeneration(true),
223+
ocv1ac.ProgressionProbe().
224+
WithSelector(ocv1ac.ObjectSelector().
225+
WithType(ocv1.SelectorTypeGroupKind).
226+
WithGroupKind(metav1.GroupKind{
227+
Group: "acme.cert-manager.io",
228+
Kind: "Certificate",
229+
})).
230+
WithAssertions(ocv1ac.Assertion().
231+
WithType(ocv1.ProbeTypeConditionEqual).
232+
WithConditionEqual(
233+
ocv1ac.ConditionEqualProbe().
234+
WithType("Ready").
235+
WithStatus("True"))).
236+
WithObservedGeneration(true),
237+
ocv1ac.ProgressionProbe().
238+
WithSelector(ocv1ac.ObjectSelector().
239+
WithType(ocv1.SelectorTypeGroupKind).
240+
WithGroupKind(metav1.GroupKind{
241+
Group: "apiextensions.k8s.io",
242+
Kind: "CustomResourceDefinition",
243+
})).
244+
WithAssertions(ocv1ac.Assertion().
245+
WithType(ocv1.ProbeTypeConditionEqual).
246+
WithConditionEqual(
247+
ocv1ac.ConditionEqualProbe().
248+
WithType(string(apiextensions.Established)).
249+
WithStatus(string(corev1.ConditionTrue)))),
147250
),
148251
)
149252
assert.Equal(t, expected, rev)

0 commit comments

Comments
 (0)