From 14f2d612b48e35de613885c744a2a1f825a5c9e4 Mon Sep 17 00:00:00 2001 From: Moritz Clasmeier Date: Mon, 27 Oct 2025 15:37:01 +0100 Subject: [PATCH] Add WithPredicate() option to support custom event filtering This adds support for custom predicates to filter reconciliation events, which is critical for multi-controller scenarios where status-only updates should not trigger expensive helm dry-runs. Changes: - Add customPredicates []predicate.Predicate field to Reconciler - Add WithPredicate() option to configure custom predicates - Update setupWatches() to include custom predicates in watch configuration Example usage: reconciler.WithPredicate(predicate.GenerationChangedPredicate{}) This allows operators to skip reconciliation when only status/metadata changes, preventing unnecessary helm dry-runs that can cause significant load in environments with many CRs. --- pkg/reconciler/reconciler.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/pkg/reconciler/reconciler.go b/pkg/reconciler/reconciler.go index 98b07fc..db92150 100644 --- a/pkg/reconciler/reconciler.go +++ b/pkg/reconciler/reconciler.go @@ -80,6 +80,7 @@ type Reconciler struct { gvk *schema.GroupVersionKind chrt *chart.Chart selectorPredicate predicate.Predicate + customPredicates []predicate.Predicate overrideValues map[string]string skipDependentWatches bool extraWatchSources []source.Source @@ -593,6 +594,20 @@ func WithSelector(s metav1.LabelSelector) Option { } } +// WithPredicate is an Option that adds a custom predicate to filter reconciliation events. +// Multiple predicates can be added and will be combined using AND logic. +// This is useful for filtering out unnecessary reconciliations, such as status-only updates. +// +// Example: +// +// reconciler.WithPredicate(predicate.GenerationChangedPredicate{}) +func WithPredicate(p predicate.Predicate) Option { + return func(r *Reconciler) error { + r.customPredicates = append(r.customPredicates, p) + return nil + } +} + // WithControllerSetupFunc is an Option that allows customizing a controller before it is started. // The only supported customization here is adding additional Watch sources to the controller. func WithControllerSetupFunc(f ControllerSetupFunc) Option { @@ -1155,10 +1170,11 @@ func (r *Reconciler) setupWatches(mgr ctrl.Manager, c controller.Controller) err obj := &unstructured.Unstructured{} obj.SetGroupVersionKind(*r.gvk) - var preds []predicate.Predicate + preds := make([]predicate.Predicate, 0, len(r.customPredicates)+1) if r.selectorPredicate != nil { preds = append(preds, r.selectorPredicate) } + preds = append(preds, r.customPredicates...) if err := c.Watch( source.Kind(