diff --git a/pkg/internal/handler/requeue_filter.go b/pkg/internal/handler/requeue_filter.go new file mode 100644 index 00000000..bbf2e4c3 --- /dev/null +++ b/pkg/internal/handler/requeue_filter.go @@ -0,0 +1,61 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package handler + +import ( + "context" + + "k8s.io/client-go/util/workqueue" + "sigs.k8s.io/controller-runtime/pkg/event" + crthandler "sigs.k8s.io/controller-runtime/pkg/handler" +) + +type requeueFilter struct { + crthandler.EventHandler +} + +// RequeueFilter wraps the EventHandler and skips the event if it was requeued for the given object +func RequeueFilter(delegate crthandler.EventHandler) crthandler.EventHandler { + return &requeueFilter{EventHandler: delegate} +} + +func (r *requeueFilter) Create(ctx context.Context, evt event.CreateEvent, q workqueue.RateLimitingInterface) { + r.EventHandler.Create(ctx, evt, &delegatingQueue{RateLimitingInterface: q}) +} + +func (r *requeueFilter) Update(ctx context.Context, evt event.UpdateEvent, q workqueue.RateLimitingInterface) { + r.EventHandler.Update(ctx, evt, &delegatingQueue{RateLimitingInterface: q}) +} + +func (r *requeueFilter) Delete(ctx context.Context, evt event.DeleteEvent, q workqueue.RateLimitingInterface) { + r.EventHandler.Delete(ctx, evt, &delegatingQueue{RateLimitingInterface: q}) +} + +func (r *requeueFilter) Generic(ctx context.Context, evt event.GenericEvent, q workqueue.RateLimitingInterface) { + r.EventHandler.Generic(ctx, evt, &delegatingQueue{RateLimitingInterface: q}) +} + +type delegatingQueue struct { + workqueue.RateLimitingInterface +} + +func (q *delegatingQueue) Add(item interface{}) { + if q.RateLimitingInterface.NumRequeues(item) > 0 { + return + } + q.RateLimitingInterface.Add(item) +} diff --git a/pkg/reconciler/reconciler.go b/pkg/reconciler/reconciler.go index 6ccb05b1..187aa242 100644 --- a/pkg/reconciler/reconciler.go +++ b/pkg/reconciler/reconciler.go @@ -25,6 +25,7 @@ import ( "time" "github.com/go-logr/logr" + "github.com/operator-framework/helm-operator-plugins/pkg/internal/handler" sdkhandler "github.com/operator-framework/operator-lib/handler" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart" @@ -41,8 +42,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/predicate" + ctrlhandler "sigs.k8s.io/controller-runtime/pkg/handler" ctrlpredicate "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/source" @@ -73,7 +73,7 @@ type Reconciler struct { log logr.Logger gvk *schema.GroupVersionKind chrt *chart.Chart - selectorPredicate predicate.Predicate + selectorPredicate ctrlpredicate.Predicate overrideValues map[string]string skipDependentWatches bool maxConcurrentReconciles int @@ -929,7 +929,11 @@ func (r *Reconciler) setupWatches(mgr ctrl.Manager, c controller.Controller) err if err := c.Watch( source.Kind(mgr.GetCache(), secret), - handler.EnqueueRequestForOwner(mgr.GetScheme(), mgr.GetRESTMapper(), obj, handler.OnlyControllerOwner()), + // Do not schedule a new reconcile if the upgrade has failed and the controller has entered an exponential backoff + // to avoid endless reconcile loop. + handler.RequeueFilter( + ctrlhandler.EnqueueRequestForOwner(mgr.GetScheme(), mgr.GetRESTMapper(), obj, ctrlhandler.OnlyControllerOwner()), + ), ); err != nil { return err }