Skip to content

Commit d2ad0cc

Browse files
authored
Merge pull request kubernetes#129956 from chrischdi/pr-kubeadm-cp-local-mode-fixes
kubeadm: Promote ControlPlaneKubeletLocalMode feature gate to beta - second attempt
2 parents 8b13078 + 6c093b1 commit d2ad0cc

File tree

6 files changed

+78
-60
lines changed

6 files changed

+78
-60
lines changed

cmd/kubeadm/app/cmd/join.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,9 @@ func (j *joinData) Client() (clientset.Interface, error) {
609609
AppendReactor(dryRun.GetKubeadmConfigReactor()).
610610
AppendReactor(dryRun.GetKubeadmCertsReactor()).
611611
AppendReactor(dryRun.GetKubeProxyConfigReactor()).
612-
AppendReactor(dryRun.GetKubeletConfigReactor())
612+
AppendReactor(dryRun.GetKubeletConfigReactor()).
613+
AppendReactor(dryRun.GetNodeReactor()).
614+
AppendReactor(dryRun.PatchNodeReactor())
613615

614616
j.client = dryRun.FakeClient()
615617
return j.client, nil

cmd/kubeadm/app/cmd/phases/join/kubelet.go

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -293,44 +293,55 @@ func runKubeletWaitBootstrapPhase(c workflow.RunData) (returnErr error) {
293293
return err
294294
}
295295

296-
bootstrapKubeConfigFile := filepath.Join(data.KubeConfigDir(), kubeadmconstants.KubeletBootstrapKubeConfigFileName)
297-
// Deletes the bootstrapKubeConfigFile, so the credential used for TLS bootstrap is removed from disk
298-
defer func() {
299-
_ = os.Remove(bootstrapKubeConfigFile)
300-
}()
301-
302-
// Apply patches to the in-memory kubelet configuration so that any configuration changes like kubelet healthz
303-
// address and port options are respected during the wait below. WriteConfigToDisk already applied patches to
304-
// the kubelet.yaml written to disk. This should be done after WriteConfigToDisk because both use the same config
305-
// in memory and we don't want patches to be applied two times to the config that is written to disk.
306-
if err := kubeletphase.ApplyPatchesToConfig(&initCfg.ClusterConfiguration, data.PatchesDir()); err != nil {
307-
return errors.Wrap(err, "could not apply patches to the in-memory kubelet configuration")
308-
}
296+
var client clientset.Interface
309297

310-
// Now the kubelet will perform the TLS Bootstrap, transforming /etc/kubernetes/bootstrap-kubelet.conf to /etc/kubernetes/kubelet.conf
311-
// Wait for the kubelet to create the /etc/kubernetes/kubelet.conf kubeconfig file. If this process
312-
// times out, display a somewhat user-friendly message.
313-
waiter := apiclient.NewKubeWaiter(nil, 0, os.Stdout)
314-
waiter.SetTimeout(cfg.Timeouts.KubeletHealthCheck.Duration)
315-
kubeletConfig := initCfg.ClusterConfiguration.ComponentConfigs[componentconfigs.KubeletGroup].Get()
316-
kubeletConfigTyped, ok := kubeletConfig.(*kubeletconfig.KubeletConfiguration)
317-
if !ok {
318-
return errors.New("could not convert the KubeletConfiguration to a typed object")
319-
}
320-
if err := waiter.WaitForKubelet(kubeletConfigTyped.HealthzBindAddress, *kubeletConfigTyped.HealthzPort); err != nil {
321-
fmt.Printf(kubeadmJoinFailMsg, err)
322-
return err
323-
}
298+
if data.DryRun() {
299+
fmt.Println("[kubelet-wait] Would wait for the kubelet to be bootstrapped")
324300

325-
if err := waitForTLSBootstrappedClient(cfg.Timeouts.TLSBootstrap.Duration); err != nil {
326-
fmt.Printf(kubeadmJoinFailMsg, err)
327-
return err
328-
}
301+
// Use the dry-run client.
302+
if client, err = data.Client(); err != nil {
303+
return errors.Wrap(err, "could not get client for dry-run")
304+
}
305+
} else {
306+
bootstrapKubeConfigFile := filepath.Join(data.KubeConfigDir(), kubeadmconstants.KubeletBootstrapKubeConfigFileName)
307+
// Deletes the bootstrapKubeConfigFile, so the credential used for TLS bootstrap is removed from disk
308+
defer func() {
309+
_ = os.Remove(bootstrapKubeConfigFile)
310+
}()
329311

330-
// When we know the /etc/kubernetes/kubelet.conf file is available, get the client
331-
client, err := kubeconfigutil.ClientSetFromFile(kubeadmconstants.GetKubeletKubeConfigPath())
332-
if err != nil {
333-
return err
312+
// Apply patches to the in-memory kubelet configuration so that any configuration changes like kubelet healthz
313+
// address and port options are respected during the wait below. WriteConfigToDisk already applied patches to
314+
// the kubelet.yaml written to disk. This should be done after WriteConfigToDisk because both use the same config
315+
// in memory and we don't want patches to be applied two times to the config that is written to disk.
316+
if err := kubeletphase.ApplyPatchesToConfig(&initCfg.ClusterConfiguration, data.PatchesDir()); err != nil {
317+
return errors.Wrap(err, "could not apply patches to the in-memory kubelet configuration")
318+
}
319+
320+
// Now the kubelet will perform the TLS Bootstrap, transforming /etc/kubernetes/bootstrap-kubelet.conf to /etc/kubernetes/kubelet.conf
321+
// Wait for the kubelet to create the /etc/kubernetes/kubelet.conf kubeconfig file. If this process
322+
// times out, display a somewhat user-friendly message.
323+
waiter := apiclient.NewKubeWaiter(nil, 0, os.Stdout)
324+
waiter.SetTimeout(cfg.Timeouts.KubeletHealthCheck.Duration)
325+
kubeletConfig := initCfg.ClusterConfiguration.ComponentConfigs[componentconfigs.KubeletGroup].Get()
326+
kubeletConfigTyped, ok := kubeletConfig.(*kubeletconfig.KubeletConfiguration)
327+
if !ok {
328+
return errors.New("could not convert the KubeletConfiguration to a typed object")
329+
}
330+
if err := waiter.WaitForKubelet(kubeletConfigTyped.HealthzBindAddress, *kubeletConfigTyped.HealthzPort); err != nil {
331+
fmt.Printf(kubeadmJoinFailMsg, err)
332+
return err
333+
}
334+
335+
if err := waitForTLSBootstrappedClient(cfg.Timeouts.TLSBootstrap.Duration); err != nil {
336+
fmt.Printf(kubeadmJoinFailMsg, err)
337+
return err
338+
}
339+
340+
// When we know the /etc/kubernetes/kubelet.conf file is available, get the client
341+
client, err = kubeconfigutil.ClientSetFromFile(kubeadmconstants.GetKubeletKubeConfigPath())
342+
if err != nil {
343+
return err
344+
}
334345
}
335346

336347
if !features.Enabled(initCfg.ClusterConfiguration.FeatureGates, features.NodeLocalCRISocket) {

cmd/kubeadm/app/cmd/phases/upgrade/apply/kubeconfig.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424

2525
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
2626
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
27-
"k8s.io/kubernetes/cmd/kubeadm/app/features"
2827
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
2928
)
3029

@@ -53,13 +52,11 @@ func runKubeconfig() func(c workflow.RunData) error {
5352

5453
cfg := data.InitCfg()
5554

56-
if features.Enabled(cfg.FeatureGates, features.ControlPlaneKubeletLocalMode) {
57-
if err := upgrade.UpdateKubeletLocalMode(cfg, data.DryRun()); err != nil {
58-
return errors.Wrap(err, "failed to update kubelet local mode")
59-
}
55+
if err := upgrade.UpdateKubeletKubeconfigServer(cfg, data.DryRun()); err != nil {
56+
return errors.Wrap(err, "failed to update kubelet local mode")
6057
}
6158

62-
fmt.Println("[upgrad/kubeconfig] The kubeconfig files for this node were successfully upgraded!")
59+
fmt.Println("[upgrade/kubeconfig] The kubeconfig files for this node were successfully upgraded!")
6360

6461
return nil
6562
}

cmd/kubeadm/app/cmd/phases/upgrade/node/kubeconfig.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424

2525
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
2626
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
27-
"k8s.io/kubernetes/cmd/kubeadm/app/features"
2827
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
2928
)
3029

@@ -60,10 +59,8 @@ func runKubeconfig() func(c workflow.RunData) error {
6059
// Otherwise, retrieve all the info required for kubeconfig upgrade.
6160
cfg := data.InitCfg()
6261

63-
if features.Enabled(cfg.FeatureGates, features.ControlPlaneKubeletLocalMode) {
64-
if err := upgrade.UpdateKubeletLocalMode(cfg, data.DryRun()); err != nil {
65-
return errors.Wrap(err, "failed to update kubelet local mode")
66-
}
62+
if err := upgrade.UpdateKubeletKubeconfigServer(cfg, data.DryRun()); err != nil {
63+
return errors.Wrap(err, "failed to update kubelet local mode")
6764
}
6865

6966
fmt.Println("[upgrade/kubeconfig] The kubeconfig files for this node were successfully upgraded!")

cmd/kubeadm/app/features/features.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const (
3636
RootlessControlPlane = "RootlessControlPlane"
3737
// WaitForAllControlPlaneComponents is expected to be alpha in v1.30
3838
WaitForAllControlPlaneComponents = "WaitForAllControlPlaneComponents"
39-
// ControlPlaneKubeletLocalMode is expected to be in alpha in v1.31, beta in v1.32
39+
// ControlPlaneKubeletLocalMode is expected to be in alpha in v1.31, beta in v1.33
4040
ControlPlaneKubeletLocalMode = "ControlPlaneKubeletLocalMode"
4141
// NodeLocalCRISocket is expected to be in alpha in v1.32, beta in v1.33, ga in v1.35
4242
NodeLocalCRISocket = "NodeLocalCRISocket"
@@ -54,7 +54,7 @@ var InitFeatureGates = FeatureList{
5454
" Once UserNamespacesSupport graduates to GA, kubeadm will start using it and RootlessControlPlane will be removed.",
5555
},
5656
WaitForAllControlPlaneComponents: {FeatureSpec: featuregate.FeatureSpec{Default: true, PreRelease: featuregate.Beta}},
57-
ControlPlaneKubeletLocalMode: {FeatureSpec: featuregate.FeatureSpec{Default: false, PreRelease: featuregate.Alpha}},
57+
ControlPlaneKubeletLocalMode: {FeatureSpec: featuregate.FeatureSpec{Default: true, PreRelease: featuregate.Beta}},
5858
NodeLocalCRISocket: {FeatureSpec: featuregate.FeatureSpec{Default: false, PreRelease: featuregate.Alpha}},
5959
}
6060

cmd/kubeadm/app/phases/upgrade/postupgrade.go

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -193,11 +193,11 @@ func WriteKubeletConfigFiles(cfg *kubeadmapi.InitConfiguration, kubeletConfigDir
193193
return nil
194194
}
195195

196-
// UpdateKubeletLocalMode changes the Server URL in the kubelets kubeconfig to the local API endpoint if it is currently
197-
// set to the ControlPlaneEndpoint.
198-
// TODO: remove this function once kubeletKubeConfigFilePath goes GA and is hardcoded to enabled by default:
196+
// UpdateKubeletKubeconfigServer changes the Server URL in the kubelets kubeconfig if necessary depending on the
197+
// ControlPlaneKubeletLocalMode feature gate.
198+
// TODO: remove this function once ControlPlaneKubeletLocalMode goes GA and is hardcoded to be enabled by default:
199199
// https://github.com/kubernetes/kubeadm/issues/2271
200-
func UpdateKubeletLocalMode(cfg *kubeadmapi.InitConfiguration, dryRun bool) error {
200+
func UpdateKubeletKubeconfigServer(cfg *kubeadmapi.InitConfiguration, dryRun bool) error {
201201
kubeletKubeConfigFilePath := filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.KubeletKubeConfigFileName)
202202

203203
if _, err := os.Stat(kubeletKubeConfigFilePath); err != nil {
@@ -228,19 +228,30 @@ func UpdateKubeletLocalMode(cfg *kubeadmapi.InitConfiguration, dryRun bool) erro
228228
return err
229229
}
230230

231-
// Skip changing kubeconfig file if Server does not match the ControlPlaneEndpoint.
232-
if config.Clusters[configContext.Cluster].Server != controlPlaneAPIEndpoint || controlPlaneAPIEndpoint == localAPIEndpoint {
233-
klog.V(2).Infof("Skipping update of the Server URL in %s, because it's already not equal to %q or already matches the localAPIEndpoint", kubeletKubeConfigFilePath, cfg.ControlPlaneEndpoint)
234-
return nil
231+
var targetServer string
232+
if features.Enabled(cfg.FeatureGates, features.ControlPlaneKubeletLocalMode) {
233+
// Skip changing kubeconfig file if Server does not match the ControlPlaneEndpoint.
234+
if config.Clusters[configContext.Cluster].Server != controlPlaneAPIEndpoint || controlPlaneAPIEndpoint == localAPIEndpoint {
235+
klog.V(2).Infof("Skipping update of the Server URL in %s, because it's already not equal to %q or already matches the localAPIEndpoint", kubeletKubeConfigFilePath, controlPlaneAPIEndpoint)
236+
return nil
237+
}
238+
targetServer = localAPIEndpoint
239+
} else {
240+
// Skip changing kubeconfig file if Server does not match the localAPIEndpoint.
241+
if config.Clusters[configContext.Cluster].Server != localAPIEndpoint {
242+
klog.V(2).Infof("Skipping update of the Server URL in %s, because it already matches the controlPlaneAPIEndpoint", kubeletKubeConfigFilePath)
243+
return nil
244+
}
245+
targetServer = controlPlaneAPIEndpoint
235246
}
236247

237248
if dryRun {
238-
fmt.Printf("[dryrun] Would change the Server URL from %q to %q in %s and try to restart kubelet\n", config.Clusters[configContext.Cluster].Server, localAPIEndpoint, kubeletKubeConfigFilePath)
249+
fmt.Printf("[dryrun] Would change the Server URL from %q to %q in %s and try to restart kubelet\n", config.Clusters[configContext.Cluster].Server, targetServer, kubeletKubeConfigFilePath)
239250
return nil
240251
}
241252

242-
klog.V(1).Infof("Changing the Server URL from %q to %q in %s", config.Clusters[configContext.Cluster].Server, localAPIEndpoint, kubeletKubeConfigFilePath)
243-
config.Clusters[configContext.Cluster].Server = localAPIEndpoint
253+
klog.V(1).Infof("Changing the Server URL from %q to %q in %s", config.Clusters[configContext.Cluster].Server, targetServer, kubeletKubeConfigFilePath)
254+
config.Clusters[configContext.Cluster].Server = targetServer
244255

245256
if err := clientcmd.WriteToFile(*config, kubeletKubeConfigFilePath); err != nil {
246257
return err

0 commit comments

Comments
 (0)