@@ -28,12 +28,15 @@ import (
28
28
"github.com/go-logr/logr"
29
29
30
30
corev1 "k8s.io/api/core/v1"
31
- toolscache "k8s.io/client-go/tools/cache "
31
+ apierrors "k8s.io/apimachinery/pkg/api/errors "
32
32
"k8s.io/client-go/tools/clientcmd"
33
33
34
+ ctrl "sigs.k8s.io/controller-runtime"
35
+ "sigs.k8s.io/controller-runtime/pkg/builder"
34
36
"sigs.k8s.io/controller-runtime/pkg/client"
35
37
"sigs.k8s.io/controller-runtime/pkg/cluster"
36
38
"sigs.k8s.io/controller-runtime/pkg/log"
39
+ "sigs.k8s.io/controller-runtime/pkg/predicate"
37
40
38
41
mcmanager "sigs.k8s.io/multicluster-runtime/pkg/manager"
39
42
"sigs.k8s.io/multicluster-runtime/pkg/multicluster"
@@ -62,7 +65,6 @@ func New(opts Options) *Provider {
62
65
return & Provider {
63
66
opts : opts ,
64
67
log : log .Log .WithName ("kubeconfig-provider" ),
65
- client : nil , // Will be set in Run
66
68
clusters : map [string ]activeCluster {},
67
69
}
68
70
}
@@ -88,10 +90,10 @@ type index struct {
88
90
type Provider struct {
89
91
opts Options
90
92
log logr.Logger
91
- client client.Client
92
93
lock sync.RWMutex // protects everything below.
93
94
clusters map [string ]activeCluster
94
95
indexers []index
96
+ mgr mcmanager.Manager
95
97
}
96
98
97
99
type activeCluster struct {
@@ -113,83 +115,69 @@ func (p *Provider) Get(ctx context.Context, clusterName string) (cluster.Cluster
113
115
return nil , fmt .Errorf ("cluster %s not found" , clusterName )
114
116
}
115
117
116
- // Run starts the provider and blocks, watching for kubeconfig secrets .
117
- func (p * Provider ) Run (ctx context.Context , mgr mcmanager.Manager ) error {
118
+ // SetupWithManager sets up the provider with the manager .
119
+ func (p * Provider ) SetupWithManager (ctx context.Context , mgr mcmanager.Manager ) error {
118
120
log := p .log
119
121
log .Info ("Starting kubeconfig provider" , "options" , p .opts )
120
122
121
- // If client isn't set yet, get it from the manager
122
- if p .client == nil && mgr != nil {
123
- log .Info ("Setting client from manager" )
124
- p .client = mgr .GetLocalManager ().GetClient ()
125
- if p .client == nil {
126
- return fmt .Errorf ("failed to get client from manager" )
127
- }
123
+ if mgr == nil {
124
+ return fmt .Errorf ("manager is nil" )
128
125
}
126
+ p .mgr = mgr
129
127
130
- // Get the informer for secrets
131
- secretInf , err := mgr .GetLocalManager (). GetCache (). GetInformer ( ctx , & corev1. Secret {} )
132
- if err ! = nil {
133
- return fmt .Errorf ("failed to get secret informer: %w" , err )
128
+ // Get the local manager from the multicluster manager
129
+ localMgr := mgr .GetLocalManager ()
130
+ if localMgr = = nil {
131
+ return fmt .Errorf ("local manager is nil" )
134
132
}
135
133
136
- // Add event handlers for secrets
137
- if _ , err := secretInf .AddEventHandler (toolscache.FilteringResourceEventHandler {
138
- FilterFunc : func (obj interface {}) bool {
139
- secret , ok := obj .(* corev1.Secret )
140
- if ! ok {
141
- return false
142
- }
143
- // Only process secrets in our namespace with our label
144
- return secret .Namespace == p .opts .Namespace &&
145
- secret .Labels [p .opts .KubeconfigSecretLabel ] == "true"
146
- },
147
- Handler : toolscache.ResourceEventHandlerFuncs {
148
- AddFunc : func (obj interface {}) {
149
- secret := obj .(* corev1.Secret )
150
- log .Info ("Processing new secret" , "name" , secret .Name )
151
- if err := p .handleSecret (ctx , secret , mgr ); err != nil {
152
- log .Error (err , "Failed to handle secret" , "name" , secret .Name )
153
- }
154
- },
155
- UpdateFunc : func (oldObj , newObj interface {}) {
156
- secret := newObj .(* corev1.Secret )
157
- log .Info ("Processing updated secret" , "name" , secret .Name )
158
- if err := p .handleSecret (ctx , secret , mgr ); err != nil {
159
- log .Error (err , "Failed to handle secret" , "name" , secret .Name )
160
- }
161
- },
162
- DeleteFunc : func (obj interface {}) {
163
- secret := obj .(* corev1.Secret )
164
- log .Info ("Processing deleted secret" , "name" , secret .Name )
165
- p .handleSecretDelete (secret )
134
+ // Setup the controller to watch for secrets containing kubeconfig data
135
+ err := ctrl .NewControllerManagedBy (localMgr ).
136
+ For (& corev1.Secret {}, builder .WithPredicates (predicate .NewPredicateFuncs (
137
+ // Only watch for secrets in the configured namespace and with the configured label
138
+ func (obj client.Object ) bool {
139
+ return obj .GetNamespace () == p .opts .Namespace &&
140
+ obj .GetLabels ()[p .opts .KubeconfigSecretLabel ] == "true"
166
141
},
167
- },
168
- }); err != nil {
169
- return fmt .Errorf ("failed to add event handlers: %w" , err )
142
+ ))).
143
+ Complete (p )
144
+ if err != nil {
145
+ return fmt .Errorf ("failed to create controller: %w" , err )
170
146
}
171
147
172
- // Block until context is done
173
- <- ctx .Done ()
174
- log .Info ("Context cancelled, exiting provider" )
175
- return ctx .Err ()
148
+ return nil
176
149
}
177
150
178
- // handleSecret processes a secret containing kubeconfig data
179
- func (p * Provider ) handleSecret (ctx context.Context , secret * corev1.Secret , mgr mcmanager.Manager ) error {
180
- if secret == nil {
181
- return fmt .Errorf ("received nil secret" )
151
+ // Reconcile is the main controller function that reconciles secrets containing kubeconfig data
152
+ // when
153
+ func (p * Provider ) Reconcile (ctx context.Context , req ctrl.Request ) (ctrl.Result , error ) {
154
+ secret := & corev1.Secret {}
155
+ if err := p .mgr .GetLocalManager ().GetClient ().Get (ctx , req .NamespacedName , secret ); err != nil {
156
+ // If the secret is not found, remove the cluster and return.
157
+ // This is a normal occurence when the secret is deleted.
158
+ if apierrors .IsNotFound (err ) {
159
+ p .removeCluster (req .Name )
160
+ return ctrl.Result {}, nil
161
+ }
162
+ return ctrl.Result {}, fmt .Errorf ("failed to get secret: %w" , err )
182
163
}
183
164
184
165
// Extract name to use as cluster name
185
166
clusterName := secret .Name
186
167
log := p .log .WithValues ("cluster" , clusterName , "secret" , fmt .Sprintf ("%s/%s" , secret .Namespace , secret .Name ))
187
168
169
+ // If the secret is being deleted, remove the cluster and return.
170
+ // Will probably only hit this if there is a finalizer on the secret.
171
+ if secret .DeletionTimestamp != nil {
172
+ p .removeCluster (clusterName )
173
+ return ctrl.Result {}, nil
174
+ }
175
+
188
176
// Check if this secret has kubeconfig data
189
177
kubeconfigData , ok := secret .Data [p .opts .KubeconfigSecretKey ]
190
178
if ! ok {
191
179
log .Info ("Secret does not contain kubeconfig data" , "key" , p .opts .KubeconfigSecretKey )
192
- return nil
180
+ return ctrl. Result {}, nil
193
181
}
194
182
195
183
// Hash the kubeconfig
@@ -204,26 +192,24 @@ func (p *Provider) handleSecret(ctx context.Context, secret *corev1.Secret, mgr
204
192
if clusterExists {
205
193
if ac .Hash == hashStr {
206
194
log .Info ("Cluster already exists and has the same kubeconfig, skipping" )
207
- return nil
195
+ return ctrl. Result {}, nil
208
196
}
209
197
210
198
log .Info ("Cluster already exists, updating it" )
211
- if err := p .removeCluster (clusterName ); err != nil {
212
- return fmt .Errorf ("failed to remove existing cluster: %w" , err )
213
- }
199
+ p .removeCluster (clusterName )
214
200
}
215
201
216
202
// Parse the kubeconfig
217
203
restConfig , err := clientcmd .RESTConfigFromKubeConfig (kubeconfigData )
218
204
if err != nil {
219
- return fmt .Errorf ("failed to parse kubeconfig: %w" , err )
205
+ return ctrl. Result {}, fmt .Errorf ("failed to parse kubeconfig: %w" , err )
220
206
}
221
207
222
208
// Create a new cluster
223
209
log .Info ("Creating new cluster from kubeconfig" )
224
210
cl , err := cluster .New (restConfig )
225
211
if err != nil {
226
- return fmt .Errorf ("failed to create cluster: %w" , err )
212
+ return ctrl. Result {}, fmt .Errorf ("failed to create cluster: %w" , err )
227
213
}
228
214
229
215
// Copy indexers to avoid holding lock.
@@ -235,7 +221,7 @@ func (p *Provider) handleSecret(ctx context.Context, secret *corev1.Secret, mgr
235
221
// Apply any field indexers
236
222
for _ , idx := range indexers {
237
223
if err := cl .GetFieldIndexer ().IndexField (ctx , idx .object , idx .field , idx .extractValue ); err != nil {
238
- return fmt .Errorf ("failed to index field %q: %w" , idx .field , err )
224
+ return ctrl. Result {}, fmt .Errorf ("failed to index field %q: %w" , idx .field , err )
239
225
}
240
226
}
241
227
@@ -253,7 +239,7 @@ func (p *Provider) handleSecret(ctx context.Context, secret *corev1.Secret, mgr
253
239
log .Info ("Waiting for cluster cache to be ready" )
254
240
if ! cl .GetCache ().WaitForCacheSync (clusterCtx ) {
255
241
cancel () // Cancel context before returning error
256
- return fmt .Errorf ("failed to wait for cache sync" )
242
+ return ctrl. Result {}, fmt .Errorf ("failed to wait for cache sync" )
257
243
}
258
244
log .Info ("Cluster cache is ready" )
259
245
@@ -269,50 +255,32 @@ func (p *Provider) handleSecret(ctx context.Context, secret *corev1.Secret, mgr
269
255
270
256
log .Info ("Successfully added cluster" )
271
257
272
- // Engage the manager if provided
273
- if mgr != nil {
274
- if err := mgr .Engage (clusterCtx , clusterName , cl ); err != nil {
275
- log .Error (err , "Failed to engage manager, removing cluster" )
276
- p .lock .Lock ()
277
- delete (p .clusters , clusterName )
278
- p .lock .Unlock ()
279
- cancel () // Cancel the cluster context
280
- return fmt .Errorf ("failed to engage manager: %w" , err )
281
- }
282
- log .Info ("Successfully engaged manager" )
283
- }
284
-
285
- return nil
286
- }
287
-
288
- // handleSecretDelete handles the deletion of a secret
289
- func (p * Provider ) handleSecretDelete (secret * corev1.Secret ) {
290
- if secret == nil {
291
- return
258
+ // Engage the manager
259
+ if err := p .mgr .Engage (clusterCtx , clusterName , cl ); err != nil {
260
+ log .Error (err , "Failed to engage manager, removing cluster" )
261
+ p .lock .Lock ()
262
+ delete (p .clusters , clusterName )
263
+ p .lock .Unlock ()
264
+ cancel () // Cancel the cluster context
265
+ return ctrl.Result {}, fmt .Errorf ("failed to engage manager: %w" , err )
292
266
}
267
+ log .Info ("Successfully engaged manager" )
293
268
294
- clusterName := secret .Name
295
- log := p .log .WithValues ("cluster" , clusterName )
296
-
297
- log .Info ("Handling deleted secret" )
298
-
299
- // Remove the cluster
300
- if err := p .removeCluster (clusterName ); err != nil {
301
- log .Error (err , "Failed to remove cluster" )
302
- }
269
+ return ctrl.Result {}, nil
303
270
}
304
271
305
272
// removeCluster removes a cluster by name
306
- func (p * Provider ) removeCluster (clusterName string ) error {
273
+ func (p * Provider ) removeCluster (clusterName string ) {
307
274
log := p .log .WithValues ("cluster" , clusterName )
308
- log .Info ("Removing cluster" )
309
275
310
276
p .lock .Lock ()
311
277
ac , exists := p .clusters [clusterName ]
312
278
if ! exists {
313
279
p .lock .Unlock ()
314
- return fmt . Errorf ( "cluster not found" )
280
+ return
315
281
}
282
+
283
+ log .Info ("Removing cluster" )
316
284
delete (p .clusters , clusterName )
317
285
p .lock .Unlock ()
318
286
@@ -322,7 +290,6 @@ func (p *Provider) removeCluster(clusterName string) error {
322
290
log .Info ("Cancelled cluster context" )
323
291
324
292
log .Info ("Successfully removed cluster" )
325
- return nil
326
293
}
327
294
328
295
// IndexField indexes a field on all clusters, existing and future.
0 commit comments