Skip to content
Merged
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/prometheus/client_golang v1.22.0
github.com/prometheus/client_model v0.6.2
github.com/samber/lo v1.50.0
github.com/stretchr/testify v1.10.0
go.uber.org/multierr v1.11.0
go.uber.org/zap v1.27.0
golang.org/x/time v0.12.0
Expand Down Expand Up @@ -50,6 +51,7 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
Expand Down
80 changes: 80 additions & 0 deletions reconciler/reconciler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package reconciler

import (
"context"

"k8s.io/client-go/util/workqueue"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

// Result is a wrapper around reconcile.Result that adds RequeueWithBackoff functionality.
type Result struct {
reconcile.Result
RequeueWithBackoff bool
}

// KeyExtractor extracts a rate limiter key from a context and reconcile.Request.
type KeyExtractor[K any] interface {
Extract(ctx context.Context, req reconcile.Request) K
}

// RequestKeyExtractor extracts a reconcile.Request key for rate limiting.
type RequestKeyExtractor struct{}

// Extract returns the reconcile.Request as the key.
func (e RequestKeyExtractor) Extract(ctx context.Context, req reconcile.Request) reconcile.Request {
return req
}

// Reconciler defines the interface for standard reconcilers
type Reconciler interface {
Reconcile(ctx context.Context) (Result, error)
}

// ReconcilerFunc is a function type that implements the Reconciler interface.
type ReconcilerFunc func(ctx context.Context) (Result, error)

// Reconcile implements the Reconciler interface.
func (f ReconcilerFunc) Reconcile(ctx context.Context) (Result, error) {
return f(ctx)
}

// AsReconciler creates a reconciler from a standard reconciler
func AsReconciler(reconciler Reconciler) reconcile.Reconciler {
return AsGenericReconciler(
func(ctx context.Context, req reconcile.Request) (Result, error) {
return reconciler.Reconcile(ctx)
},
RequestKeyExtractor{},
)
}

// AsGenericReconciler creates a reconciler with a specific key extractor
func AsGenericReconciler[K comparable](
reconcileFunc func(ctx context.Context, req reconcile.Request) (Result, error),
keyExtractor KeyExtractor[K],
) reconcile.Reconciler {
return AsGenericReconcilerWithRateLimiter(
reconcileFunc,
keyExtractor,
workqueue.DefaultTypedControllerRateLimiter[K](),
)
}

// AsGenericReconcilerWithRateLimiter creates a reconciler with a custom rate limiter
func AsGenericReconcilerWithRateLimiter[K comparable](
reconcileFunc func(ctx context.Context, req reconcile.Request) (Result, error),
keyExtractor KeyExtractor[K],
rateLimiter workqueue.TypedRateLimiter[K],
) reconcile.Reconciler {
return reconcile.Func(func(ctx context.Context, req reconcile.Request) (reconcile.Result, error) {
result, err := reconcileFunc(ctx, req)
if err != nil {
return reconcile.Result{}, err
}
if result.RequeueWithBackoff {
return reconcile.Result{RequeueAfter: rateLimiter.When(keyExtractor.Extract(ctx, req))}, nil
}
return result.Result, nil
})
}
Loading