Skip to content

Commit 0af007d

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 0af007d

5 files changed

Lines changed: 245 additions & 92 deletions

File tree

api/v1/clusterextensionrevision_types.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,9 +227,14 @@ const (
227227
type ProbeType string
228228

229229
const (
230-
ProbeTypeFieldCondition ProbeType = "ConditionEqual"
231-
ProbeTypeFieldEqual ProbeType = "FieldsEqual"
230+
ProbeTypeConditionEqual ProbeType = "ConditionEqual"
231+
ProbeTypeFieldsEqual ProbeType = "FieldsEqual"
232232
ProbeTypeFieldValue ProbeType = "FieldValue"
233+
234+
// Deprecated: use ProbeTypeConditionEqual instead.
235+
ProbeTypeFieldCondition = ProbeTypeConditionEqual
236+
// Deprecated: use ProbeTypeFieldsEqual instead.
237+
ProbeTypeFieldEqual = ProbeTypeFieldsEqual
233238
)
234239

235240
// Assertion is a discriminated union which defines the probe type and definition used as an assertion.

internal/operator-controller/applier/boxcutter.go

Lines changed: 110 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,110 @@ 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+
issuerProbe := ocv1ac.ProgressionProbe().
642+
WithSelector(ocv1ac.ObjectSelector().
643+
WithType(ocv1.SelectorTypeGroupKind).
644+
WithGroupKind(metav1.GroupKind{
645+
Group: "acme.cert-manager.io",
646+
Kind: "Issuer",
647+
})).
648+
WithAssertions(readyConditionAssertion)
649+
650+
// namespaceActiveProbe is a probe which asserts that the namespace is in "Active" phase
651+
namespaceActiveProbe := ocv1ac.ProgressionProbe().
652+
WithSelector(ocv1ac.ObjectSelector().
653+
WithType(ocv1.SelectorTypeGroupKind).
654+
WithGroupKind(metav1.GroupKind{
655+
Group: corev1.GroupName,
656+
Kind: "Namespace",
657+
})).
658+
WithAssertions(ocv1ac.Assertion().
659+
WithType(ocv1.ProbeTypeFieldValue).
660+
WithFieldValue(ocv1ac.FieldValueProbe().
661+
WithFieldPath("status.phase").
662+
WithValue(string(corev1.NamespaceActive))))
663+
664+
// pvcBoundProbe is a probe which asserts that the PVC is in "Bound" phase
665+
pvcBoundProbe := ocv1ac.ProgressionProbe().
666+
WithSelector(ocv1ac.ObjectSelector().
667+
WithType(ocv1.SelectorTypeGroupKind).
668+
WithGroupKind(metav1.GroupKind{
669+
Group: corev1.GroupName,
670+
Kind: "PersistentVolumeClaim",
671+
})).
672+
WithAssertions(ocv1ac.Assertion().
673+
WithType(ocv1.ProbeTypeFieldValue).
674+
WithFieldValue(ocv1ac.FieldValueProbe().
675+
WithFieldPath("status.phase").
676+
WithValue(string(corev1.ClaimBound))))
677+
678+
// Checks if the Type: "Available" Condition is "True".
679+
availableConditionAssertion := ocv1ac.Assertion().
680+
WithType(ocv1.ProbeTypeConditionEqual).
681+
WithConditionEqual(ocv1ac.ConditionEqualProbe().
682+
WithType(string(appsv1.DeploymentAvailable)).
683+
WithStatus(string(corev1.ConditionTrue)))
684+
685+
// Checks if status.updatedReplicas == status.replicas.
686+
// Works for StatefulSets, Deployments and ReplicaSets.
687+
replicasUpdatedAssertion := ocv1ac.Assertion().
688+
WithType(ocv1.ProbeTypeFieldsEqual).
689+
WithFieldsEqual(ocv1ac.FieldsEqualProbe().
690+
WithFieldA("status.updatedReplicas").
691+
WithFieldB("status.replicas"))
692+
693+
statefulSetProbe := ocv1ac.ProgressionProbe().WithSelector(
694+
ocv1ac.ObjectSelector().WithType(ocv1.SelectorTypeGroupKind).
695+
WithGroupKind(metav1.GroupKind{
696+
Group: appsv1.GroupName,
697+
Kind: "StatefulSet",
698+
}),
699+
).WithAssertions(replicasUpdatedAssertion, availableConditionAssertion)
700+
701+
deploymentProbe := ocv1ac.ProgressionProbe().WithSelector(
702+
ocv1ac.ObjectSelector().WithType(ocv1.SelectorTypeGroupKind).
703+
WithGroupKind(metav1.GroupKind{
704+
Group: appsv1.GroupName,
705+
Kind: "Deployment",
706+
}),
707+
).WithAssertions(replicasUpdatedAssertion, availableConditionAssertion)
708+
709+
return []*ocv1ac.ProgressionProbeApplyConfiguration{
710+
deploymentProbe, statefulSetProbe, pvcBoundProbe, namespaceActiveProbe, issuerProbe, certProbe, crdProbe,
711+
}
712+
}
713+
605714
func splitManifestDocuments(file string) []string {
606715
// Estimate: typical manifests have ~50-100 lines per document
607716
// Pre-allocate for reasonable bundle size to reduce allocations

internal/operator-controller/applier/boxcutter_test.go

Lines changed: 100 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,105 @@ 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+
ocv1ac.ProgressionProbe().WithSelector(
167+
ocv1ac.ObjectSelector().WithType(ocv1.SelectorTypeGroupKind).
168+
WithGroupKind(metav1.GroupKind{
169+
Group: appsv1.GroupName,
170+
Kind: "StatefulSet",
171+
})).
172+
WithAssertions(
173+
ocv1ac.Assertion().
174+
WithType(ocv1.ProbeTypeFieldsEqual).
175+
WithFieldsEqual(ocv1ac.FieldsEqualProbe().
176+
WithFieldA("status.updatedReplicas").
177+
WithFieldB("status.replicas")),
178+
ocv1ac.Assertion().
179+
WithType(ocv1.ProbeTypeConditionEqual).
180+
WithConditionEqual(ocv1ac.ConditionEqualProbe().
181+
WithType(string(appsv1.DeploymentAvailable)).
182+
WithStatus(string(corev1.ConditionTrue)))),
183+
ocv1ac.ProgressionProbe().
184+
WithSelector(ocv1ac.ObjectSelector().
185+
WithType(ocv1.SelectorTypeGroupKind).
186+
WithGroupKind(metav1.GroupKind{
187+
Group: corev1.GroupName,
188+
Kind: "PersistentVolumeClaim",
189+
})).
190+
WithAssertions(ocv1ac.Assertion().
191+
WithType(ocv1.ProbeTypeFieldValue).
192+
WithFieldValue(ocv1ac.FieldValueProbe().
193+
WithFieldPath("status.phase").
194+
WithValue(string(corev1.ClaimBound)))),
195+
ocv1ac.ProgressionProbe().
196+
WithSelector(ocv1ac.ObjectSelector().
197+
WithType(ocv1.SelectorTypeGroupKind).
198+
WithGroupKind(metav1.GroupKind{
199+
Group: corev1.GroupName,
200+
Kind: "Namespace",
201+
})).
202+
WithAssertions(ocv1ac.Assertion().
203+
WithType(ocv1.ProbeTypeFieldValue).
204+
WithFieldValue(ocv1ac.FieldValueProbe().
205+
WithFieldPath("status.phase").
206+
WithValue(string(corev1.NamespaceActive)))),
207+
ocv1ac.ProgressionProbe().
208+
WithSelector(ocv1ac.ObjectSelector().
209+
WithType(ocv1.SelectorTypeGroupKind).
210+
WithGroupKind(metav1.GroupKind{
211+
Group: "acme.cert-manager.io",
212+
Kind: "Issuer",
213+
})).
214+
WithAssertions(ocv1ac.Assertion().
215+
WithType(ocv1.ProbeTypeConditionEqual).
216+
WithConditionEqual(
217+
ocv1ac.ConditionEqualProbe().
218+
WithType("Ready").
219+
WithStatus("True"))),
220+
ocv1ac.ProgressionProbe().
221+
WithSelector(ocv1ac.ObjectSelector().
222+
WithType(ocv1.SelectorTypeGroupKind).
223+
WithGroupKind(metav1.GroupKind{
224+
Group: "acme.cert-manager.io",
225+
Kind: "Certificate",
226+
})).
227+
WithAssertions(ocv1ac.Assertion().
228+
WithType(ocv1.ProbeTypeConditionEqual).
229+
WithConditionEqual(
230+
ocv1ac.ConditionEqualProbe().
231+
WithType("Ready").
232+
WithStatus("True"))),
233+
ocv1ac.ProgressionProbe().
234+
WithSelector(ocv1ac.ObjectSelector().
235+
WithType(ocv1.SelectorTypeGroupKind).
236+
WithGroupKind(metav1.GroupKind{
237+
Group: "apiextensions.k8s.io",
238+
Kind: "CustomResourceDefinition",
239+
})).
240+
WithAssertions(ocv1ac.Assertion().
241+
WithType(ocv1.ProbeTypeConditionEqual).
242+
WithConditionEqual(
243+
ocv1ac.ConditionEqualProbe().
244+
WithType(string(apiextensions.Established)).
245+
WithStatus(string(corev1.ConditionTrue)))),
147246
),
148247
)
149248
assert.Equal(t, expected, rev)

