Skip to content

Commit 7b2b01d

Browse files
authored
Unregister prom gauges when recycling cluster watcher (#11875)
Unregister prom gauges when recycling cluster watcher Fixes #11839 When in `restartClusterWatcher` we fail to connect to the target cluster for whatever reason, the function gets called again 10s later, and tries to register the same prometheus metrics without unregistering them first, which generates warnings. The problem lies in `NewRemoteClusterServiceWatcher`, which instantiates the remote kube-api client and registers the metrics, returning a nil object if the client can't connect. `cleanupWorkers` at the beginning of `restartClusterWatcher` won't unregister those metrics because of that nil object. To fix this, gauges are unregistered on error.
1 parent 55d1049 commit 7b2b01d

File tree

3 files changed

+19
-5
lines changed

3 files changed

+19
-5
lines changed

controller/k8s/api.go

+16-4
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,10 @@ type API struct {
6767
l5dCrdSharedInformers l5dcrdinformer.SharedInformerFactory
6868
}
6969

70-
// InitializeAPI creates Kubernetes clients and returns an initialized API wrapper.
70+
// InitializeAPI creates Kubernetes clients and returns an initialized API
71+
// wrapper. This creates informers on each one of resources passed, registering
72+
// metrics on each one; don't forget to call UnregisterGauges() on the returned
73+
// API reference to clean them up!
7174
func InitializeAPI(ctx context.Context, kubeConfig string, ensureClusterWideAccess bool, cluster string, resources ...APIResource) (*API, error) {
7275
config, err := k8s.GetConfig(kubeConfig, "")
7376
if err != nil {
@@ -87,7 +90,10 @@ func InitializeAPI(ctx context.Context, kubeConfig string, ensureClusterWideAcce
8790
return initAPI(ctx, k8sClient, dynamicClient, config, ensureClusterWideAccess, cluster, resources...)
8891
}
8992

90-
// InitializeAPIForConfig creates Kubernetes clients and returns an initialized API wrapper.
93+
// InitializeAPIForConfig creates Kubernetes clients and returns an initialized
94+
// API wrapper. This creates informers on each one of resources passed,
95+
// registering metrics on each one; don't forget to call UnregisterGauges() on
96+
// the returned API reference to clean them up!
9197
func InitializeAPIForConfig(ctx context.Context, kubeConfig *rest.Config, ensureClusterWideAccess bool, cluster string, resources ...APIResource) (*API, error) {
9298
k8sClient, err := k8s.NewAPIForConfig(kubeConfig, "", []string{}, 0, 0, 0)
9399
if err != nil {
@@ -141,7 +147,10 @@ func initAPI(ctx context.Context, k8sClient *k8s.KubernetesAPI, dynamicClient dy
141147
return api, nil
142148
}
143149

144-
// NewClusterScopedAPI takes a Kubernetes client and returns an initialized cluster-wide API.
150+
// NewClusterScopedAPI takes a Kubernetes client and returns an initialized
151+
// cluster-wide API. This creates informers on each one of resources passed,
152+
// registering metrics on each one; don't forget to call UnregisterGauges() on
153+
// the returned API reference to clean them up!
145154
func NewClusterScopedAPI(
146155
k8sClient kubernetes.Interface,
147156
dynamicClient dynamic.Interface,
@@ -153,7 +162,10 @@ func NewClusterScopedAPI(
153162
return newAPI(k8sClient, dynamicClient, l5dCrdClient, sharedInformers, cluster, resources...)
154163
}
155164

156-
// NewNamespacedAPI takes a Kubernetes client and returns an initialized API scoped to namespace.
165+
// NewNamespacedAPI takes a Kubernetes client and returns an initialized API
166+
// scoped to namespace. This creates informers on each one of resources passed,
167+
// registering metrics on each one; don't forget to call UnregisterGauges() on
168+
// the returned API reference to clean them up!
157169
func NewNamespacedAPI(
158170
k8sClient kubernetes.Interface,
159171
dynamicClient dynamic.Interface,

multicluster/cmd/service-mirror/main.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ func restartClusterWatcher(
303303
if err != nil {
304304
return fmt.Errorf("unable to parse kube config: %w", err)
305305
}
306-
clusterWatcher, err = servicemirror.NewRemoteClusterServiceWatcher(
306+
cw, err := servicemirror.NewRemoteClusterServiceWatcher(
307307
ctx,
308308
namespace,
309309
controllerK8sAPI,
@@ -317,6 +317,7 @@ func restartClusterWatcher(
317317
if err != nil {
318318
return fmt.Errorf("unable to create cluster watcher: %w", err)
319319
}
320+
clusterWatcher = cw
320321
err = clusterWatcher.Start(ctx)
321322
if err != nil {
322323
return fmt.Errorf("failed to start cluster watcher: %w", err)

multicluster/service-mirror/cluster_watcher.go

+1
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ func NewRemoteClusterServiceWatcher(
175175
}
176176
_, err = remoteAPI.Client.Discovery().ServerVersion()
177177
if err != nil {
178+
remoteAPI.UnregisterGauges()
178179
return nil, fmt.Errorf("cannot connect to api for target cluster %s: %w", clusterName, err)
179180
}
180181

0 commit comments

Comments
 (0)