diff --git a/api/v1/clusterextensionrevision_types.go b/api/v1/clusterextensionrevision_types.go index 208d96d9f..a81f15601 100644 --- a/api/v1/clusterextensionrevision_types.go +++ b/api/v1/clusterextensionrevision_types.go @@ -162,6 +162,15 @@ type ProgressionProbe struct { // +required // Assertions []Assertion `json:"assertions,omitempty"` + + // observedGeneration is a flag which, when true, ensures that status.observedGeneration is equal to + // .metadata.generation before running the given assertions on the selected object(s). If a probed object does + // not contain the status.observedGeneration field, the given prober is executed directly. + // + // +optional + // +kubebuilder:default:=false + // + ObservedGeneration *bool `json:"observedGeneration,omitempty"` } // ObjectSelector is a discriminated union which defines the method by which we select objects to make assertions against. @@ -227,9 +236,14 @@ const ( type ProbeType string const ( - ProbeTypeFieldCondition ProbeType = "ConditionEqual" - ProbeTypeFieldEqual ProbeType = "FieldsEqual" + ProbeTypeConditionEqual ProbeType = "ConditionEqual" + ProbeTypeFieldsEqual ProbeType = "FieldsEqual" ProbeTypeFieldValue ProbeType = "FieldValue" + + // Deprecated: use ProbeTypeConditionEqual instead. + ProbeTypeFieldCondition = ProbeTypeConditionEqual + // Deprecated: use ProbeTypeFieldsEqual instead. + ProbeTypeFieldEqual = ProbeTypeFieldsEqual ) // Assertion is a discriminated union which defines the probe type and definition used as an assertion. diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index 9c801ca77..52d9f49dd 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -677,6 +677,11 @@ func (in *ProgressionProbe) DeepCopyInto(out *ProgressionProbe) { *out = make([]Assertion, len(*in)) copy(*out, *in) } + if in.ObservedGeneration != nil { + in, out := &in.ObservedGeneration, &out.ObservedGeneration + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProgressionProbe. diff --git a/applyconfigurations/api/v1/progressionprobe.go b/applyconfigurations/api/v1/progressionprobe.go index c3c3f9f8c..16f9f0aec 100644 --- a/applyconfigurations/api/v1/progressionprobe.go +++ b/applyconfigurations/api/v1/progressionprobe.go @@ -37,6 +37,12 @@ type ProgressionProbeApplyConfiguration struct { // // Assertions []AssertionApplyConfiguration `json:"assertions,omitempty"` + // observedGeneration is a flag which, when true, ensures that status.observedGeneration is equal to + // .metadata.generation before running the given assertions on the selected object(s). If a probed object does + // not contain the status.observedGeneration field, the given prober is executed directly. + // + // + ObservedGeneration *bool `json:"observedGeneration,omitempty"` } // ProgressionProbeApplyConfiguration constructs a declarative configuration of the ProgressionProbe type for use with @@ -65,3 +71,11 @@ func (b *ProgressionProbeApplyConfiguration) WithAssertions(values ...*Assertion } return b } + +// WithObservedGeneration sets the ObservedGeneration field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ObservedGeneration field is set to the value of the last call. +func (b *ProgressionProbeApplyConfiguration) WithObservedGeneration(value bool) *ProgressionProbeApplyConfiguration { + b.ObservedGeneration = &value + return b +} diff --git a/helm/olmv1/base/operator-controller/crd/experimental/olm.operatorframework.io_clusterextensionrevisions.yaml b/helm/olmv1/base/operator-controller/crd/experimental/olm.operatorframework.io_clusterextensionrevisions.yaml index 491f8327d..dca9186f3 100644 --- a/helm/olmv1/base/operator-controller/crd/experimental/olm.operatorframework.io_clusterextensionrevisions.yaml +++ b/helm/olmv1/base/operator-controller/crd/experimental/olm.operatorframework.io_clusterextensionrevisions.yaml @@ -364,6 +364,13 @@ spec: minItems: 1 type: array x-kubernetes-list-type: atomic + observedGeneration: + default: false + description: |- + observedGeneration is a flag which, when true, ensures that status.observedGeneration is equal to + .metadata.generation before running the given assertions on the selected object(s). If a probed object does + not contain the status.observedGeneration field, the given prober is executed directly. + type: boolean selector: description: |- selector is a required field which defines the method by which we select objects to apply the below diff --git a/internal/operator-controller/applier/boxcutter.go b/internal/operator-controller/applier/boxcutter.go index cb4da7e53..6937aab0d 100644 --- a/internal/operator-controller/applier/boxcutter.go +++ b/internal/operator-controller/applier/boxcutter.go @@ -14,6 +14,9 @@ import ( "helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/storage/driver" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -208,10 +211,12 @@ func (r *SimpleRevisionGenerator) buildClusterExtensionRevision( annotations[labels.ServiceAccountNamespaceKey] = ext.Spec.Namespace phases := PhaseSort(objects) + progressionProbes := defaultProgressionProbes() spec := ocv1ac.ClusterExtensionRevisionSpec(). WithLifecycleState(ocv1.ClusterExtensionRevisionLifecycleStateActive). - WithPhases(phases...) + WithPhases(phases...). + WithProgressionProbes(progressionProbes...) if p := ext.Spec.ProgressDeadlineMinutes; p > 0 { spec.WithProgressDeadlineMinutes(p) } @@ -602,6 +607,115 @@ func latestRevisionNumber(prevRevisions []ocv1.ClusterExtensionRevision) int64 { return prevRevisions[len(prevRevisions)-1].Spec.Revision } +func defaultProgressionProbes() []*ocv1ac.ProgressionProbeApplyConfiguration { + crdProbe := ocv1ac.ProgressionProbe(). + WithSelector(ocv1ac.ObjectSelector(). + WithType(ocv1.SelectorTypeGroupKind). + WithGroupKind(metav1.GroupKind{ + Group: "apiextensions.k8s.io", + Kind: "CustomResourceDefinition", + })). + WithAssertions(ocv1ac.Assertion(). + WithType(ocv1.ProbeTypeConditionEqual). + WithConditionEqual( + ocv1ac.ConditionEqualProbe(). + WithType(string(apiextensions.Established)). + WithStatus(string(corev1.ConditionTrue)))). + WithObservedGeneration(true) + + // Checks if the Type: "Ready" Condition is "True" + readyConditionAssertion := ocv1ac.Assertion(). + WithType(ocv1.ProbeTypeConditionEqual). + WithConditionEqual( + ocv1ac.ConditionEqualProbe(). + WithType("Ready"). + WithStatus("True")) + + certProbe := ocv1ac.ProgressionProbe(). + WithSelector(ocv1ac.ObjectSelector(). + WithType(ocv1.SelectorTypeGroupKind). + WithGroupKind(metav1.GroupKind{ + Group: "acme.cert-manager.io", + Kind: "Certificate", + })). + WithAssertions(readyConditionAssertion). + WithObservedGeneration(true) + issuerProbe := ocv1ac.ProgressionProbe(). + WithSelector(ocv1ac.ObjectSelector(). + WithType(ocv1.SelectorTypeGroupKind). + WithGroupKind(metav1.GroupKind{ + Group: "acme.cert-manager.io", + Kind: "Issuer", + })). + WithAssertions(readyConditionAssertion). + WithObservedGeneration(true) + + // namespaceActiveProbe is a probe which asserts that the namespace is in "Active" phase + namespaceActiveProbe := ocv1ac.ProgressionProbe(). + WithSelector(ocv1ac.ObjectSelector(). + WithType(ocv1.SelectorTypeGroupKind). + WithGroupKind(metav1.GroupKind{ + Group: corev1.GroupName, + Kind: "Namespace", + })). + WithAssertions(ocv1ac.Assertion(). + WithType(ocv1.ProbeTypeFieldValue). + WithFieldValue(ocv1ac.FieldValueProbe(). + WithFieldPath("status.phase"). + WithValue(string(corev1.NamespaceActive)))) + + // pvcBoundProbe is a probe which asserts that the PVC is in "Bound" phase + pvcBoundProbe := ocv1ac.ProgressionProbe(). + WithSelector(ocv1ac.ObjectSelector(). + WithType(ocv1.SelectorTypeGroupKind). + WithGroupKind(metav1.GroupKind{ + Group: corev1.GroupName, + Kind: "PersistentVolumeClaim", + })). + WithAssertions(ocv1ac.Assertion(). + WithType(ocv1.ProbeTypeFieldValue). + WithFieldValue(ocv1ac.FieldValueProbe(). + WithFieldPath("status.phase"). + WithValue(string(corev1.ClaimBound)))) + + // Checks if the Type: "Available" Condition is "True". + availableConditionAssertion := ocv1ac.Assertion(). + WithType(ocv1.ProbeTypeConditionEqual). + WithConditionEqual(ocv1ac.ConditionEqualProbe(). + WithType(string(appsv1.DeploymentAvailable)). + WithStatus(string(corev1.ConditionTrue))) + + // Checks if status.updatedReplicas == status.replicas. + // Works for StatefulSets, Deployments and ReplicaSets. + replicasUpdatedAssertion := ocv1ac.Assertion(). + WithType(ocv1.ProbeTypeFieldsEqual). + WithFieldsEqual(ocv1ac.FieldsEqualProbe(). + WithFieldA("status.updatedReplicas"). + WithFieldB("status.replicas")) + + statefulSetProbe := ocv1ac.ProgressionProbe().WithSelector( + ocv1ac.ObjectSelector().WithType(ocv1.SelectorTypeGroupKind). + WithGroupKind(metav1.GroupKind{ + Group: appsv1.GroupName, + Kind: "StatefulSet", + }), + ).WithAssertions(replicasUpdatedAssertion, availableConditionAssertion). + WithObservedGeneration(true) + + deploymentProbe := ocv1ac.ProgressionProbe().WithSelector( + ocv1ac.ObjectSelector().WithType(ocv1.SelectorTypeGroupKind). + WithGroupKind(metav1.GroupKind{ + Group: appsv1.GroupName, + Kind: "Deployment", + }), + ).WithAssertions(replicasUpdatedAssertion, availableConditionAssertion). + WithObservedGeneration(true) + + return []*ocv1ac.ProgressionProbeApplyConfiguration{ + deploymentProbe, statefulSetProbe, pvcBoundProbe, namespaceActiveProbe, issuerProbe, certProbe, crdProbe, + } +} + func splitManifestDocuments(file string) []string { // Estimate: typical manifests have ~50-100 lines per document // Pre-allocate for reasonable bundle size to reduce allocations diff --git a/internal/operator-controller/applier/boxcutter_test.go b/internal/operator-controller/applier/boxcutter_test.go index 4f8461250..f99d744f2 100644 --- a/internal/operator-controller/applier/boxcutter_test.go +++ b/internal/operator-controller/applier/boxcutter_test.go @@ -18,6 +18,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" apierrors "k8s.io/apimachinery/pkg/api/errors" apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -143,7 +144,110 @@ func Test_SimpleRevisionGenerator_GenerateRevisionFromHelmRelease(t *testing.T) }, }, }), - ), + )). + WithProgressionProbes( + ocv1ac.ProgressionProbe().WithSelector( + ocv1ac.ObjectSelector().WithType(ocv1.SelectorTypeGroupKind). + WithGroupKind(metav1.GroupKind{ + Group: appsv1.GroupName, + Kind: "Deployment", + })). + WithAssertions( + ocv1ac.Assertion(). + WithType(ocv1.ProbeTypeFieldsEqual). + WithFieldsEqual(ocv1ac.FieldsEqualProbe(). + WithFieldA("status.updatedReplicas"). + WithFieldB("status.replicas")), + ocv1ac.Assertion(). + WithType(ocv1.ProbeTypeConditionEqual). + WithConditionEqual(ocv1ac.ConditionEqualProbe(). + WithType(string(appsv1.DeploymentAvailable)). + WithStatus(string(corev1.ConditionTrue)))). + WithObservedGeneration(true), + ocv1ac.ProgressionProbe().WithSelector( + ocv1ac.ObjectSelector().WithType(ocv1.SelectorTypeGroupKind). + WithGroupKind(metav1.GroupKind{ + Group: appsv1.GroupName, + Kind: "StatefulSet", + })). + WithAssertions( + ocv1ac.Assertion(). + WithType(ocv1.ProbeTypeFieldsEqual). + WithFieldsEqual(ocv1ac.FieldsEqualProbe(). + WithFieldA("status.updatedReplicas"). + WithFieldB("status.replicas")), + ocv1ac.Assertion(). + WithType(ocv1.ProbeTypeConditionEqual). + WithConditionEqual(ocv1ac.ConditionEqualProbe(). + WithType(string(appsv1.DeploymentAvailable)). + WithStatus(string(corev1.ConditionTrue)))). + WithObservedGeneration(true), + ocv1ac.ProgressionProbe(). + WithSelector(ocv1ac.ObjectSelector(). + WithType(ocv1.SelectorTypeGroupKind). + WithGroupKind(metav1.GroupKind{ + Group: corev1.GroupName, + Kind: "PersistentVolumeClaim", + })). + WithAssertions(ocv1ac.Assertion(). + WithType(ocv1.ProbeTypeFieldValue). + WithFieldValue(ocv1ac.FieldValueProbe(). + WithFieldPath("status.phase"). + WithValue(string(corev1.ClaimBound)))), + ocv1ac.ProgressionProbe(). + WithSelector(ocv1ac.ObjectSelector(). + WithType(ocv1.SelectorTypeGroupKind). + WithGroupKind(metav1.GroupKind{ + Group: corev1.GroupName, + Kind: "Namespace", + })). + WithAssertions(ocv1ac.Assertion(). + WithType(ocv1.ProbeTypeFieldValue). + WithFieldValue(ocv1ac.FieldValueProbe(). + WithFieldPath("status.phase"). + WithValue(string(corev1.NamespaceActive)))), + ocv1ac.ProgressionProbe(). + WithSelector(ocv1ac.ObjectSelector(). + WithType(ocv1.SelectorTypeGroupKind). + WithGroupKind(metav1.GroupKind{ + Group: "acme.cert-manager.io", + Kind: "Issuer", + })). + WithAssertions(ocv1ac.Assertion(). + WithType(ocv1.ProbeTypeConditionEqual). + WithConditionEqual( + ocv1ac.ConditionEqualProbe(). + WithType("Ready"). + WithStatus("True"))). + WithObservedGeneration(true), + ocv1ac.ProgressionProbe(). + WithSelector(ocv1ac.ObjectSelector(). + WithType(ocv1.SelectorTypeGroupKind). + WithGroupKind(metav1.GroupKind{ + Group: "acme.cert-manager.io", + Kind: "Certificate", + })). + WithAssertions(ocv1ac.Assertion(). + WithType(ocv1.ProbeTypeConditionEqual). + WithConditionEqual( + ocv1ac.ConditionEqualProbe(). + WithType("Ready"). + WithStatus("True"))). + WithObservedGeneration(true), + ocv1ac.ProgressionProbe(). + WithSelector(ocv1ac.ObjectSelector(). + WithType(ocv1.SelectorTypeGroupKind). + WithGroupKind(metav1.GroupKind{ + Group: "apiextensions.k8s.io", + Kind: "CustomResourceDefinition", + })). + WithAssertions(ocv1ac.Assertion(). + WithType(ocv1.ProbeTypeConditionEqual). + WithConditionEqual( + ocv1ac.ConditionEqualProbe(). + WithType(string(apiextensions.Established)). + WithStatus(string(corev1.ConditionTrue)))). + WithObservedGeneration(true), ), ) assert.Equal(t, expected, rev) diff --git a/internal/operator-controller/controllers/clusterextensionrevision_controller.go b/internal/operator-controller/controllers/clusterextensionrevision_controller.go index 2e9d2fce6..8924134b9 100644 --- a/internal/operator-controller/controllers/clusterextensionrevision_controller.go +++ b/internal/operator-controller/controllers/clusterextensionrevision_controller.go @@ -10,9 +10,6 @@ import ( "strings" "time" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -479,9 +476,7 @@ func (c *ClusterExtensionRevisionReconciler) buildBoxcutterPhases(ctx context.Co opts := []boxcutter.RevisionReconcileOption{ boxcutter.WithPreviousOwners(previousObjs), - boxcutter.WithProbe(boxcutter.ProgressProbeType, probing.And{ - &namespaceActiveProbe, deploymentProbe, statefulSetProbe, crdProbe, issuerProbe, certProbe, &pvcBoundProbe, progressionProbes, - }), + boxcutter.WithProbe(boxcutter.ProgressProbeType, progressionProbes), } phases := make([]boxcutter.Phase, 0) @@ -535,10 +530,10 @@ func buildProgressionProbes(progressionProbes []ocv1.ProgressionProbe) (probing. for _, probe := range progressionProbe.Assertions { switch probe.Type { // Switch based on the union discriminator - case ocv1.ProbeTypeFieldCondition: + case ocv1.ProbeTypeConditionEqual: conditionProbe := probing.ConditionProbe(probe.ConditionEqual) assertions = append(assertions, &conditionProbe) - case ocv1.ProbeTypeFieldEqual: + case ocv1.ProbeTypeFieldsEqual: fieldsEqualProbe := probing.FieldsEqualProbe(probe.FieldsEqual) assertions = append(assertions, &fieldsEqualProbe) case ocv1.ProbeTypeFieldValue: @@ -548,6 +543,13 @@ func buildProgressionProbes(progressionProbes []ocv1.ProgressionProbe) (probing. return nil, fmt.Errorf("unknown progressionProbe assertion probe type: %s", probe.Type) } } + var prober probing.Prober + if progressionProbe.ObservedGeneration != nil && *progressionProbe.ObservedGeneration { + // Wrap the and-ed probes within an ObservedGeneration probe + prober = &probing.ObservedGenerationProbe{Prober: assertions} + } else { + prober = &assertions + } // Create the selector probe based on user-requested type and provide the assertions var selectorProbe probing.Prober @@ -556,7 +558,7 @@ func buildProgressionProbes(progressionProbes []ocv1.ProgressionProbe) (probing. case ocv1.SelectorTypeGroupKind: selectorProbe = &probing.GroupKindSelector{ GroupKind: schema.GroupKind(progressionProbe.Selector.GroupKind), - Prober: assertions, + Prober: prober, } case ocv1.SelectorTypeLabel: selector, err := metav1.LabelSelectorAsSelector(&progressionProbe.Selector.Label) @@ -565,7 +567,7 @@ func buildProgressionProbes(progressionProbes []ocv1.ProgressionProbe) (probing. } selectorProbe = &probing.LabelSelector{ Selector: selector, - Prober: assertions, + Prober: prober, } default: return nil, fmt.Errorf("unknown progressionProbe selector type: %s", progressionProbe.Selector.Type) @@ -575,85 +577,6 @@ func buildProgressionProbes(progressionProbes []ocv1.ProgressionProbe) (probing. return userProbes, nil } -var ( - deploymentProbe = &probing.GroupKindSelector{ - GroupKind: schema.GroupKind{Group: appsv1.GroupName, Kind: "Deployment"}, - Prober: deplStatefulSetProbe, - } - statefulSetProbe = &probing.GroupKindSelector{ - GroupKind: schema.GroupKind{Group: appsv1.GroupName, Kind: "StatefulSet"}, - Prober: deplStatefulSetProbe, - } - crdProbe = &probing.GroupKindSelector{ - GroupKind: schema.GroupKind{Group: "apiextensions.k8s.io", Kind: "CustomResourceDefinition"}, - Prober: &probing.ObservedGenerationProbe{ - Prober: &probing.ConditionProbe{ // "Available" == "True" - Type: string(apiextensions.Established), - Status: string(corev1.ConditionTrue), - }, - }, - } - certProbe = &probing.GroupKindSelector{ - GroupKind: schema.GroupKind{Group: "acme.cert-manager.io", Kind: "Certificate"}, - Prober: &probing.ObservedGenerationProbe{ - Prober: readyConditionProbe, - }, - } - issuerProbe = &probing.GroupKindSelector{ - GroupKind: schema.GroupKind{Group: "acme.cert-manager.io", Kind: "Issuer"}, - Prober: &probing.ObservedGenerationProbe{ - Prober: readyConditionProbe, - }, - } - - // namespaceActiveProbe is a probe which asserts that the namespace is in "Active" phase - namespaceActiveProbe = probing.GroupKindSelector{ - GroupKind: schema.GroupKind{Group: corev1.GroupName, Kind: "Namespace"}, - Prober: &applier.FieldValueProbe{ - FieldPath: "status.phase", - Value: "Active", - }, - } - - // pvcBoundProbe is a probe which asserts that the PVC is in "Bound" phase - pvcBoundProbe = probing.GroupKindSelector{ - GroupKind: schema.GroupKind{Group: corev1.GroupName, Kind: "PersistentVolumeClaim"}, - Prober: &applier.FieldValueProbe{ - FieldPath: "status.phase", - Value: "Bound", - }, - } - - // deplStaefulSetProbe probes Deployment, StatefulSet objects. - deplStatefulSetProbe = &probing.ObservedGenerationProbe{ - Prober: probing.And{ - availableConditionProbe, - replicasUpdatedProbe, - }, - } - - // Checks if the Type: "Available" Condition is "True". - availableConditionProbe = &probing.ConditionProbe{ // "Available" == "True" - Type: string(appsv1.DeploymentAvailable), - Status: string(corev1.ConditionTrue), - } - - // Checks if the Type: "Ready" Condition is "True" - readyConditionProbe = &probing.ObservedGenerationProbe{ - Prober: &probing.ConditionProbe{ - Type: "Ready", - Status: "True", - }, - } - - // Checks if .status.updatedReplicas == .status.replicas. - // Works for StatefulSts, Deployments and ReplicaSets. - replicasUpdatedProbe = &probing.FieldsEqualProbe{ - FieldA: ".status.updatedReplicas", - FieldB: ".status.replicas", - } -) - func setRetryingConditions(cer *ocv1.ClusterExtensionRevision, message string) { markAsProgressing(cer, ocv1.ClusterExtensionRevisionReasonRetrying, message) if meta.FindStatusCondition(cer.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable) != nil { diff --git a/manifests/experimental-e2e.yaml b/manifests/experimental-e2e.yaml index 55a4e157c..0ed83c4ab 100644 --- a/manifests/experimental-e2e.yaml +++ b/manifests/experimental-e2e.yaml @@ -976,6 +976,13 @@ spec: minItems: 1 type: array x-kubernetes-list-type: atomic + observedGeneration: + default: false + description: |- + observedGeneration is a flag which, when true, ensures that status.observedGeneration is equal to + .metadata.generation before running the given assertions on the selected object(s). If a probed object does + not contain the status.observedGeneration field, the given prober is executed directly. + type: boolean selector: description: |- selector is a required field which defines the method by which we select objects to apply the below diff --git a/manifests/experimental.yaml b/manifests/experimental.yaml index 8604a7db8..0eb664be7 100644 --- a/manifests/experimental.yaml +++ b/manifests/experimental.yaml @@ -937,6 +937,13 @@ spec: minItems: 1 type: array x-kubernetes-list-type: atomic + observedGeneration: + default: false + description: |- + observedGeneration is a flag which, when true, ensures that status.observedGeneration is equal to + .metadata.generation before running the given assertions on the selected object(s). If a probed object does + not contain the status.observedGeneration field, the given prober is executed directly. + type: boolean selector: description: |- selector is a required field which defines the method by which we select objects to apply the below diff --git a/test/e2e/features/revision.feature b/test/e2e/features/revision.feature index 755762a8a..f2c2c7fa2 100644 --- a/test/e2e/features/revision.feature +++ b/test/e2e/features/revision.feature @@ -20,6 +20,17 @@ Feature: Install ClusterExtensionRevision spec: lifecycleState: Active collisionProtection: Prevent + progressionProbes: + - selector: + type: GroupKind + groupKind: + group: "" + kind: PersistentVolumeClaim + assertions: + - type: FieldValue + fieldValue: + fieldPath: "status.phase" + value: "Bound" phases: - name: pvc objects: @@ -72,6 +83,17 @@ Feature: Install ClusterExtensionRevision spec: lifecycleState: Active collisionProtection: Prevent + progressionProbes: + - selector: + type: GroupKind + groupKind: + group: "" + kind: PersistentVolumeClaim + assertions: + - type: FieldValue + fieldValue: + fieldPath: "status.phase" + value: "Bound" phases: - name: pvc objects: