diff --git a/internal/operator-controller/controllers/clusterextensionrevision_controller.go b/internal/operator-controller/controllers/clusterextensionrevision_controller.go index aa757b7f9a..8e08ebfb2f 100644 --- a/internal/operator-controller/controllers/clusterextensionrevision_controller.go +++ b/internal/operator-controller/controllers/clusterextensionrevision_controller.go @@ -41,6 +41,7 @@ import ( const ( clusterExtensionRevisionTeardownFinalizer = "olm.operatorframework.io/teardown" + defaultRequeueAfter = 10 * time.Second ) // ClusterExtensionRevisionReconciler actions individual snapshots of ClusterExtensions, @@ -186,14 +187,14 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, cer if verr := rres.GetValidationError(); verr != nil { l.Error(fmt.Errorf("%w", verr), "preflight validation failed, retrying after 10s") setRetryingConditions(cer, fmt.Sprintf("revision validation error: %s", verr)) - return ctrl.Result{RequeueAfter: 10 * time.Second}, nil + return ctrl.Result{RequeueAfter: defaultRequeueAfter}, nil } for i, pres := range rres.GetPhases() { if verr := pres.GetValidationError(); verr != nil { l.Error(fmt.Errorf("%w", verr), "phase preflight validation failed, retrying after 10s", "phase", i) setRetryingConditions(cer, fmt.Sprintf("phase %d validation error: %s", i, verr)) - return ctrl.Result{RequeueAfter: 10 * time.Second}, nil + return ctrl.Result{RequeueAfter: defaultRequeueAfter}, nil } var collidingObjs []string @@ -206,7 +207,7 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, cer if len(collidingObjs) > 0 { l.Error(fmt.Errorf("object collision detected"), "object collision, retrying after 10s", "phase", i, "collisions", collidingObjs) setRetryingConditions(cer, fmt.Sprintf("revision object collisions in phase %d\n%s", i, strings.Join(collidingObjs, "\n\n"))) - return ctrl.Result{RequeueAfter: 10 * time.Second}, nil + return ctrl.Result{RequeueAfter: defaultRequeueAfter}, nil } } @@ -280,6 +281,10 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, cer if meta.FindStatusCondition(cer.Status.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing) == nil { markAsProgressing(cer, ocv1.ReasonRollingOut, fmt.Sprintf("Revision %s is rolling out.", revVersion)) } + // Requeue to periodically re-evaluate probes. While the TrackingCache watches + // managed objects and triggers reconciliation on status changes, this requeue + // acts as a safety net for cases where watch events are delayed or missed. + return ctrl.Result{RequeueAfter: defaultRequeueAfter}, nil } return ctrl.Result{}, nil diff --git a/internal/operator-controller/controllers/clusterextensionrevision_controller_test.go b/internal/operator-controller/controllers/clusterextensionrevision_controller_test.go index 36224232a3..fecd11cc80 100644 --- a/internal/operator-controller/controllers/clusterextensionrevision_controller_test.go +++ b/internal/operator-controller/controllers/clusterextensionrevision_controller_test.go @@ -46,12 +46,14 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_RevisionReconciliation(t existingObjs func() []client.Object revisionResult machinery.RevisionResult revisionReconcileErr error + expectedResult ctrl.Result validate func(*testing.T, client.Client) }{ { name: "sets teardown finalizer", reconcilingRevisionName: clusterExtensionRevisionName, revisionResult: mockRevisionResult{}, + expectedResult: ctrl.Result{RequeueAfter: 10 * time.Second}, existingObjs: func() []client.Object { ext := newTestClusterExtension() rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme) @@ -120,6 +122,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_RevisionReconciliation(t { name: "set Available:False:RollingOut status condition during rollout when no probe failures are detected", reconcilingRevisionName: clusterExtensionRevisionName, + expectedResult: ctrl.Result{RequeueAfter: 10 * time.Second}, revisionResult: mockRevisionResult{}, existingObjs: func() []client.Object { ext := newTestClusterExtension() @@ -143,6 +146,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_RevisionReconciliation(t { name: "set Available:False:ProbeFailure condition when probe failures are detected and revision is in transition", reconcilingRevisionName: clusterExtensionRevisionName, + expectedResult: ctrl.Result{RequeueAfter: 10 * time.Second}, revisionResult: mockRevisionResult{ inTransition: true, isComplete: false, @@ -234,6 +238,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_RevisionReconciliation(t { name: "set Available:False:ProbeFailure condition when probe failures are detected and revision is not in transition", reconcilingRevisionName: clusterExtensionRevisionName, + expectedResult: ctrl.Result{RequeueAfter: 10 * time.Second}, revisionResult: mockRevisionResult{ inTransition: false, isComplete: false, @@ -346,7 +351,8 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_RevisionReconciliation(t }, }, { - name: "set Progressing:True:RollingOut condition while revision is transitioning", + name: "set Progressing:True:RollingOut condition while revision is transitioning", + expectedResult: ctrl.Result{RequeueAfter: 10 * time.Second}, revisionResult: mockRevisionResult{ inTransition: true, }, @@ -501,8 +507,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_RevisionReconciliation(t }, }) - // reconcile cluster extension revision - require.Equal(t, ctrl.Result{}, result) + require.Equal(t, tc.expectedResult, result) if tc.revisionReconcileErr == nil { require.NoError(t, err) } else { @@ -652,6 +657,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ArchivalAndDeletion(t *te { name: "teardown finalizer is removed", revisionResult: mockRevisionResult{}, + expectedResult: ctrl.Result{RequeueAfter: 10 * time.Second}, existingObjs: func() []client.Object { ext := newTestClusterExtension() rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme) @@ -1021,6 +1027,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ProgressDeadline(t *testi }) return []client.Object{rev1, ext} }, + reconcileResult: ctrl.Result{RequeueAfter: 10 * time.Second}, revisionResult: &mockRevisionResult{ inTransition: true, },