Skip to content
This repository was archived by the owner on Jul 30, 2021. It is now read-only.

Commit a43cb19

Browse files
committed
🐛 refresh token for provisioning machines
1 parent 7aea998 commit a43cb19

File tree

3 files changed

+58
-13
lines changed

3 files changed

+58
-13
lines changed

controllers/kubeadmconfig_controller.go

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,6 @@ func (r *KubeadmConfigReconciler) Reconcile(req ctrl.Request) (_ ctrl.Result, re
100100
return ctrl.Result{}, err
101101
}
102102

103-
// bail super early if it's already ready
104-
if config.Status.Ready {
105-
log.Info("ignoring an already ready config")
106-
return ctrl.Result{}, nil
107-
}
108-
109103
// Look up the Machine that owns this KubeConfig if there is one
110104
machine, err := util.GetOwnerMachine(ctx, r.Client, config.ObjectMeta)
111105
if err != nil {
@@ -118,8 +112,14 @@ func (r *KubeadmConfigReconciler) Reconcile(req ctrl.Request) (_ ctrl.Result, re
118112
}
119113
log = log.WithValues("machine-name", machine.Name)
120114

121-
// Ignore machines that already have bootstrap data
122-
if machine.Spec.Bootstrap.Data != nil {
115+
// bail super early if it's already ready
116+
if config.Status.Ready && machine.Status.InfrastructureReady {
117+
log.Info("ignoring config for an already ready machine")
118+
return ctrl.Result{}, nil
119+
}
120+
121+
// Ignore machines that already have bootstrap data we didn't generate
122+
if machine.Spec.Bootstrap.Data != nil && !config.Status.Ready {
123123
// TODO: mark the config as ready?
124124
return ctrl.Result{}, nil
125125
}
@@ -140,6 +140,23 @@ func (r *KubeadmConfigReconciler) Reconcile(req ctrl.Request) (_ ctrl.Result, re
140140
return ctrl.Result{}, err
141141
}
142142

143+
// If we've already embedded a time-limited join token into a config, but are still waiting for the token to be used, refresh it
144+
if config.Status.Ready {
145+
token := config.Spec.JoinConfiguration.Discovery.BootstrapToken.Token
146+
147+
// gets the remote secret interface client for the current cluster
148+
secretsClient, err := r.SecretsClientFactory.NewSecretsClient(r.Client, cluster)
149+
if err != nil {
150+
return ctrl.Result{}, err
151+
}
152+
153+
err = refreshToken(secretsClient, token)
154+
if err != nil {
155+
// It would be nice to re-create the bootstrap token if the error was "not found", but we have no way to update the Machine's bootstrap data
156+
return ctrl.Result{}, errors.Wrapf(err, "failed to refresh bootstrap token")
157+
}
158+
}
159+
143160
// Wait patiently for the infrastructure to be ready
144161
if !cluster.Status.InfrastructureReady {
145162
log.Info("Infrastructure is not ready, waiting until ready.")

controllers/token.go

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ import (
3030
"sigs.k8s.io/controller-runtime/pkg/client"
3131
)
3232

33-
const (
34-
defaultTokenTTL = 10 * time.Minute
33+
var (
34+
// DefaultTokenTTL is the amount of time a bootstrap token (and therefore a KubeadmConfig) will be valid
35+
DefaultTokenTTL = 10 * time.Minute
3536
)
3637

3738
// ClusterSecretsClientFactory support creation of secrets client for clusters
@@ -76,7 +77,7 @@ func createToken(client corev1.SecretInterface) (string, error) {
7677
Data: map[string][]byte{
7778
bootstrapapi.BootstrapTokenIDKey: []byte(tokenID),
7879
bootstrapapi.BootstrapTokenSecretKey: []byte(tokenSecret),
79-
bootstrapapi.BootstrapTokenExpirationKey: []byte(time.Now().UTC().Add(defaultTokenTTL).Format(time.RFC3339)),
80+
bootstrapapi.BootstrapTokenExpirationKey: []byte(time.Now().UTC().Add(DefaultTokenTTL).Format(time.RFC3339)),
8081
bootstrapapi.BootstrapTokenUsageSigningKey: []byte("true"),
8182
bootstrapapi.BootstrapTokenUsageAuthentication: []byte("true"),
8283
bootstrapapi.BootstrapTokenExtraGroupsKey: []byte("system:bootstrappers:kubeadm:default-node-token"),
@@ -89,3 +90,26 @@ func createToken(client corev1.SecretInterface) (string, error) {
8990
}
9091
return token, nil
9192
}
93+
94+
// refreshToken extends the TTL for an existing token
95+
func refreshToken(client corev1.SecretInterface, token string) error {
96+
substrs := bootstraputil.BootstrapTokenRegexp.FindStringSubmatch(token)
97+
if len(substrs) != 3 {
98+
return errors.Errorf("the bootstrap token %q was not of the form %q", token, bootstrapapi.BootstrapTokenPattern)
99+
}
100+
tokenID := substrs[1]
101+
102+
secretName := bootstraputil.BootstrapTokenSecretName(tokenID)
103+
secret, err := client.Get(secretName, metav1.GetOptions{})
104+
if err != nil {
105+
return err
106+
}
107+
108+
if secret.Data == nil {
109+
return errors.Errorf("Invalid bootstrap secret %q, remove the token from the kubadm config to re-create", secretName)
110+
}
111+
secret.Data[bootstrapapi.BootstrapTokenExpirationKey] = []byte(time.Now().UTC().Add(DefaultTokenTTL).Format(time.RFC3339))
112+
113+
_, err = client.Update(secret)
114+
return err
115+
}

main.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ func main() {
7373
flag.DurationVar(
7474
&syncPeriod,
7575
"sync-period",
76-
10*time.Minute,
77-
"The minimum interval at which watched resources are reconciled (e.g. 10m)",
76+
9*time.Minute,
77+
"The minimum interval at which watched resources are reconciled (e.g. 9m)",
7878
)
7979

8080
flag.StringVar(
@@ -88,6 +88,10 @@ func main() {
8888

8989
ctrl.SetLogger(klogr.New())
9090

91+
if controllers.DefaultTokenTTL-syncPeriod < 1*time.Minute {
92+
setupLog.Info("warning: the sync interval is close to the configured token TTL, tokens may expire temporarily before being refreshed")
93+
}
94+
9195
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
9296
Scheme: scheme,
9397
MetricsBindAddress: metricsAddr,

0 commit comments

Comments
 (0)