diff --git a/webhosting-operator/pkg/experiment/generator/reconciler.go b/webhosting-operator/pkg/experiment/generator/reconciler.go index 37e9ce94..e83438e3 100644 --- a/webhosting-operator/pkg/experiment/generator/reconciler.go +++ b/webhosting-operator/pkg/experiment/generator/reconciler.go @@ -60,7 +60,7 @@ func (r *Every) AddToManager(mgr manager.Manager) error { RateLimiter: &workqueue.BucketRateLimiter{Limiter: rate.NewLimiter(r.Rate, int(r.Rate))}, }). WatchesRawSource(EmitN(reconcileWorkers), &handler.EnqueueRequestForObject{}). - Complete(r) + Complete(StopOnContextCanceled(r)) } func (r *Every) Reconcile(ctx context.Context, _ reconcile.Request) (reconcile.Result, error) { @@ -122,7 +122,7 @@ func (r *ForEach[T]) AddToManager(mgr manager.Manager) error { GenericFunc: func(event.GenericEvent) bool { return false }, }), ). - Complete(r) + Complete(StopOnContextCanceled(r)) } func (r *ForEach[T]) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) { diff --git a/webhosting-operator/pkg/experiment/generator/utils.go b/webhosting-operator/pkg/experiment/generator/utils.go index 82dcc39e..c6af9447 100644 --- a/webhosting-operator/pkg/experiment/generator/utils.go +++ b/webhosting-operator/pkg/experiment/generator/utils.go @@ -18,6 +18,7 @@ package generator import ( "context" + "errors" "fmt" "sync" "time" @@ -32,6 +33,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" ) @@ -62,6 +64,19 @@ func EmitN(n int) source.Source { }) } +// StopOnContextCanceled wraps the given reconciler so that "context canceled" errors are ignored. This is helpful when +// a reconciler is expected to be canceled (e.g., when the scenario finishes). We neither need to retry nor log on such +// errors. We can just stop silently. +func StopOnContextCanceled(r reconcile.Reconciler) reconcile.Reconciler { + return reconcile.Func(func(ctx context.Context, request reconcile.Request) (reconcile.Result, error) { + result, err := r.Reconcile(ctx, request) + if errors.Is(err, context.Canceled) { + err = nil + } + return result, err + }) +} + // NTimesConcurrently runs the given action n times. It distributes the work across the given number of concurrent // workers. func NTimesConcurrently(n, workers int, do func() error) error {