@@ -32,6 +32,8 @@ import (
32
32
ctrl "sigs.k8s.io/controller-runtime"
33
33
"sigs.k8s.io/controller-runtime/pkg/cache"
34
34
"sigs.k8s.io/controller-runtime/pkg/client"
35
+ "sigs.k8s.io/controller-runtime/pkg/handler"
36
+ "sigs.k8s.io/controller-runtime/pkg/predicate"
35
37
"sigs.k8s.io/controller-runtime/pkg/source"
36
38
37
39
"sigs.k8s.io/cluster-api/util/certs"
@@ -410,13 +412,13 @@ func (ca *clusterAccessor) GetClientCertificatePrivateKey(ctx context.Context) *
410
412
// Each unique watch (by input.Name) is only added once after a Connect (otherwise we return early).
411
413
// During a disconnect existing watches (i.e. informers) are shutdown when stopping the cache.
412
414
// After a re-connect watches will be re-added (assuming the Watch method is called again).
413
- func (ca * clusterAccessor ) Watch (ctx context.Context , input WatchInput ) error {
414
- if input .Name == "" {
415
- return errors .New ("input .Name is required " )
415
+ func (ca * clusterAccessor ) Watch (ctx context.Context , watcher Watcher ) error {
416
+ if watcher .Name () == "" {
417
+ return errors .New ("watcher .Name() cannot be empty " )
416
418
}
417
419
418
420
if ! ca .Connected (ctx ) {
419
- return errors .Wrapf (ErrClusterNotConnected , "error creating watch %s for %T " , input .Name , input . Kind )
421
+ return errors .Wrapf (ErrClusterNotConnected , "error creating watch %s for %s " , watcher .Name (), watcher . KindType () )
420
422
}
421
423
422
424
log := ctrl .LoggerFrom (ctx )
@@ -429,21 +431,21 @@ func (ca *clusterAccessor) Watch(ctx context.Context, input WatchInput) error {
429
431
430
432
// Checking connection again while holding the lock, because maybe Disconnect was called since checking above.
431
433
if ca .lockedState .connection == nil {
432
- return errors .Wrapf (ErrClusterNotConnected , "error creating watch %s for %T " , input .Name , input . Kind )
434
+ return errors .Wrapf (ErrClusterNotConnected , "error creating watch %s for %s " , watcher .Name (), watcher . KindType () )
433
435
}
434
436
435
437
// Return early if the watch was already added.
436
- if ca .lockedState .connection .watches .Has (input .Name ) {
437
- log .V (6 ).Info (fmt .Sprintf ("Skip creation of watch %s for %T because it already exists" , input .Name , input . Kind ))
438
+ if ca .lockedState .connection .watches .Has (watcher .Name () ) {
439
+ log .V (6 ).Info (fmt .Sprintf ("Skip creation of watch %s for %s because it already exists" , watcher .Name (), watcher . KindType () ))
438
440
return nil
439
441
}
440
442
441
- log .Info (fmt .Sprintf ("Creating watch %s for %T " , input .Name , input . Kind ))
442
- if err := input . Watcher . Watch (source . Kind ( ca .lockedState .connection .cache , input . Kind , input . EventHandler , input . Predicates ... ) ); err != nil {
443
- return errors .Wrapf (err , "error creating watch %s for %T " , input .Name , input . Kind )
443
+ log .Info (fmt .Sprintf ("Creating watch %s for %s " , watcher .Name (), watcher . KindType () ))
444
+ if err := watcher . Watch (ca .lockedState .connection .cache ); err != nil {
445
+ return errors .Wrapf (err , "error creating watch %s for %s " , watcher .Name (), watcher . KindType () )
444
446
}
445
447
446
- ca .lockedState .connection .watches .Insert (input .Name )
448
+ ca .lockedState .connection .watches .Insert (watcher .Name () )
447
449
return nil
448
450
}
449
451
@@ -495,3 +497,80 @@ func (ca *clusterAccessor) unlock(ctx context.Context) {
495
497
ca .lockedStateLock .Unlock ()
496
498
log .V (10 ).Info ("Removed lock for ClusterAccessor" )
497
499
}
500
+
501
+ // ScopedWatcher is a scoped-down interface from Controller that only has the Watch func.
502
+ type ScopedWatcher [request comparable ] interface {
503
+ Watch (src source.TypedSource [request ]) error
504
+ }
505
+
506
+ // WatchInput specifies the parameters used to establish a new watch for a workload cluster.
507
+ // A source.TypedKind source (configured with Kind, TypedEventHandler and Predicates) will be added to the Watcher.
508
+ // To watch for events, the source.TypedKind will create an informer on the Cache that we have created and cached
509
+ // for the given Cluster.
510
+ type WatchInput = TypedWatchInput [client.Object , ctrl.Request ]
511
+
512
+ // TypedWatchInput specifies the parameters used to establish a new watch for a workload cluster.
513
+ // A source.TypedKind source (configured with Kind, TypedEventHandler and Predicates) will be added to the Watcher.
514
+ // To watch for events, the source.TypedKind will create an informer on the Cache that we have created and cached
515
+ // for the given Cluster.
516
+ type TypedWatchInput [object client.Object , request comparable ] struct {
517
+ // Name represents a unique Watch request for the specified Cluster.
518
+ // The name is used to track that a specific watch is only added once to a cache.
519
+ // After a connection (and thus also the cache) has been re-created, watches have to be added
520
+ // again by calling the Watch method again.
521
+ Name string
522
+
523
+ // Watcher is the watcher (controller) whose Reconcile() function will be called for events.
524
+ Watcher ScopedWatcher [request ]
525
+
526
+ // Kind is the type of resource to watch.
527
+ Kind object
528
+
529
+ // EventHandler contains the event handlers to invoke for resource events.
530
+ EventHandler handler.TypedEventHandler [object , request ]
531
+
532
+ // Predicates is used to filter resource events.
533
+ Predicates []predicate.TypedPredicate [object ]
534
+ }
535
+
536
+ // NewWatcher creates a Watcher on the workload cluster.
537
+ // A source.TypedKind source (configured with Kind, TypedEventHandler and Predicates) will be added to the ScopedWatcher.
538
+ // To watch for events, the source.TypedKind will create an informer on the Cache that we have created and cached
539
+ // for the given Cluster.
540
+ func NewWatcher (input WatchInput ) Watcher {
541
+ return & typedWatcher [client.Object , ctrl.Request ]{
542
+ name : input .Name ,
543
+ kind : input .Kind ,
544
+ eventHandler : input .EventHandler ,
545
+ predicates : input .Predicates ,
546
+ watcher : input .Watcher ,
547
+ }
548
+ }
549
+
550
+ // NewTypedWatcher creates a Watcher on the workload cluster.
551
+ // A source.TypedKind source (configured with Kind, TypedEventHandler and Predicates) will be added to the ScopedWatcher.
552
+ // To watch for events, the source.TypedKind will create an informer on the Cache that we have created and cached
553
+ // for the given Cluster.
554
+ func NewTypedWatcher [object client.Object , request comparable ](input TypedWatchInput [object , request ]) Watcher {
555
+ return & typedWatcher [object , request ]{
556
+ name : input .Name ,
557
+ kind : input .Kind ,
558
+ eventHandler : input .EventHandler ,
559
+ predicates : input .Predicates ,
560
+ watcher : input .Watcher ,
561
+ }
562
+ }
563
+
564
+ type typedWatcher [object client.Object , request comparable ] struct {
565
+ name string
566
+ kind object
567
+ eventHandler handler.TypedEventHandler [object , request ]
568
+ predicates []predicate.TypedPredicate [object ]
569
+ watcher ScopedWatcher [request ]
570
+ }
571
+
572
+ func (tw * typedWatcher [object , request ]) Name () string { return tw .name }
573
+ func (tw * typedWatcher [object , request ]) KindType () string { return fmt .Sprintf ("%T" , tw .kind ) }
574
+ func (tw * typedWatcher [object , request ]) Watch (cache cache.Cache ) error {
575
+ return tw .watcher .Watch (source .TypedKind [object , request ](cache , tw .kind , tw .eventHandler , tw .predicates ... ))
576
+ }
0 commit comments