@@ -28,14 +28,11 @@ import (
2828 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2929 "k8s.io/apimachinery/pkg/runtime"
3030 "k8s.io/apimachinery/pkg/types"
31+ "k8s.io/apimachinery/pkg/util/wait"
3132 "k8s.io/client-go/util/retry"
3233 ctrl "sigs.k8s.io/controller-runtime"
33- "sigs.k8s.io/controller-runtime/pkg/builder"
3434 "sigs.k8s.io/controller-runtime/pkg/client"
35- "sigs.k8s.io/controller-runtime/pkg/event"
36- "sigs.k8s.io/controller-runtime/pkg/handler"
3735 "sigs.k8s.io/controller-runtime/pkg/log"
38- "sigs.k8s.io/controller-runtime/pkg/predicate"
3936 "sigs.k8s.io/controller-runtime/pkg/reconcile"
4037
4138 corev1 "k8s.io/api/core/v1"
@@ -54,43 +51,133 @@ const VALIDATION_MW_RETRY_INTERVAL = 10 * time.Second
5451type ClusterPermissionReconciler struct {
5552 client.Client
5653 Scheme * runtime.Scheme
54+ // customInformer is the custom informer for ManagedClusterAddOn resources
55+ customInformer * ManagedClusterAddOnInformer
5756}
5857
5958// SetupWithManager sets up the controller with the Manager.
6059func (r * ClusterPermissionReconciler ) SetupWithManager (mgr ctrl.Manager ) error {
60+ // Create custom informer for ManagedClusterAddOn resources
61+ config := mgr .GetConfig ()
62+ eventHandler := r .createCustomInformerEventHandler ()
63+
64+ customInformer , err := NewManagedClusterAddOnInformer (config , eventHandler )
65+ if err != nil {
66+ return err
67+ }
68+
69+ // Store the custom informer in the reconciler
70+ r .customInformer = customInformer
71+
72+ // Start the custom informer in a goroutine with retry logic
73+ go func () {
74+ backoff := wait.Backoff {
75+ Duration : 1 * time .Second ,
76+ Factor : 2.0 ,
77+ Jitter : 0.1 ,
78+ Steps : 5 , // Retry up to 5 times (1s, 2s, 4s, 8s, 16s)
79+ }
80+
81+ err := wait .ExponentialBackoff (backoff , func () (bool , error ) {
82+ if err := customInformer .Start (); err != nil {
83+ log .Log .Error (err , "Failed to start custom ManagedClusterAddOn informer, will retry" )
84+ return false , nil // Retry
85+ }
86+ log .Log .Info ("Successfully started custom ManagedClusterAddOn informer" )
87+ return true , nil // Success
88+ })
89+
90+ if err != nil {
91+ log .Log .Error (err , "Failed to start custom ManagedClusterAddOn informer after retries, controller cannot function properly" )
92+ panic ("custom ManagedClusterAddOn informer failed to start after retries" )
93+ }
94+ }()
95+
96+ // Setup the controller without the built-in ManagedClusterAddOn watching
97+ // since we're using the custom informer instead
6198 return ctrl .NewControllerManagedBy (mgr ).
6299 For (& cpv1alpha1.ClusterPermission {}).
63- Watches (& addonv1alpha1.ManagedClusterAddOn {},
64- r .managedClusterAddOnEventHandler (),
65- builder .WithPredicates (predicate.Funcs {
66- CreateFunc : func (e event.CreateEvent ) bool {
67- return false // Don't process Create events
68- },
69- UpdateFunc : func (e event.UpdateEvent ) bool {
70- // Only process Update events for managed-serviceaccount addon when status.namespace changes
71- oldAddon , oldOk := e .ObjectOld .(* addonv1alpha1.ManagedClusterAddOn )
72- newAddon , newOk := e .ObjectNew .(* addonv1alpha1.ManagedClusterAddOn )
100+ Complete (r )
101+ }
73102
74- if ! oldOk || ! newOk {
75- return false
76- }
103+ // Stop stops the custom informer if it exists
104+ func (r * ClusterPermissionReconciler ) Stop () {
105+ if r .customInformer != nil {
106+ r .customInformer .Stop ()
107+ }
108+ }
77109
78- // Only process if this is the managed-serviceaccount addon
79- if newAddon . Name != msacommon . AddonName {
80- return false
81- }
110+ // createCustomInformerEventHandler creates an event handler for the custom informer
111+ func ( r * ClusterPermissionReconciler ) createCustomInformerEventHandler () func ( obj * addonv1alpha1. ManagedClusterAddOn ) {
112+ return func ( addon * addonv1alpha1. ManagedClusterAddOn ) {
113+ log := log . Log . WithName ( "CustomInformerEventHandler" )
82114
83- // Only process if status.namespace has changed
84- return oldAddon .Status .Namespace != newAddon .Status .Namespace
85- },
86- DeleteFunc : func (e event.DeleteEvent ) bool {
87- return false // Don't process Delete events
88- },
89- GenericFunc : func (e event.GenericEvent ) bool {
90- return false // Don't process Generic events
91- },
92- })).
93- Complete (r )
115+ // Find all ClusterPermissions in this addon's namespace that have ManagedServiceAccount subjects
116+ ctx := context .Background ()
117+ var clusterPermissions cpv1alpha1.ClusterPermissionList
118+ err := r .List (ctx , & clusterPermissions , & client.ListOptions {
119+ Namespace : addon .Namespace ,
120+ })
121+ if err != nil {
122+ log .Error (err , "failed to list ClusterPermissions" , "namespace" , addon .Namespace )
123+ return
124+ }
125+
126+ // Process each ClusterPermission that uses ManagedServiceAccount
127+ for _ , cp := range clusterPermissions .Items {
128+ if r .clusterPermissionUsesManagedServiceAccount (& cp ) {
129+ log .Info ("Triggering reconciliation for ClusterPermission due to ManagedClusterAddOn change" ,
130+ "clusterPermission" , cp .Name ,
131+ "namespace" , cp .Namespace ,
132+ "addonNamespace" , addon .Status .Namespace ,
133+ )
134+
135+ // Trigger reconciliation by updating the ClusterPermission
136+ r .reconcileClusterPermission (ctx , & cp )
137+ }
138+ }
139+ }
140+ }
141+
142+ // reconcileClusterPermission triggers reconciliation of a specific ClusterPermission
143+ func (r * ClusterPermissionReconciler ) reconcileClusterPermission (ctx context.Context , cp * cpv1alpha1.ClusterPermission ) {
144+ log := log .FromContext (ctx )
145+
146+ // Create a reconcile request
147+ req := reconcile.Request {
148+ NamespacedName : types.NamespacedName {
149+ Name : cp .Name ,
150+ Namespace : cp .Namespace ,
151+ },
152+ }
153+
154+ // Retry with exponential backoff
155+ backoff := wait.Backoff {
156+ Duration : 1 * time .Second ,
157+ Factor : 2.0 ,
158+ Jitter : 0.1 ,
159+ Steps : 3 , // Retry up to 3 times (1s, 2s, 4s)
160+ }
161+
162+ err := wait .ExponentialBackoffWithContext (ctx , backoff , func (context.Context ) (bool , error ) {
163+ result , err := r .Reconcile (ctx , req )
164+ if err != nil {
165+ log .Error (err , "Failed to reconcile ClusterPermission, will retry" , "name" , cp .Name , "namespace" , cp .Namespace )
166+ return false , nil // Retry on error
167+ }
168+ if result .Requeue || result .RequeueAfter > 0 {
169+ log .Info ("Reconcile requested requeue, will retry" , "name" , cp .Name , "namespace" , cp .Namespace , "requeueAfter" , result .RequeueAfter )
170+ if result .RequeueAfter > 0 {
171+ time .Sleep (result .RequeueAfter )
172+ }
173+ return false , nil // Retry if requeue requested
174+ }
175+ return true , nil // Success
176+ })
177+
178+ if err != nil {
179+ log .Error (err , "Failed to reconcile ClusterPermission after retries" , "name" , cp .Name , "namespace" , cp .Namespace )
180+ }
94181}
95182
96183//+kubebuilder:rbac:groups=rbac.open-cluster-management.io,resources=clusterpermissions,verbs=get;list;watch;create;update;patch;delete
@@ -694,47 +781,6 @@ func joinStrings(strings []string, separator string) string {
694781 return result
695782}
696783
697- // managedClusterAddOnEventHandler returns an event handler that reconciles ClusterPermissions
698- // when a ManagedClusterAddOn's status.namespace changes
699- func (r * ClusterPermissionReconciler ) managedClusterAddOnEventHandler () handler.EventHandler {
700- return handler .EnqueueRequestsFromMapFunc (func (ctx context.Context , obj client.Object ) []reconcile.Request {
701- log := log .FromContext (ctx )
702-
703- addon , ok := obj .(* addonv1alpha1.ManagedClusterAddOn )
704- if ! ok {
705- log .Error (nil , "object is not a ManagedClusterAddOn" , "object" , obj )
706- return []reconcile.Request {}
707- }
708-
709- // Find all ClusterPermissions in this addon's namespace that have ManagedServiceAccount subjects
710- var clusterPermissions cpv1alpha1.ClusterPermissionList
711- err := r .List (ctx , & clusterPermissions , & client.ListOptions {
712- Namespace : addon .Namespace ,
713- })
714- if err != nil {
715- log .Error (err , "failed to list ClusterPermissions" , "namespace" , addon .Namespace )
716- return []reconcile.Request {}
717- }
718-
719- var requests []reconcile.Request
720- for _ , cp := range clusterPermissions .Items {
721- if r .clusterPermissionUsesManagedServiceAccount (& cp ) {
722- requests = append (requests , reconcile.Request {
723- NamespacedName : types.NamespacedName {
724- Name : cp .Name ,
725- Namespace : cp .Namespace ,
726- },
727- })
728- }
729- }
730-
731- log .Info ("ManagedClusterAddOn status.namespace changed, reconciling ClusterPermissions" ,
732- "addon" , addon .Name , "namespace" , addon .Namespace , "requests" , len (requests ))
733-
734- return requests
735- })
736- }
737-
738784// clusterPermissionUsesManagedServiceAccount checks if a ClusterPermission uses ManagedServiceAccount subjects
739785func (r * ClusterPermissionReconciler ) clusterPermissionUsesManagedServiceAccount (cp * cpv1alpha1.ClusterPermission ) bool {
740786 // Check ClusterRoleBinding
0 commit comments