internal/operator-controller/controllers/clusterextensionrevision_controller.go

Lines changed: 6 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,6 @@ import (
1010
"strings"
1111
"time"
1212

13-
appsv1 "k8s.io/api/apps/v1"
14-
corev1 "k8s.io/api/core/v1"
15-
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
1613
"k8s.io/apimachinery/pkg/api/equality"
1714
"k8s.io/apimachinery/pkg/api/meta"
1815
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -479,9 +476,7 @@ func (c *ClusterExtensionRevisionReconciler) buildBoxcutterPhases(ctx context.Co
479476

480477
opts := []boxcutter.RevisionReconcileOption{
481478
boxcutter.WithPreviousOwners(previousObjs),
482-
boxcutter.WithProbe(boxcutter.ProgressProbeType, probing.And{
483-
&namespaceActiveProbe, deploymentProbe, statefulSetProbe, crdProbe, issuerProbe, certProbe, &pvcBoundProbe, progressionProbes,
484-
}),
479+
boxcutter.WithProbe(boxcutter.ProgressProbeType, progressionProbes),
485480
}
486481

487482
phases := make([]boxcutter.Phase, 0)
@@ -535,10 +530,10 @@ func buildProgressionProbes(progressionProbes []ocv1.ProgressionProbe) (probing.
535530
for _, probe := range progressionProbe.Assertions {
536531
switch probe.Type {
537532
// Switch based on the union discriminator
538-
case ocv1.ProbeTypeFieldCondition:
533+
case ocv1.ProbeTypeConditionEqual:
539534
conditionProbe := probing.ConditionProbe(probe.ConditionEqual)
540535
assertions = append(assertions, &conditionProbe)
541-
case ocv1.ProbeTypeFieldEqual:
536+
case ocv1.ProbeTypeFieldsEqual:
542537
fieldsEqualProbe := probing.FieldsEqualProbe(probe.FieldsEqual)
543538
assertions = append(assertions, &fieldsEqualProbe)
544539
case ocv1.ProbeTypeFieldValue:
@@ -570,90 +565,13 @@ func buildProgressionProbes(progressionProbes []ocv1.ProgressionProbe) (probing.
570565
default:
571566
return nil, fmt.Errorf("unknown progressionProbe selector type: %s", progressionProbe.Selector.Type)
572567
}
573-
userProbes = append(userProbes, selectorProbe)
568+
userProbes = append(userProbes, &probing.ObservedGenerationProbe{
569+
Prober: selectorProbe,
570+
})
574571
}
575572
return userProbes, nil
576573
}
577574

578-
var (
579-
deploymentProbe = &probing.GroupKindSelector{
580-
GroupKind: schema.GroupKind{Group: appsv1.GroupName, Kind: "Deployment"},
581-
Prober: deplStatefulSetProbe,
582-
}
583-
statefulSetProbe = &probing.GroupKindSelector{
584-
GroupKind: schema.GroupKind{Group: appsv1.GroupName, Kind: "StatefulSet"},
585-
Prober: deplStatefulSetProbe,
586-
}
587-
crdProbe = &probing.GroupKindSelector{
588-
GroupKind: schema.GroupKind{Group: "apiextensions.k8s.io", Kind: "CustomResourceDefinition"},
589-
Prober: &probing.ObservedGenerationProbe{
590-
Prober: &probing.ConditionProbe{ // "Available" == "True"
591-
Type: string(apiextensions.Established),
592-
Status: string(corev1.ConditionTrue),
593-
},
594-
},
595-
}
596-
certProbe = &probing.GroupKindSelector{
597-
GroupKind: schema.GroupKind{Group: "acme.cert-manager.io", Kind: "Certificate"},
598-
Prober: &probing.ObservedGenerationProbe{
599-
Prober: readyConditionProbe,
600-
},
601-
}
602-
issuerProbe = &probing.GroupKindSelector{
603-
GroupKind: schema.GroupKind{Group: "acme.cert-manager.io", Kind: "Issuer"},
604-
Prober: &probing.ObservedGenerationProbe{
605-
Prober: readyConditionProbe,
606-
},
607-
}
608-
609-
// namespaceActiveProbe is a probe which asserts that the namespace is in "Active" phase
610-
namespaceActiveProbe = probing.GroupKindSelector{
611-
GroupKind: schema.GroupKind{Group: corev1.GroupName, Kind: "Namespace"},
612-
Prober: &applier.FieldValueProbe{
613-
FieldPath: "status.phase",
614-
Value: "Active",
615-
},
616-
}
617-
618-
// pvcBoundProbe is a probe which asserts that the PVC is in "Bound" phase
619-
pvcBoundProbe = probing.GroupKindSelector{
620-
GroupKind: schema.GroupKind{Group: corev1.GroupName, Kind: "PersistentVolumeClaim"},
621-
Prober: &applier.FieldValueProbe{
622-
FieldPath: "status.phase",
623-
Value: "Bound",
624-
},
625-
}
626-
627-
// deplStaefulSetProbe probes Deployment, StatefulSet objects.
628-
deplStatefulSetProbe = &probing.ObservedGenerationProbe{
629-
Prober: probing.And{
630-
availableConditionProbe,
631-
replicasUpdatedProbe,
632-
},
633-
}
634-
635-
// Checks if the Type: "Available" Condition is "True".
636-
availableConditionProbe = &probing.ConditionProbe{ // "Available" == "True"
637-
Type: string(appsv1.DeploymentAvailable),
638-
Status: string(corev1.ConditionTrue),
639-
}
640-
641-
// Checks if the Type: "Ready" Condition is "True"
642-
readyConditionProbe = &probing.ObservedGenerationProbe{
643-
Prober: &probing.ConditionProbe{
644-
Type: "Ready",
645-
Status: "True",
646-
},
647-
}
648-
649-
// Checks if .status.updatedReplicas == .status.replicas.
650-
// Works for StatefulSts, Deployments and ReplicaSets.
651-
replicasUpdatedProbe = &probing.FieldsEqualProbe{
652-
FieldA: ".status.updatedReplicas",
653-
FieldB: ".status.replicas",
654-
}
655-
)
656-
657575
func setRetryingConditions(cer *ocv1.ClusterExtensionRevision, message string) {
658576
markAsProgressing(cer, ocv1.ClusterExtensionRevisionReasonRetrying, message)
659577
if meta.FindStatusCondition(cer.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable) != nil {

0 commit comments

Comments
 (0)