Skip to content

Commit 1756aaf

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. All probes will now check that status.observedGeneration==metadata.generation before executing probes, or execute them normally if objects do not contain those fields. Signed-off-by: Daniel Franz <dfranz@redhat.com>
1 parent a307a6d commit 1756aaf

5 files changed

Lines changed: 247 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: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@ import (
1212
"slices"
1313
"strings"
1414

15+
"github.com/cert-manager/cert-manager/pkg/apis/certmanager"
1516
"helm.sh/helm/v3/pkg/release"
1617
"helm.sh/helm/v3/pkg/storage/driver"
18+
appsv1 "k8s.io/api/apps/v1"
19+
corev1 "k8s.io/api/core/v1"
20+
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
1721
apierrors "k8s.io/apimachinery/pkg/api/errors"
1822
"k8s.io/apimachinery/pkg/api/meta"
1923
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -208,10 +212,12 @@ func (r *SimpleRevisionGenerator) buildClusterExtensionRevision(
208212
annotations[labels.ServiceAccountNamespaceKey] = ext.Spec.Namespace
209213

210214
phases := PhaseSort(objects)
215+
progressionProbes := defaultProgressionProbes()
211216

212217
spec := ocv1ac.ClusterExtensionRevisionSpec().
213218
WithLifecycleState(ocv1.ClusterExtensionRevisionLifecycleStateActive).
214-
WithPhases(phases...)
219+
WithPhases(phases...).
220+
WithProgressionProbes(progressionProbes...)
215221
if p := ext.Spec.ProgressDeadlineMinutes; p > 0 {
216222
spec.WithProgressDeadlineMinutes(p)
217223
}
@@ -602,6 +608,110 @@ func latestRevisionNumber(prevRevisions []ocv1.ClusterExtensionRevision) int64 {
602608
return prevRevisions[len(prevRevisions)-1].Spec.Revision
603609
}
604610

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

internal/operator-controller/applier/boxcutter_test.go

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"testing"
1111
"testing/fstest"
1212

13+
"github.com/cert-manager/cert-manager/pkg/apis/certmanager"
1314
"github.com/stretchr/testify/assert"
1415
"github.com/stretchr/testify/mock"
1516
"github.com/stretchr/testify/require"
@@ -18,6 +19,7 @@ import (
1819
appsv1 "k8s.io/api/apps/v1"
1920
corev1 "k8s.io/api/core/v1"
2021
rbacv1 "k8s.io/api/rbac/v1"
22+
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
2123
apierrors "k8s.io/apimachinery/pkg/api/errors"
2224
apimeta "k8s.io/apimachinery/pkg/api/meta"
2325
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -143,7 +145,105 @@ func Test_SimpleRevisionGenerator_GenerateRevisionFromHelmRelease(t *testing.T)
143145
},
144146
},
145147
}),
146-
),
148+
)).
149+
WithProgressionProbes(
150+
ocv1ac.ProgressionProbe().WithSelector(
151+
ocv1ac.ObjectSelector().WithType(ocv1.SelectorTypeGroupKind).
152+
WithGroupKind(metav1.GroupKind{
153+
Group: appsv1.GroupName,
154+
Kind: "Deployment",
155+
})).
156+
WithAssertions(
157+
ocv1ac.Assertion().
158+
WithType(ocv1.ProbeTypeFieldsEqual).
159+
WithFieldsEqual(ocv1ac.FieldsEqualProbe().
160+
WithFieldA("status.updatedReplicas").
161+
WithFieldB("status.replicas")),
162+
ocv1ac.Assertion().
163+
WithType(ocv1.ProbeTypeConditionEqual).
164+
WithConditionEqual(ocv1ac.ConditionEqualProbe().
165+
WithType(string(appsv1.DeploymentAvailable)).
166+
WithStatus(string(corev1.ConditionTrue)))),
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+
ocv1ac.ProgressionProbe().
185+
WithSelector(ocv1ac.ObjectSelector().
186+
WithType(ocv1.SelectorTypeGroupKind).
187+
WithGroupKind(metav1.GroupKind{
188+
Group: corev1.GroupName,
189+
Kind: "PersistentVolumeClaim",
190+
})).
191+
WithAssertions(ocv1ac.Assertion().
192+
WithType(ocv1.ProbeTypeFieldValue).
193+
WithFieldValue(ocv1ac.FieldValueProbe().
194+
WithFieldPath("status.phase").
195+
WithValue(string(corev1.ClaimBound)))),
196+
ocv1ac.ProgressionProbe().
197+
WithSelector(ocv1ac.ObjectSelector().
198+
WithType(ocv1.SelectorTypeGroupKind).
199+
WithGroupKind(metav1.GroupKind{
200+
Group: corev1.GroupName,
201+
Kind: "Namespace",
202+
})).
203+
WithAssertions(ocv1ac.Assertion().
204+
WithType(ocv1.ProbeTypeFieldValue).
205+
WithFieldValue(ocv1ac.FieldValueProbe().
206+
WithFieldPath("status.phase").
207+
WithValue(string(corev1.NamespaceActive)))),
208+
ocv1ac.ProgressionProbe().
209+
WithSelector(ocv1ac.ObjectSelector().
210+
WithType(ocv1.SelectorTypeGroupKind).
211+
WithGroupKind(metav1.GroupKind{
212+
Group: certmanager.GroupName,
213+
Kind: "Issuer",
214+
})).
215+
WithAssertions(ocv1ac.Assertion().
216+
WithType(ocv1.ProbeTypeConditionEqual).
217+
WithConditionEqual(
218+
ocv1ac.ConditionEqualProbe().
219+
WithType("Ready").
220+
WithStatus("True"))),
221+
ocv1ac.ProgressionProbe().
222+
WithSelector(ocv1ac.ObjectSelector().
223+
WithType(ocv1.SelectorTypeGroupKind).
224+
WithGroupKind(metav1.GroupKind{
225+
Group: certmanager.GroupName,
226+
Kind: "Certificate",
227+
})).
228+
WithAssertions(ocv1ac.Assertion().
229+
WithType(ocv1.ProbeTypeConditionEqual).
230+
WithConditionEqual(
231+
ocv1ac.ConditionEqualProbe().
232+
WithType("Ready").
233+
WithStatus("True"))),
234+
ocv1ac.ProgressionProbe().
235+
WithSelector(ocv1ac.ObjectSelector().
236+
WithType(ocv1.SelectorTypeGroupKind).
237+
WithGroupKind(metav1.GroupKind{
238+
Group: "apiextensions.k8s.io",
239+
Kind: "CustomResourceDefinition",
240+
})).
241+
WithAssertions(ocv1ac.Assertion().
242+
WithType(ocv1.ProbeTypeConditionEqual).
243+
WithConditionEqual(
244+
ocv1ac.ConditionEqualProbe().
245+
WithType(string(apiextensions.Established)).
246+
WithStatus(string(corev1.ConditionTrue)))),
147247
),
148248
)
149249
assert.Equal(t, expected, rev)

0 commit comments

Comments
 (0)