@@ -40,8 +40,9 @@ import (
4040 "sigs.k8s.io/controller-runtime/pkg/source"
4141
4242 "github.com/sap/component-operator-runtime/internal/backoff"
43- "github.com/sap/component-operator-runtime/internal/cluster "
43+ "github.com/sap/component-operator-runtime/internal/clientfactory "
4444 "github.com/sap/component-operator-runtime/internal/metrics"
45+ "github.com/sap/component-operator-runtime/pkg/cluster"
4546 "github.com/sap/component-operator-runtime/pkg/manifests"
4647 "github.com/sap/component-operator-runtime/pkg/reconciler"
4748 "github.com/sap/component-operator-runtime/pkg/status"
@@ -85,6 +86,10 @@ const (
8586// has been successful.
8687type HookFunc [T Component ] func (ctx context.Context , clnt client.Client , component T ) error
8788
89+ // NewClientFunc is the function signature that can be used to modify or replace the default
90+ // client used by the reconciler.
91+ type NewClientFunc func (clnt cluster.Client ) (cluster.Client , error )
92+
8893// ReconcilerOptions are creation options for a Reconciler.
8994type ReconcilerOptions struct {
9095 // Which field manager to use in API calls.
@@ -114,6 +119,10 @@ type ReconcilerOptions struct {
114119 // SchemeBuilder allows to define additional schemes to be made available in the
115120 // target client.
116121 SchemeBuilder types.SchemeBuilder
122+ // NewClientFunc allows to modify or replace the default client used by the reconciler.
123+ // The returned client is used by the reconciler to manage the component instances, and passed to hooks.
124+ // Its scheme therefore must recognize the component type.
125+ NewClient NewClientFunc
117126}
118127
119128// Reconciler provides the implementation of controller-runtime's Reconciler interface, for a given Component type T.
@@ -126,7 +135,7 @@ type Reconciler[T Component] struct {
126135 resourceGenerator manifests.Generator
127136 statusAnalyzer status.StatusAnalyzer
128137 options ReconcilerOptions
129- clients * cluster .ClientFactory
138+ clients * clientfactory .ClientFactory
130139 backoff * backoff.Backoff
131140 postReadHooks []HookFunc [T ]
132141 preReconcileHooks []HookFunc [T ]
@@ -300,10 +309,15 @@ func (r *Reconciler[T]) Reconcile(ctx context.Context, req ctrl.Request) (result
300309 // TODO: should we move this behind the DeepEqual check below to avoid noise?
301310 // also note: it seems that no events will be written if the component's namespace is in deletion
302311 state , reason , message := status .GetState ()
312+ var eventAnnotations map [string ]string
313+ // TODO: formalize this into a real published interface
314+ if eventAnnotationProvider , ok := Component (component ).(interface { GetEventAnnotations () map [string ]string }); ok {
315+ eventAnnotations = eventAnnotationProvider .GetEventAnnotations ()
316+ }
303317 if state == StateError {
304- r .client .EventRecorder ().Event (component , corev1 .EventTypeWarning , reason , message )
318+ r .client .EventRecorder ().AnnotatedEventf (component , eventAnnotations , corev1 .EventTypeWarning , reason , message )
305319 } else {
306- r .client .EventRecorder ().Event (component , corev1 .EventTypeNormal , reason , message )
320+ r .client .EventRecorder ().AnnotatedEventf (component , eventAnnotations , corev1 .EventTypeNormal , reason , message )
307321 }
308322
309323 if skipStatusUpdate {
@@ -431,15 +445,13 @@ func (r *Reconciler[T]) Reconcile(ctx context.Context, req ctrl.Request) (result
431445 log .V (1 ).Info ("deletion not allowed" )
432446 // TODO: have an additional StateDeletionBlocked?
433447 // TODO: eliminate this msg logic
434- r .client .EventRecorder ().Event (component , corev1 .EventTypeNormal , readyConditionReasonDeletionBlocked , "Deletion blocked: " + msg )
435448 status .SetState (StateDeleting , readyConditionReasonDeletionBlocked , "Deletion blocked: " + msg )
436449 return ctrl.Result {RequeueAfter : 1 * time .Second + r .backoff .Next (req , readyConditionReasonDeletionBlocked )}, nil
437450 }
438451 if len (slices .Remove (component .GetFinalizers (), * r .options .Finalizer )) > 0 {
439452 // deletion is blocked because of foreign finalizers
440453 log .V (1 ).Info ("deleted blocked due to existence of foreign finalizers" )
441454 // TODO: have an additional StateDeletionBlocked?
442- r .client .EventRecorder ().Event (component , corev1 .EventTypeNormal , readyConditionReasonDeletionBlocked , "Deletion blocked due to existing foreign finalizers" )
443455 status .SetState (StateDeleting , readyConditionReasonDeletionBlocked , "Deletion blocked due to existing foreign finalizers" )
444456 return ctrl.Result {RequeueAfter : 1 * time .Second + r .backoff .Next (req , readyConditionReasonDeletionBlocked )}, nil
445457 }
@@ -578,7 +590,14 @@ func (r *Reconciler[T]) SetupWithManagerAndBuilder(mgr ctrl.Manager, blder *ctrl
578590 if err != nil {
579591 return errors .Wrap (err , "error creating discovery client" )
580592 }
581- r .client = cluster .NewClient (mgr .GetClient (), discoveryClient , mgr .GetEventRecorderFor (r .name ))
593+ r .client = cluster .NewClient (mgr .GetClient (), discoveryClient , mgr .GetEventRecorderFor (r .name ), mgr .GetConfig (), mgr .GetHTTPClient ())
594+ if r .options .NewClient != nil {
595+ clnt , err := r .options .NewClient (r .client )
596+ if err != nil {
597+ return errors .Wrap (err , "error calling custom client constructor" )
598+ }
599+ r .client = clnt
600+ }
582601
583602 component := newComponent [T ]()
584603 r .groupVersionKind , err = apiutil .GVKForObject (component , r .client .Scheme ())
@@ -596,7 +615,7 @@ func (r *Reconciler[T]) SetupWithManagerAndBuilder(mgr ctrl.Manager, blder *ctrl
596615 if r .options .SchemeBuilder != nil {
597616 schemeBuilders = append (schemeBuilders , r .options .SchemeBuilder )
598617 }
599- r .clients , err = cluster .NewClientFactory (r .name , r .controllerName , mgr .GetConfig (), schemeBuilders )
618+ r .clients , err = clientfactory .NewClientFactory (r .name , r .controllerName , mgr .GetConfig (), schemeBuilders )
600619 if err != nil {
601620 return errors .Wrap (err , "error creating client factory" )
602621 }
0 commit comments