Skip to content

Commit

Permalink
Unregister prom gauges when recycling cluster watcher (#11875)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
alpeb authored Jan 6, 2024
1 parent 55d1049 commit 7b2b01d
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 5 deletions.
20 changes: 16 additions & 4 deletions controller/k8s/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@ type API struct {
l5dCrdSharedInformers l5dcrdinformer.SharedInformerFactory
}

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

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

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

// NewNamespacedAPI takes a Kubernetes client and returns an initialized API scoped to namespace.
// NewNamespacedAPI takes a Kubernetes client and returns an initialized API
// scoped to namespace. This creates informers on each one of resources passed,
// registering metrics on each one; don't forget to call UnregisterGauges() on
// the returned API reference to clean them up!
func NewNamespacedAPI(
k8sClient kubernetes.Interface,
dynamicClient dynamic.Interface,
Expand Down
3 changes: 2 additions & 1 deletion multicluster/cmd/service-mirror/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ func restartClusterWatcher(
if err != nil {
return fmt.Errorf("unable to parse kube config: %w", err)
}
clusterWatcher, err = servicemirror.NewRemoteClusterServiceWatcher(
cw, err := servicemirror.NewRemoteClusterServiceWatcher(
ctx,
namespace,
controllerK8sAPI,
Expand All @@ -317,6 +317,7 @@ func restartClusterWatcher(
if err != nil {
return fmt.Errorf("unable to create cluster watcher: %w", err)
}
clusterWatcher = cw
err = clusterWatcher.Start(ctx)
if err != nil {
return fmt.Errorf("failed to start cluster watcher: %w", err)
Expand Down
1 change: 1 addition & 0 deletions multicluster/service-mirror/cluster_watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ func NewRemoteClusterServiceWatcher(
}
_, err = remoteAPI.Client.Discovery().ServerVersion()
if err != nil {
remoteAPI.UnregisterGauges()
return nil, fmt.Errorf("cannot connect to api for target cluster %s: %w", clusterName, err)
}

Expand Down

0 comments on commit 7b2b01d

Please sign in to comment.