Commit 3cd7a46b authored by Brad Davidson's avatar Brad Davidson Committed by Brad Davidson

Defer ensuring node passwords on etcd-only nodes during initial cluster bootstrap

This allows secondary etcd nodes to bootstrap the kubelet before an apiserver joins the cluster. Rancher waits for all the etcd nodes to come up before adding the control-plane nodes, so this needs to be handled properly. Signed-off-by: 's avatarBrad Davidson <brad.davidson@rancher.com> (cherry picked from commit 38706eee)
parent e750ee50
...@@ -332,8 +332,9 @@ type nodePassBootstrapper func(req *http.Request) (string, int, error) ...@@ -332,8 +332,9 @@ type nodePassBootstrapper func(req *http.Request) (string, int, error)
func passwordBootstrap(ctx context.Context, config *Config) nodePassBootstrapper { func passwordBootstrap(ctx context.Context, config *Config) nodePassBootstrapper {
runtime := config.ControlConfig.Runtime runtime := config.ControlConfig.Runtime
deferredNodes := map[string]bool{}
var secretClient coreclient.SecretClient var secretClient coreclient.SecretClient
var once sync.Once var mu sync.Mutex
return nodePassBootstrapper(func(req *http.Request) (string, int, error) { return nodePassBootstrapper(func(req *http.Request) (string, int, error) {
nodeName, nodePassword, err := getNodeInfo(req) nodeName, nodePassword, err := getNodeInfo(req)
...@@ -347,7 +348,10 @@ func passwordBootstrap(ctx context.Context, config *Config) nodePassBootstrapper ...@@ -347,7 +348,10 @@ func passwordBootstrap(ctx context.Context, config *Config) nodePassBootstrapper
secretClient = runtime.Core.Core().V1().Secret() secretClient = runtime.Core.Core().V1().Secret()
} else if nodeName == os.Getenv("NODE_NAME") { } else if nodeName == os.Getenv("NODE_NAME") {
// or verify the password locally and ensure a secret later // or verify the password locally and ensure a secret later
return verifyLocalPassword(ctx, config, &once, nodeName, nodePassword) return verifyLocalPassword(ctx, config, &mu, deferredNodes, nodeName, nodePassword)
} else if config.ControlConfig.DisableAPIServer {
// or defer node password verification until an apiserver joins the cluster
return verifyRemotePassword(ctx, config, &mu, deferredNodes, nodeName, nodePassword)
} else { } else {
// or reject the request until the core is ready // or reject the request until the core is ready
return "", http.StatusServiceUnavailable, errors.New("runtime core not ready") return "", http.StatusServiceUnavailable, errors.New("runtime core not ready")
...@@ -362,7 +366,7 @@ func passwordBootstrap(ctx context.Context, config *Config) nodePassBootstrapper ...@@ -362,7 +366,7 @@ func passwordBootstrap(ctx context.Context, config *Config) nodePassBootstrapper
}) })
} }
func verifyLocalPassword(ctx context.Context, config *Config, once *sync.Once, nodeName, nodePassword string) (string, int, error) { func verifyLocalPassword(ctx context.Context, config *Config, mu *sync.Mutex, deferredNodes map[string]bool, nodeName, nodePassword string) (string, int, error) {
// use same password file location that the agent creates // use same password file location that the agent creates
nodePasswordRoot := "/" nodePasswordRoot := "/"
if config.Rootless { if config.Rootless {
...@@ -381,29 +385,46 @@ func verifyLocalPassword(ctx context.Context, config *Config, once *sync.Once, n ...@@ -381,29 +385,46 @@ func verifyLocalPassword(ctx context.Context, config *Config, once *sync.Once, n
return "", http.StatusForbidden, errors.Wrapf(err, "unable to verify local password for node '%s'", nodeName) return "", http.StatusForbidden, errors.Wrapf(err, "unable to verify local password for node '%s'", nodeName)
} }
// make sure the secret is created when the api server is ready mu.Lock()
ensureSecret := func() { defer mu.Unlock()
runtime := config.ControlConfig.Runtime
for { if _, ok := deferredNodes[nodeName]; !ok {
select { deferredNodes[nodeName] = true
case <-ctx.Done(): go ensureSecret(ctx, config, nodeName, nodePassword)
return logrus.Debugf("Password verified locally for node '%s'", nodeName)
case <-time.After(1 * time.Second):
if runtime.Core != nil {
logrus.Debugf("runtime core has become available, ensuring password secret for node '%s'", nodeName)
secretClient := runtime.Core.Core().V1().Secret()
if err := nodepassword.Ensure(secretClient, nodeName, nodePassword); err != nil {
logrus.Warnf("error ensuring node password secret for pre-validated node '%s': %v", nodeName, err)
}
return
}
}
}
} }
go once.Do(ensureSecret) return nodeName, http.StatusOK, nil
}
logrus.Debugf("password verified locally for node '%s'", nodeName) func verifyRemotePassword(ctx context.Context, config *Config, mu *sync.Mutex, deferredNodes map[string]bool, nodeName, nodePassword string) (string, int, error) {
mu.Lock()
defer mu.Unlock()
if _, ok := deferredNodes[nodeName]; !ok {
deferredNodes[nodeName] = true
go ensureSecret(ctx, config, nodeName, nodePassword)
logrus.Debugf("Password verification deferred for node '%s'", nodeName)
}
return nodeName, http.StatusOK, nil return nodeName, http.StatusOK, nil
} }
func ensureSecret(ctx context.Context, config *Config, nodeName, nodePassword string) {
runtime := config.ControlConfig.Runtime
for {
select {
case <-ctx.Done():
return
case <-time.After(1 * time.Second):
if runtime.Core != nil {
logrus.Debugf("Runtime core has become available, ensuring password secret for node '%s'", nodeName)
secretClient := runtime.Core.Core().V1().Secret()
if err := nodepassword.Ensure(secretClient, nodeName, nodePassword); err != nil {
logrus.Warnf("Error ensuring node password secret for pre-validated node '%s': %v", nodeName, err)
}
return
}
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment