@@ -183,7 +183,7 @@ func NewClusterCacheTracker(manager ctrl.Manager, options ClusterCacheTrackerOpt
183
183
func (t * ClusterCacheTracker ) GetClient (ctx context.Context , cluster client.ObjectKey ) (client.Client , error ) {
184
184
accessor , err := t .getClusterAccessor (ctx , cluster )
185
185
if err != nil {
186
- return nil , err
186
+ return nil , errors . Wrapf ( err , "failed to get client" )
187
187
}
188
188
189
189
return accessor .client , nil
@@ -198,7 +198,7 @@ func (t *ClusterCacheTracker) GetReader(ctx context.Context, cluster client.Obje
198
198
func (t * ClusterCacheTracker ) GetRESTConfig (ctc context.Context , cluster client.ObjectKey ) (* rest.Config , error ) {
199
199
accessor , err := t .getClusterAccessor (ctc , cluster )
200
200
if err != nil {
201
- return nil , err
201
+ return nil , errors . Wrapf ( err , "failed to get REST config" )
202
202
}
203
203
204
204
return accessor .config , nil
@@ -208,7 +208,7 @@ func (t *ClusterCacheTracker) GetRESTConfig(ctc context.Context, cluster client.
208
208
func (t * ClusterCacheTracker ) GetEtcdClientCertificateKey (ctx context.Context , cluster client.ObjectKey ) (* rsa.PrivateKey , error ) {
209
209
accessor , err := t .getClusterAccessor (ctx , cluster )
210
210
if err != nil {
211
- return nil , err
211
+ return nil , errors . Wrapf ( err , "failed to get etcd client certificate key" )
212
212
}
213
213
214
214
return accessor .etcdClientCertificateKey , nil
@@ -267,7 +267,7 @@ func (t *ClusterCacheTracker) getClusterAccessor(ctx context.Context, cluster cl
267
267
// for the cluster at the same time.
268
268
// Return an error if another go routine already tries to create a clusterAccessor.
269
269
if ok := t .clusterLock .TryLock (cluster ); ! ok {
270
- return nil , errors .Wrapf (ErrClusterLocked , "failed to create cluster accessor: failed to get lock for cluster" )
270
+ return nil , errors .Wrapf (ErrClusterLocked , "failed to create cluster accessor: failed to get lock for cluster (probably because another worker is trying to create the client at the moment) " )
271
271
}
272
272
defer t .clusterLock .Unlock (cluster )
273
273
@@ -305,7 +305,7 @@ func (t *ClusterCacheTracker) newClusterAccessor(ctx context.Context, cluster cl
305
305
}
306
306
307
307
// Create a http client and a mapper for the cluster.
308
- httpClient , mapper , err := t .createHTTPClientAndMapper (config , cluster )
308
+ httpClient , mapper , restClient , err := t .createHTTPClientAndMapper (ctx , config , cluster )
309
309
if err != nil {
310
310
return nil , errors .Wrapf (err , "error creating http client and mapper for remote cluster %q" , cluster .String ())
311
311
}
@@ -337,7 +337,7 @@ func (t *ClusterCacheTracker) newClusterAccessor(ctx context.Context, cluster cl
337
337
config .Host = inClusterConfig .Host
338
338
339
339
// Update the http client and the mapper to use in-cluster config.
340
- httpClient , mapper , err = t .createHTTPClientAndMapper (config , cluster )
340
+ httpClient , mapper , restClient , err = t .createHTTPClientAndMapper (ctx , config , cluster )
341
341
if err != nil {
342
342
return nil , errors .Wrapf (err , "error creating http client and mapper (using in-cluster config) for remote cluster %q" , cluster .String ())
343
343
}
@@ -348,7 +348,7 @@ func (t *ClusterCacheTracker) newClusterAccessor(ctx context.Context, cluster cl
348
348
}
349
349
350
350
// Create a client and a cache for the cluster.
351
- cachedClient , err := t .createCachedClient (ctx , config , cluster , httpClient , mapper )
351
+ cachedClient , err := t .createCachedClient (ctx , config , cluster , httpClient , restClient , mapper )
352
352
if err != nil {
353
353
return nil , err
354
354
}
@@ -397,28 +397,40 @@ func (t *ClusterCacheTracker) runningOnWorkloadCluster(ctx context.Context, c cl
397
397
}
398
398
399
399
// createHTTPClientAndMapper creates a http client and a dynamic rest mapper for the given cluster, based on the rest.Config.
400
- func (t * ClusterCacheTracker ) createHTTPClientAndMapper (config * rest.Config , cluster client.ObjectKey ) (* http.Client , meta.RESTMapper , error ) {
400
+ func (t * ClusterCacheTracker ) createHTTPClientAndMapper (ctx context. Context , config * rest.Config , cluster client.ObjectKey ) (* http.Client , meta.RESTMapper , * rest. RESTClient , error ) {
401
401
// Create a http client for the cluster.
402
402
httpClient , err := rest .HTTPClientFor (config )
403
403
if err != nil {
404
- return nil , nil , errors .Wrapf (err , "error creating client for remote cluster %q: error creating http client" , cluster .String ())
404
+ return nil , nil , nil , errors .Wrapf (err , "error creating client for remote cluster %q: error creating http client" , cluster .String ())
405
405
}
406
406
407
407
// Create a mapper for it
408
408
mapper , err := apiutil .NewDynamicRESTMapper (config , httpClient )
409
409
if err != nil {
410
- return nil , nil , errors .Wrapf (err , "error creating client for remote cluster %q: error creating dynamic rest mapper" , cluster .String ())
410
+ return nil , nil , nil , errors .Wrapf (err , "error creating client for remote cluster %q: error creating dynamic rest mapper" , cluster .String ())
411
+ }
412
+
413
+ // Create a REST client for the cluster (this is later used for health checking as well).
414
+ codec := runtime.NoopEncoder {Decoder : scheme .Codecs .UniversalDecoder ()}
415
+ restClientConfig := rest .CopyConfig (config )
416
+ restClientConfig .NegotiatedSerializer = serializer .NegotiatedSerializerWrapper (runtime.SerializerInfo {Serializer : codec })
417
+ restClient , err := rest .UnversionedRESTClientForConfigAndClient (restClientConfig , httpClient )
418
+ if err != nil {
419
+ return nil , nil , nil , errors .Wrapf (err , "error creating client for remote cluster %q: error creating REST client" , cluster .String ())
420
+ }
421
+
422
+ // Note: This checks if the apiserver is up. We do this already here to produce a clearer error message if the cluster is unreachable.
423
+ if _ , err := restClient .Get ().AbsPath ("/" ).Timeout (healthCheckRequestTimeout ).DoRaw (ctx ); err != nil {
424
+ return nil , nil , nil , errors .Wrapf (err , "error creating client for remote cluster %q: cluster is not reachable" , cluster .String ())
411
425
}
412
426
413
427
// Verify if we can get a rest mapping from the workload cluster apiserver.
414
- // Note: This also checks if the apiserver is up in general. We do this already here
415
- // to avoid further effort creating a cache and a client and to produce a clearer error message.
416
428
_ , err = mapper .RESTMapping (corev1 .SchemeGroupVersion .WithKind ("Node" ).GroupKind (), corev1 .SchemeGroupVersion .Version )
417
429
if err != nil {
418
- return nil , nil , errors .Wrapf (err , "error creating client for remote cluster %q: error getting rest mapping" , cluster .String ())
430
+ return nil , nil , nil , errors .Wrapf (err , "error creating client for remote cluster %q: error getting rest mapping" , cluster .String ())
419
431
}
420
432
421
- return httpClient , mapper , nil
433
+ return httpClient , mapper , restClient , nil
422
434
}
423
435
424
436
// createUncachedClient creates an uncached client for the given cluster, based on the rest.Config.
@@ -442,7 +454,7 @@ type cachedClientOutput struct {
442
454
}
443
455
444
456
// createCachedClient creates a cached client for the given cluster, based on a rest.Config.
445
- func (t * ClusterCacheTracker ) createCachedClient (ctx context.Context , config * rest.Config , cluster client.ObjectKey , httpClient * http.Client , mapper meta.RESTMapper ) (* cachedClientOutput , error ) {
457
+ func (t * ClusterCacheTracker ) createCachedClient (ctx context.Context , config * rest.Config , cluster client.ObjectKey , httpClient * http.Client , restClient * rest. RESTClient , mapper meta.RESTMapper ) (* cachedClientOutput , error ) {
446
458
// Create the cache for the remote cluster
447
459
cacheOptions := cache.Options {
448
460
HTTPClient : httpClient ,
@@ -504,8 +516,7 @@ func (t *ClusterCacheTracker) createCachedClient(ctx context.Context, config *re
504
516
// Start cluster healthcheck!!!
505
517
go t .healthCheckCluster (cacheCtx , & healthCheckInput {
506
518
cluster : cluster ,
507
- cfg : config ,
508
- httpClient : httpClient ,
519
+ restClient : restClient ,
509
520
})
510
521
511
522
return & cachedClientOutput {
@@ -568,13 +579,13 @@ func (t *ClusterCacheTracker) Watch(ctx context.Context, input WatchInput) error
568
579
569
580
accessor , err := t .getClusterAccessor (ctx , input .Cluster )
570
581
if err != nil {
571
- return errors .Wrapf (err , "failed to add %s watch on cluster %s" , input .Kind , klog .KRef (input .Cluster .Namespace , input .Cluster .Name ))
582
+ return errors .Wrapf (err , "failed to add %T watch on cluster %s" , input .Kind , klog .KRef (input .Cluster .Namespace , input .Cluster .Name ))
572
583
}
573
584
574
585
// We have to lock the cluster, so that the watch is not created multiple times in parallel.
575
586
ok := t .clusterLock .TryLock (input .Cluster )
576
587
if ! ok {
577
- return errors .Wrapf (ErrClusterLocked , "failed to add %T watch on cluster %s: failed to get lock for cluster" , input .Kind , klog .KRef (input .Cluster .Namespace , input .Cluster .Name ))
588
+ return errors .Wrapf (ErrClusterLocked , "failed to add %T watch on cluster %s: failed to get lock for cluster (probably because another worker is trying to create the client at the moment) " , input .Kind , klog .KRef (input .Cluster .Namespace , input .Cluster .Name ))
578
589
}
579
590
defer t .clusterLock .Unlock (input .Cluster )
580
591
@@ -586,7 +597,7 @@ func (t *ClusterCacheTracker) Watch(ctx context.Context, input WatchInput) error
586
597
587
598
// Need to create the watch
588
599
if err := input .Watcher .Watch (source .Kind (accessor .cache , input .Kind , input .EventHandler , input .Predicates ... )); err != nil {
589
- return errors .Wrapf (err , "failed to add %s watch on cluster %s: failed to create watch" , input .Kind , klog .KRef (input .Cluster .Namespace , input .Cluster .Name ))
600
+ return errors .Wrapf (err , "failed to add %T watch on cluster %s: failed to create watch" , input .Kind , klog .KRef (input .Cluster .Namespace , input .Cluster .Name ))
590
601
}
591
602
592
603
accessor .watches .Insert (input .Name )
@@ -597,8 +608,7 @@ func (t *ClusterCacheTracker) Watch(ctx context.Context, input WatchInput) error
597
608
// healthCheckInput provides the input for the healthCheckCluster method.
598
609
type healthCheckInput struct {
599
610
cluster client.ObjectKey
600
- httpClient * http.Client
601
- cfg * rest.Config
611
+ restClient * rest.RESTClient
602
612
interval time.Duration
603
613
requestTimeout time.Duration
604
614
unhealthyThreshold int
@@ -630,18 +640,7 @@ func (t *ClusterCacheTracker) healthCheckCluster(ctx context.Context, in *health
630
640
631
641
unhealthyCount := 0
632
642
633
- // This gets us a client that can make raw http(s) calls to the remote apiserver. We only need to create it once
634
- // and we can reuse it inside the polling loop.
635
- codec := runtime.NoopEncoder {Decoder : scheme .Codecs .UniversalDecoder ()}
636
- cfg := rest .CopyConfig (in .cfg )
637
- cfg .NegotiatedSerializer = serializer .NegotiatedSerializerWrapper (runtime.SerializerInfo {Serializer : codec })
638
- restClient , restClientErr := rest .UnversionedRESTClientForConfigAndClient (cfg , in .httpClient )
639
-
640
643
runHealthCheckWithThreshold := func (ctx context.Context ) (bool , error ) {
641
- if restClientErr != nil {
642
- return false , restClientErr
643
- }
644
-
645
644
cluster := & clusterv1.Cluster {}
646
645
if err := t .client .Get (ctx , in .cluster , cluster ); err != nil {
647
646
if apierrors .IsNotFound (err ) {
@@ -672,7 +671,7 @@ func (t *ClusterCacheTracker) healthCheckCluster(ctx context.Context, in *health
672
671
673
672
// An error here means there was either an issue connecting or the API returned an error.
674
673
// If no error occurs, reset the unhealthy counter.
675
- _ , err := restClient .Get ().AbsPath (in .path ).Timeout (in .requestTimeout ).DoRaw (ctx )
674
+ _ , err := in . restClient .Get ().AbsPath (in .path ).Timeout (in .requestTimeout ).DoRaw (ctx )
676
675
if err != nil {
677
676
if apierrors .IsUnauthorized (err ) {
678
677
// Unauthorized means that the underlying kubeconfig is not authorizing properly anymore, which
0 commit comments