Skip to content

Commit 49671db

Browse files
authored
Merge pull request #73 from fxiang1/feng-informer
Add custom informer for ManagedClusterAddOns
2 parents 467bc8f + 0e04278 commit 49671db

File tree

7 files changed

+713
-87
lines changed

7 files changed

+713
-87
lines changed

controllers/clusterpermission_controller.go

Lines changed: 119 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -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
5451
type 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.
6059
func (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
739785
func (r *ClusterPermissionReconciler) clusterPermissionUsesManagedServiceAccount(cp *cpv1alpha1.ClusterPermission) bool {
740786
// Check ClusterRoleBinding

0 commit comments

Comments
 (0)