Skip to content

Commit 1a49986

Browse files
author
qliang
committed
reconnect cluster when kubeconfig secret changed
1 parent 4df4094 commit 1a49986

File tree

4 files changed

+46
-1
lines changed

4 files changed

+46
-1
lines changed

controllers/clustercache/cluster_accessor.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,11 @@ type clusterAccessorHealthProbeConfig struct {
154154
// and health checking information (e.g. lastProbeSuccessTimestamp, consecutiveFailures).
155155
// lockedStateLock must be *always* held (via lock or rLock) before accessing this field.
156156
type clusterAccessorLockedState struct {
157+
// kubeconfigResourceVersion is the resource version of the kubeconfig secret.
158+
// This is used to detect if the kubeconfig secret has changed and we need to re-create the connection.
159+
// It is set when the connection is created.
160+
kubeconfigResourceVersion string
161+
157162
// lastConnectionCreationErrorTimestamp is the timestamp when connection creation failed the last time.
158163
lastConnectionCreationErrorTimestamp time.Time
159164

@@ -273,6 +278,12 @@ func (ca *clusterAccessor) Connect(ctx context.Context) (retErr error) {
273278

274279
log.Info("Connected")
275280

281+
kubeconfigSecret, err := ca.getKubeConfigSecret(ctx)
282+
if err != nil {
283+
return err
284+
}
285+
ca.lockedState.kubeconfigResourceVersion = kubeconfigSecret.ResourceVersion
286+
276287
// Only generate the clientCertificatePrivateKey once as there is no need to regenerate it after disconnect/connect.
277288
// Note: This has to be done before setting connection, because otherwise this code wouldn't be re-entrant if the
278289
// private key generation fails because we check Connected above.
@@ -414,6 +425,18 @@ func (ca *clusterAccessor) GetRESTConfig(ctx context.Context) (*rest.Config, err
414425
return ca.lockedState.connection.restConfig, nil
415426
}
416427

428+
func (ca *clusterAccessor) KubeConfigUpdated(ctx context.Context) (bool, error) {
429+
ca.rLock(ctx)
430+
defer ca.rUnlock(ctx)
431+
432+
kubeconfigSecret, err := ca.getKubeConfigSecret(ctx)
433+
if err != nil {
434+
return false, err
435+
}
436+
437+
return kubeconfigSecret.ResourceVersion != ca.lockedState.kubeconfigResourceVersion, nil
438+
}
439+
417440
func (ca *clusterAccessor) GetRESTConfigFromSecret(ctx context.Context) (*rest.Config, error) {
418441
ca.rLock(ctx)
419442
defer ca.rUnlock(ctx)

controllers/clustercache/cluster_accessor_client.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525

2626
"github.com/pkg/errors"
2727
corev1 "k8s.io/api/core/v1"
28+
v1 "k8s.io/api/core/v1"
2829
apierrors "k8s.io/apimachinery/pkg/api/errors"
2930
"k8s.io/apimachinery/pkg/api/meta"
3031
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -39,6 +40,7 @@ import (
3940
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
4041

4142
kcfg "sigs.k8s.io/cluster-api/util/kubeconfig"
43+
"sigs.k8s.io/cluster-api/util/secret"
4244
)
4345

4446
type createConnectionResult struct {
@@ -48,6 +50,14 @@ type createConnectionResult struct {
4850
Cache *stoppableCache
4951
}
5052

53+
func (ca *clusterAccessor) getKubeConfigSecret(ctx context.Context) (*v1.Secret, error) {
54+
kubeconfigSecret, err := secret.Get(ctx, ca.config.SecretClient, ca.cluster, secret.Kubeconfig)
55+
if err != nil {
56+
return nil, errors.Wrapf(err, "error getting kubeconfig secret")
57+
}
58+
return kubeconfigSecret, nil
59+
}
60+
5161
func (ca *clusterAccessor) createConnection(ctx context.Context) (*createConnectionResult, error) {
5262
log := ctrl.LoggerFrom(ctx)
5363
log.V(6).Info("Creating connection")

controllers/clustercache/cluster_cache.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,8 +460,20 @@ func (cc *clusterCache) Reconcile(ctx context.Context, req reconcile.Request) (r
460460

461461
requeueAfterDurations := []time.Duration{}
462462

463+
kubeconfigUpdated, err := accessor.KubeConfigUpdated(ctx)
464+
if err != nil {
465+
return reconcile.Result{}, errors.Wrapf(err, "error checking if kubeconfig was updated for cluster %s/%s", clusterKey.Namespace, clusterKey.Name)
466+
}
467+
463468
// Try to connect, if not connected.
464469
connected := accessor.Connected(ctx)
470+
if connected && kubeconfigUpdated {
471+
log.Info("Kubeconfig was updated, disconnecting to re-connect with the new kubeconfig")
472+
accessor.Disconnect(ctx)
473+
didDisconnect = true
474+
connected = false
475+
}
476+
465477
if !connected {
466478
lastConnectionCreationErrorTimestamp := accessor.GetLastConnectionCreationErrorTimestamp(ctx)
467479

controlplane/kubeadm/internal/cluster.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func (m *Management) GetMachinePoolsForCluster(ctx context.Context, cluster *clu
9999
func (m *Management) GetWorkloadCluster(ctx context.Context, clusterKey client.ObjectKey) (WorkloadCluster, error) {
100100
// TODO(chuckha): Inject this dependency.
101101
// TODO(chuckha): memoize this function. The workload client only exists as long as a reconciliation loop.
102-
restConfig, err := m.ClusterCache.GetRESTConfigFromSecret(ctx, clusterKey)
102+
restConfig, err := m.ClusterCache.GetRESTConfig(ctx, clusterKey)
103103
if err != nil {
104104
return nil, &RemoteClusterConnectionError{Name: clusterKey.String(), Err: err}
105105
}

0 commit comments

Comments
 (0)