diff --git a/controllers/bucket_controller.go b/controllers/bucket_controller.go index c7105664..54270940 100644 --- a/controllers/bucket_controller.go +++ b/controllers/bucket_controller.go @@ -139,20 +139,19 @@ func (r *BucketReconciler) Reconcile(ctx context.Context, req ctrl.Request) (res return ctrl.Result{}, nil } - // Initialize the patch helper + // Initialize the patch helper with the current version of the object. patchHelper, err := patch.NewHelper(obj, r.Client) if err != nil { return ctrl.Result{}, err } + // recResult stores the abstracted reconcile result. var recResult sreconcile.Result // Always attempt to patch the object and status after each reconciliation - // NOTE: This deferred block only modifies the named return error. The - // result from the reconciliation remains the same. Any requeue attributes - // set in the result will continue to be effective. + // NOTE: The final runtime result and error are set in this block. defer func() { - retErr = r.summarizeAndPatch(ctx, obj, patchHelper, recResult, retErr) + result, retErr = r.summarizeAndPatch(ctx, obj, patchHelper, recResult, retErr) // Always record readiness and duration metrics r.Metrics.RecordReadiness(ctx, obj) @@ -163,13 +162,13 @@ func (r *BucketReconciler) Reconcile(ctx context.Context, req ctrl.Request) (res if !controllerutil.ContainsFinalizer(obj, sourcev1.SourceFinalizer) { controllerutil.AddFinalizer(obj, sourcev1.SourceFinalizer) recResult = sreconcile.ResultRequeue - return ctrl.Result{Requeue: true}, nil + return } // Examine if the object is under deletion if !obj.ObjectMeta.DeletionTimestamp.IsZero() { - res, err := r.reconcileDelete(ctx, obj) - return sreconcile.BuildRuntimeResult(ctx, r.EventRecorder, obj, res, err) + recResult, retErr = r.reconcileDelete(ctx, obj) + return } // Reconcile actual object @@ -178,13 +177,21 @@ func (r *BucketReconciler) Reconcile(ctx context.Context, req ctrl.Request) (res r.reconcileSource, r.reconcileArtifact, } - recResult, err = r.reconcile(ctx, obj, reconcilers) - return sreconcile.BuildRuntimeResult(ctx, r.EventRecorder, obj, recResult, err) + recResult, retErr = r.reconcile(ctx, obj, reconcilers) + return } // summarizeAndPatch analyzes the object conditions to create a summary of the -// status conditions and patches the object with the calculated summary. -func (r *BucketReconciler) summarizeAndPatch(ctx context.Context, obj *sourcev1.Bucket, patchHelper *patch.Helper, res sreconcile.Result, recErr error) error { +// status conditions, computes runtime results and patches the object in the K8s +// API server. +func (r *BucketReconciler) summarizeAndPatch( + ctx context.Context, + obj *sourcev1.Bucket, + patchHelper *patch.Helper, + res sreconcile.Result, + recErr error) (ctrl.Result, error) { + sreconcile.RecordContextualError(ctx, r.EventRecorder, obj, recErr) + // Record the value of the reconciliation request if any. if v, ok := meta.ReconcileAnnotationValue(obj.GetAnnotations()); ok { obj.Status.SetLastHandledReconcileRequest(v) @@ -192,7 +199,8 @@ func (r *BucketReconciler) summarizeAndPatch(ctx context.Context, obj *sourcev1. // Compute the reconcile results, obtain patch options and reconcile error. var patchOpts []patch.Option - patchOpts, recErr = sreconcile.ComputeReconcileResult(obj, res, recErr, bucketOwnedConditions) + var result ctrl.Result + patchOpts, result, recErr = sreconcile.ComputeReconcileResult(obj, obj.GetRequeueAfter(), res, recErr, bucketOwnedConditions) // Summarize the Ready condition based on abnormalities that may have been observed. conditions.SetSummary(obj, @@ -214,7 +222,7 @@ func (r *BucketReconciler) summarizeAndPatch(ctx context.Context, obj *sourcev1. recErr = kerrors.NewAggregate([]error{recErr, err}) } - return recErr + return result, recErr } // reconcile steps iterates through the actual reconciliation tasks for objec, diff --git a/controllers/gitrepository_controller.go b/controllers/gitrepository_controller.go index 3dc97c79..34bb2611 100644 --- a/controllers/gitrepository_controller.go +++ b/controllers/gitrepository_controller.go @@ -145,20 +145,19 @@ func (r *GitRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reques return ctrl.Result{}, nil } - // Initialize the patch helper + // Initialize the patch helper with the current version of the object. patchHelper, err := patch.NewHelper(obj, r.Client) if err != nil { return ctrl.Result{}, err } + // recResult stores the abstracted reconcile result. var recResult sreconcile.Result // Always attempt to patch the object and status after each reconciliation - // NOTE: This deferred block only modifies the named return error. The - // result from the reconciliation remains the same. Any requeue attributes - // set in the result will continue to be effective. + // NOTE: The final runtime result and error are set in this block. defer func() { - retErr = r.summarizeAndPatch(ctx, obj, patchHelper, recResult, retErr) + result, retErr = r.summarizeAndPatch(ctx, obj, patchHelper, recResult, retErr) // Always record readiness and duration metrics r.Metrics.RecordReadiness(ctx, obj) @@ -170,13 +169,13 @@ func (r *GitRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reques if !controllerutil.ContainsFinalizer(obj, sourcev1.SourceFinalizer) { controllerutil.AddFinalizer(obj, sourcev1.SourceFinalizer) recResult = sreconcile.ResultRequeue - return ctrl.Result{Requeue: true}, nil + return } // Examine if the object is under deletion if !obj.ObjectMeta.DeletionTimestamp.IsZero() { - res, err := r.reconcileDelete(ctx, obj) - return sreconcile.BuildRuntimeResult(ctx, r.EventRecorder, obj, res, err) + recResult, retErr = r.reconcileDelete(ctx, obj) + return } // Reconcile actual object @@ -186,13 +185,21 @@ func (r *GitRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reques r.reconcileInclude, r.reconcileArtifact, } - recResult, err = r.reconcile(ctx, obj, reconcilers) - return sreconcile.BuildRuntimeResult(ctx, r.EventRecorder, obj, recResult, err) + recResult, retErr = r.reconcile(ctx, obj, reconcilers) + return } // summarizeAndPatch analyzes the object conditions to create a summary of the -// status conditions and patches the object with the calculated summary. -func (r *GitRepositoryReconciler) summarizeAndPatch(ctx context.Context, obj *sourcev1.GitRepository, patchHelper *patch.Helper, res sreconcile.Result, recErr error) error { +// status conditions, computes runtime results and patches the object in the K8s +// API server. +func (r *GitRepositoryReconciler) summarizeAndPatch( + ctx context.Context, + obj *sourcev1.GitRepository, + patchHelper *patch.Helper, + res sreconcile.Result, + recErr error) (ctrl.Result, error) { + sreconcile.RecordContextualError(ctx, r.EventRecorder, obj, recErr) + // Record the value of the reconciliation request if any. if v, ok := meta.ReconcileAnnotationValue(obj.GetAnnotations()); ok { obj.Status.SetLastHandledReconcileRequest(v) @@ -200,7 +207,8 @@ func (r *GitRepositoryReconciler) summarizeAndPatch(ctx context.Context, obj *so // Compute the reconcile results, obtain patch options and reconcile error. var patchOpts []patch.Option - patchOpts, recErr = sreconcile.ComputeReconcileResult(obj, res, recErr, gitRepoOwnedConditions) + var result ctrl.Result + patchOpts, result, recErr = sreconcile.ComputeReconcileResult(obj, obj.GetRequeueAfter(), res, recErr, gitRepoOwnedConditions) // Summarize the Ready condition based on abnormalities that may have been observed. conditions.SetSummary(obj, @@ -222,7 +230,7 @@ func (r *GitRepositoryReconciler) summarizeAndPatch(ctx context.Context, obj *so recErr = kerrors.NewAggregate([]error{recErr, err}) } - return recErr + return result, recErr } // reconcile steps iterates through the actual reconciliation tasks for objec, diff --git a/controllers/helmchart_controller.go b/controllers/helmchart_controller.go index db6ec5c1..865acd8c 100644 --- a/controllers/helmchart_controller.go +++ b/controllers/helmchart_controller.go @@ -169,21 +169,19 @@ func (r *HelmChartReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, nil } - // Initialize the patch helper + // Initialize the patch helper with the current version of the object. patchHelper, err := patch.NewHelper(obj, r.Client) if err != nil { return ctrl.Result{}, err } - // Result of the sub-reconciliation + // recResult stores the abstracted reconcile result. var recResult sreconcile.Result // Always attempt to patch the object after each reconciliation. - // NOTE: This deferred block only modifies the named return error. The - // result from the reconciliation remains the same. Any requeue attributes - // set in the result will continue to be effective. + // NOTE: The final runtime result and error are set in this block. defer func() { - retErr = r.summarizeAndPatch(ctx, obj, patchHelper, recResult, retErr) + result, retErr = r.summarizeAndPatch(ctx, obj, patchHelper, recResult, retErr) // Always record readiness and duration metrics r.Metrics.RecordReadiness(ctx, obj) @@ -195,13 +193,13 @@ func (r *HelmChartReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( if !controllerutil.ContainsFinalizer(obj, sourcev1.SourceFinalizer) { controllerutil.AddFinalizer(obj, sourcev1.SourceFinalizer) recResult = sreconcile.ResultRequeue - return ctrl.Result{Requeue: true}, nil + return } // Examine if the object is under deletion if !obj.ObjectMeta.DeletionTimestamp.IsZero() { - res, err := r.reconcileDelete(ctx, obj) - return sreconcile.BuildRuntimeResult(ctx, r.EventRecorder, obj, res, err) + recResult, retErr = r.reconcileDelete(ctx, obj) + return } // Reconcile actual object @@ -210,15 +208,21 @@ func (r *HelmChartReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( r.reconcileSource, r.reconcileArtifact, } - recResult, err = r.reconcile(ctx, obj, reconcilers) - return sreconcile.BuildRuntimeResult(ctx, r.EventRecorder, obj, recResult, err) + recResult, retErr = r.reconcile(ctx, obj, reconcilers) + return } // summarizeAndPatch analyzes the object conditions to create a summary of the -// status conditions and patches the object with the calculated summary. The -// reconciler error type is also used to determine the conditions and the -// returned error. -func (r *HelmChartReconciler) summarizeAndPatch(ctx context.Context, obj *sourcev1.HelmChart, patchHelper *patch.Helper, res sreconcile.Result, recErr error) error { +// status conditions, computes runtime results and patches the object in the K8s +// API server. +func (r *HelmChartReconciler) summarizeAndPatch( + ctx context.Context, + obj *sourcev1.HelmChart, + patchHelper *patch.Helper, + res sreconcile.Result, + recErr error) (ctrl.Result, error) { + sreconcile.RecordContextualError(ctx, r.EventRecorder, obj, recErr) + // Record the value of the reconciliation request, if any if v, ok := meta.ReconcileAnnotationValue(obj.GetAnnotations()); ok { obj.Status.SetLastHandledReconcileRequest(v) @@ -226,7 +230,8 @@ func (r *HelmChartReconciler) summarizeAndPatch(ctx context.Context, obj *source // Compute the reconcile results, obtain patch options and reconcile error. var patchOpts []patch.Option - patchOpts, recErr = sreconcile.ComputeReconcileResult(obj, res, recErr, helmChartOwnedConditions) + var result ctrl.Result + patchOpts, result, recErr = sreconcile.ComputeReconcileResult(obj, obj.GetRequeueAfter(), res, recErr, helmChartOwnedConditions) // Summarize Ready condition conditions.SetSummary(obj, @@ -247,7 +252,7 @@ func (r *HelmChartReconciler) summarizeAndPatch(ctx context.Context, obj *source } recErr = kerrors.NewAggregate([]error{recErr, err}) } - return recErr + return result, recErr } // reconcile steps through the actual reconciliation tasks for the object, it returns early on the first step that diff --git a/controllers/helmchart_controller_test.go b/controllers/helmchart_controller_test.go index 5e74173a..57abbe59 100644 --- a/controllers/helmchart_controller_test.go +++ b/controllers/helmchart_controller_test.go @@ -1372,7 +1372,8 @@ func TestHelmChartReconciler_summarizeAndPatch(t *testing.T) { builder := fake.NewClientBuilder().WithScheme(testEnv.GetScheme()) r := &HelmChartReconciler{ - Client: builder.Build(), + Client: builder.Build(), + EventRecorder: record.NewFakeRecorder(32), } obj := &sourcev1.HelmChart{ ObjectMeta: metav1.ObjectMeta{ @@ -1393,7 +1394,7 @@ func TestHelmChartReconciler_summarizeAndPatch(t *testing.T) { patchHelper, err := patch.NewHelper(obj, r.Client) g.Expect(err).ToNot(HaveOccurred()) - gotErr := r.summarizeAndPatch(ctx, obj, patchHelper, tt.result, tt.reconcileErr) + _, gotErr := r.summarizeAndPatch(ctx, obj, patchHelper, tt.result, tt.reconcileErr) g.Expect(gotErr != nil).To(Equal(tt.wantErr)) g.Expect(obj.Status.Conditions).To(conditions.MatchConditions(tt.assertConditions)) diff --git a/controllers/helmrepository_controller.go b/controllers/helmrepository_controller.go index e5d655c9..29bf46df 100644 --- a/controllers/helmrepository_controller.go +++ b/controllers/helmrepository_controller.go @@ -132,21 +132,19 @@ func (r *HelmRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, nil } - // Initialize the patch helper + // Initialize the patch helper with the current version of the object. patchHelper, err := patch.NewHelper(obj, r.Client) if err != nil { return ctrl.Result{}, err } - // Result of the sub-reconciliation. + // recResult stores the abstracted reconcile result. var recResult sreconcile.Result // Always attempt to patch the object after each reconciliation. - // NOTE: This deferred block only modifies the named return error. The - // result from the reconciliation remains the same. Any requeue attributes - // set in the result will continue to be effective. + // NOTE: The final runtime result and error are set in this block. defer func() { - retErr = r.summarizeAndPatch(ctx, obj, patchHelper, recResult, retErr) + result, retErr = r.summarizeAndPatch(ctx, obj, patchHelper, recResult, retErr) // Always record readiness and duration metrics r.Metrics.RecordReadiness(ctx, obj) @@ -158,13 +156,13 @@ func (r *HelmRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reque if !controllerutil.ContainsFinalizer(obj, sourcev1.SourceFinalizer) { controllerutil.AddFinalizer(obj, sourcev1.SourceFinalizer) recResult = sreconcile.ResultRequeue - return ctrl.Result{Requeue: true}, nil + return } // Examine if the object is under deletion if !obj.ObjectMeta.DeletionTimestamp.IsZero() { - res, err := r.reconcileDelete(ctx, obj) - return sreconcile.BuildRuntimeResult(ctx, r.EventRecorder, obj, res, err) + recResult, retErr = r.reconcileDelete(ctx, obj) + return } // Reconcile actual object @@ -173,15 +171,21 @@ func (r *HelmRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reque r.reconcileSource, r.reconcileArtifact, } - recResult, err = r.reconcile(ctx, obj, reconcilers) - return sreconcile.BuildRuntimeResult(ctx, r.EventRecorder, obj, recResult, err) + recResult, retErr = r.reconcile(ctx, obj, reconcilers) + return } // summarizeAndPatch analyzes the object conditions to create a summary of the -// status conditions and patches the object with the calculated summary. The -// reconciler error type is also used to determine the conditions and the -// returned error. -func (r *HelmRepositoryReconciler) summarizeAndPatch(ctx context.Context, obj *sourcev1.HelmRepository, patchHelper *patch.Helper, res sreconcile.Result, recErr error) error { +// status conditions, computes runtime results and patches the object in the K8s +// API server. +func (r *HelmRepositoryReconciler) summarizeAndPatch( + ctx context.Context, + obj *sourcev1.HelmRepository, + patchHelper *patch.Helper, + res sreconcile.Result, + recErr error) (ctrl.Result, error) { + sreconcile.RecordContextualError(ctx, r.EventRecorder, obj, recErr) + // Record the value of the reconciliation request, if any. if v, ok := meta.ReconcileAnnotationValue(obj.GetAnnotations()); ok { obj.Status.SetLastHandledReconcileRequest(v) @@ -189,7 +193,8 @@ func (r *HelmRepositoryReconciler) summarizeAndPatch(ctx context.Context, obj *s // Compute the reconcile results, obtain patch options and reconcile error. var patchOpts []patch.Option - patchOpts, recErr = sreconcile.ComputeReconcileResult(obj, res, recErr, helmRepoOwnedConditions) + var result ctrl.Result + patchOpts, result, recErr = sreconcile.ComputeReconcileResult(obj, obj.GetRequeueAfter(), res, recErr, helmRepoOwnedConditions) // Summarize Ready condition. conditions.SetSummary(obj, @@ -211,7 +216,7 @@ func (r *HelmRepositoryReconciler) summarizeAndPatch(ctx context.Context, obj *s recErr = kerrors.NewAggregate([]error{recErr, err}) } - return recErr + return result, recErr } // reconcile iterates through the sub-reconcilers and processes the source diff --git a/controllers/helmrepository_controller_test.go b/controllers/helmrepository_controller_test.go index 68790bd3..410f8b76 100644 --- a/controllers/helmrepository_controller_test.go +++ b/controllers/helmrepository_controller_test.go @@ -752,7 +752,8 @@ func TestHelmRepositoryReconciler_summarizeAndPatch(t *testing.T) { builder := fakeclient.NewClientBuilder().WithScheme(testEnv.GetScheme()) r := &HelmRepositoryReconciler{ - Client: builder.Build(), + Client: builder.Build(), + EventRecorder: record.NewFakeRecorder(32), } obj := &sourcev1.HelmRepository{ ObjectMeta: metav1.ObjectMeta{ @@ -773,7 +774,7 @@ func TestHelmRepositoryReconciler_summarizeAndPatch(t *testing.T) { patchHelper, err := patch.NewHelper(obj, r.Client) g.Expect(err).ToNot(HaveOccurred()) - gotErr := r.summarizeAndPatch(ctx, obj, patchHelper, tt.result, tt.reconcileErr) + _, gotErr := r.summarizeAndPatch(ctx, obj, patchHelper, tt.result, tt.reconcileErr) g.Expect(gotErr != nil).To(Equal(tt.wantErr)) g.Expect(obj.Status.Conditions).To(conditions.MatchConditions(tt.assertConditions)) diff --git a/internal/error/error.go b/internal/error/error.go index df20ccc4..4333c460 100644 --- a/internal/error/error.go +++ b/internal/error/error.go @@ -16,6 +16,8 @@ limitations under the License. package error +import "time" + // Stalling is the reconciliation stalled state error. It contains an error // and a reason for the stalled condition. type Stalling struct { @@ -54,3 +56,24 @@ func (ee *Event) Error() string { func (ee *Event) Unwrap() error { return ee.Err } + +// Waiting is the reconciliation wait state error. It contains an error, wait +// duration and a reason for the wait. +type Waiting struct { + // RequeueAfter is the wait duration after which to requeue. + RequeueAfter time.Duration + // Reason is the reason for the wait. + Reason string + // Err is the error that caused the wait. + Err error +} + +// Error implement error interface. +func (we *Waiting) Error() string { + return we.Err.Error() +} + +// Unwrap returns the underlying error. +func (we *Waiting) Unwrap() error { + return we.Err +} diff --git a/internal/reconcile/reconcile.go b/internal/reconcile/reconcile.go index 2da1f809..038e7e24 100644 --- a/internal/reconcile/reconcile.go +++ b/internal/reconcile/reconcile.go @@ -18,8 +18,10 @@ package reconcile import ( "context" + "time" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" kuberecorder "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" @@ -27,7 +29,6 @@ import ( "github.com/fluxcd/pkg/runtime/conditions" "github.com/fluxcd/pkg/runtime/patch" - sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" serror "github.com/fluxcd/source-controller/internal/error" ) @@ -48,42 +49,51 @@ const ( // BuildRuntimeResult converts a given Result and error into the // return values of a controller's Reconcile function. -func BuildRuntimeResult(ctx context.Context, recorder kuberecorder.EventRecorder, obj sourcev1.Source, rr Result, err error) (ctrl.Result, error) { - // NOTE: The return values can be modified based on the error type. - // For example, if an error signifies a short requeue period that's - // not equal to the requeue period of the object, the error can be checked - // and an appropriate result with the period can be returned. - // - // Example: - // if e, ok := err.(*waitError); ok { - // return ctrl.Result{RequeueAfter: e.RequeueAfter}, err - // } +// func BuildRuntimeResult(ctx context.Context, recorder kuberecorder.EventRecorder, obj sourcev1.Source, rr Result, err error) (ctrl.Result, error) { +func BuildRuntimeResult(successInterval time.Duration, rr Result, err error) ctrl.Result { + // Handle special errors that contribute to expressing the result. + if e, ok := err.(*serror.Waiting); ok { + return ctrl.Result{RequeueAfter: e.RequeueAfter} + } - // Log and record event based on the error. + switch rr { + case ResultRequeue: + return ctrl.Result{Requeue: true} + case ResultSuccess: + return ctrl.Result{RequeueAfter: successInterval} + default: + return ctrl.Result{} + } +} + +// RecordContextualError records the contextual errors based on their types. +// An event is recorded for the errors that are returned to the runtime. The +// runtime handles the logging of the error. +// An event is recorded and an error is logged for errors that are known to be +// swallowed, not returned to the runtime. +func RecordContextualError(ctx context.Context, recorder kuberecorder.EventRecorder, obj runtime.Object, err error) { switch e := err.(type) { case *serror.Event: recorder.Eventf(obj, corev1.EventTypeWarning, e.Reason, e.Error()) + case *serror.Waiting: + // Waiting errors are not returned to the runtime. Log it explicitly. + ctrl.LoggerFrom(ctx).Info("reconciliation waiting", "reason", e.Err, "duration", e.RequeueAfter) + recorder.Event(obj, corev1.EventTypeNormal, e.Reason, e.Error()) case *serror.Stalling: // Stalling errors are not returned to the runtime. Log it explicitly. ctrl.LoggerFrom(ctx).Error(e, "reconciliation stalled") recorder.Eventf(obj, corev1.EventTypeWarning, e.Reason, e.Error()) } - - switch rr { - case ResultRequeue: - return ctrl.Result{Requeue: true}, err - case ResultSuccess: - return ctrl.Result{RequeueAfter: obj.GetRequeueAfter()}, err - default: - return ctrl.Result{}, err - } } // ComputeReconcileResult analyzes the reconcile results (result + error), // updates the status conditions of the object with any corrections and returns -// result patch configuration and any error to the caller. The caller is -// responsible for using the patch option to patch the object in the API server. -func ComputeReconcileResult(obj conditions.Setter, res Result, recErr error, ownedConditions []string) ([]patch.Option, error) { +// object patch configuration, runtime result and runtime error. The caller is +// responsible for using the patch configuration to patch the object in the API +// server. +func ComputeReconcileResult(obj conditions.Setter, successInterval time.Duration, res Result, recErr error, ownedConditions []string) ([]patch.Option, ctrl.Result, error) { + result := BuildRuntimeResult(successInterval, res, recErr) + // Remove reconciling condition on successful reconciliation. if recErr == nil && res == ResultSuccess { conditions.Delete(obj, meta.ReconcilingCondition) @@ -105,10 +115,16 @@ func ComputeReconcileResult(obj conditions.Setter, res Result, recErr error, own // requeuing. pOpts = append(pOpts, patch.WithStatusObservedGeneration{}) conditions.MarkStalled(obj, t.Reason, t.Error()) - return pOpts, nil + return pOpts, result, nil } // NOTE: Non-empty result with stalling error indicates that the // returned result is incorrect. + case *serror.Waiting: + // The reconcile resulted in waiting error, remove stalled condition if + // present. + conditions.Delete(obj, meta.StalledCondition) + // The reconciler needs to wait and retry. Return no error. + return pOpts, result, nil case nil: // The reconcile didn't result in any error, we are not in stalled // state. If a requeue is requested, the current generation has not been @@ -123,7 +139,7 @@ func ComputeReconcileResult(obj conditions.Setter, res Result, recErr error, own conditions.Delete(obj, meta.StalledCondition) } - return pOpts, recErr + return pOpts, result, recErr } // LowestRequeuingResult returns the ReconcileResult with the lowest requeue