Skip to content

Commit b14769f

Browse files
authored
Merge pull request kubernetes#126224 from neolit123/1.31-fix-bug-in-join-patches-healthz
kubeadm: fix join bug where kubeletconfig was not patched in memory
2 parents 90a8470 + b90b280 commit b14769f

File tree

4 files changed

+105
-2
lines changed

4 files changed

+105
-2
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,14 @@ func runKubeletWaitBootstrapPhase(c workflow.RunData) (returnErr error) {
281281
_ = os.Remove(bootstrapKubeConfigFile)
282282
}()
283283

284+
// Apply patches to the in-memory kubelet configuration so that any configuration changes like kubelet healthz
285+
// address and port options are respected during the wait below. WriteConfigToDisk already applied patches to
286+
// the kubelet.yaml written to disk. This should be done after WriteConfigToDisk because both use the same config
287+
// in memory and we don't want patches to be applied two times to the config that is written to disk.
288+
if err := kubeletphase.ApplyPatchesToConfig(&initCfg.ClusterConfiguration, data.PatchesDir()); err != nil {
289+
return errors.Wrap(err, "could not apply patches to the in-memory kubelet configuration")
290+
}
291+
284292
// Now the kubelet will perform the TLS Bootstrap, transforming /etc/kubernetes/bootstrap-kubelet.conf to /etc/kubernetes/kubelet.conf
285293
// Wait for the kubelet to create the /etc/kubernetes/kubelet.conf kubeconfig file. If this process
286294
// times out, display a somewhat user-friendly message.

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

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,13 @@ import (
3434
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
3535
"k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
3636
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
37+
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
3738
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
3839
"k8s.io/kubernetes/cmd/kubeadm/app/util/patches"
3940
)
4041

42+
var applyKubeletConfigPatchesFunc = applyKubeletConfigPatches
43+
4144
// WriteConfigToDisk writes the kubelet config object down to a file
4245
// Used at "kubeadm init" and "kubeadm upgrade" time
4346
func WriteConfigToDisk(cfg *kubeadmapi.ClusterConfiguration, kubeletDir, patchesDir string, output io.Writer) error {
@@ -57,7 +60,7 @@ func WriteConfigToDisk(cfg *kubeadmapi.ClusterConfiguration, kubeletDir, patches
5760

5861
// Apply patches to the KubeletConfiguration
5962
if len(patchesDir) != 0 {
60-
kubeletBytes, err = applyKubeletConfigPatches(kubeletBytes, patchesDir, output)
63+
kubeletBytes, err = applyKubeletConfigPatchesFunc(kubeletBytes, patchesDir, output)
6164
if err != nil {
6265
return errors.Wrap(err, "could not apply patches to the KubeletConfiguration")
6366
}
@@ -66,6 +69,42 @@ func WriteConfigToDisk(cfg *kubeadmapi.ClusterConfiguration, kubeletDir, patches
6669
return writeConfigBytesToDisk(kubeletBytes, kubeletDir)
6770
}
6871

72+
// ApplyPatchesToConfig applies the patches located in patchesDir to the KubeletConfiguration stored
73+
// in the ClusterConfiguration.ComponentConfigs map.
74+
func ApplyPatchesToConfig(cfg *kubeadmapi.ClusterConfiguration, patchesDir string) error {
75+
kubeletCfg, ok := cfg.ComponentConfigs[componentconfigs.KubeletGroup]
76+
if !ok {
77+
return errors.New("no kubelet component config found")
78+
}
79+
80+
if err := kubeletCfg.Mutate(); err != nil {
81+
return err
82+
}
83+
84+
kubeletBytes, err := kubeletCfg.Marshal()
85+
if err != nil {
86+
return err
87+
}
88+
89+
// Apply patches to the KubeletConfiguration. Output is discarded.
90+
if len(patchesDir) != 0 {
91+
kubeletBytes, err = applyKubeletConfigPatchesFunc(kubeletBytes, patchesDir, io.Discard)
92+
if err != nil {
93+
return errors.Wrap(err, "could not apply patches to the KubeletConfiguration")
94+
}
95+
}
96+
97+
gvkmap, err := kubeadmutil.SplitYAMLDocuments(kubeletBytes)
98+
if err != nil {
99+
return err
100+
}
101+
if err := kubeletCfg.Unmarshal(gvkmap); err != nil {
102+
return err
103+
}
104+
105+
return nil
106+
}
107+
69108
// CreateConfigMap creates a ConfigMap with the generic kubelet configuration.
70109
// Used at "kubeadm init" and "kubeadm upgrade" time
71110
func CreateConfigMap(cfg *kubeadmapi.ClusterConfiguration, client clientset.Interface) error {

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package kubelet
1818

1919
import (
2020
"bytes"
21+
"fmt"
2122
"io"
2223
"os"
2324
"path/filepath"
@@ -28,7 +29,11 @@ import (
2829
"k8s.io/apimachinery/pkg/runtime"
2930
"k8s.io/client-go/kubernetes/fake"
3031
core "k8s.io/client-go/testing"
32+
kubeletconfig "k8s.io/kubelet/config/v1beta1"
33+
"k8s.io/utils/ptr"
3134

35+
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
36+
"k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
3237
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
3338
)
3439

@@ -103,3 +108,49 @@ func TestApplyKubeletConfigPatches(t *testing.T) {
103108
t.Fatalf("expected output:\n%s\ngot\n%s\n", expectedOutput, output)
104109
}
105110
}
111+
112+
func TestApplyPatchesToConfig(t *testing.T) {
113+
const (
114+
expectedAddress = "barfoo"
115+
expectedPort = 4321
116+
)
117+
118+
kc := &kubeletconfig.KubeletConfiguration{
119+
HealthzBindAddress: "foobar",
120+
HealthzPort: ptr.To[int32](1234),
121+
}
122+
123+
cfg := &kubeadmapi.ClusterConfiguration{}
124+
cfg.ComponentConfigs = kubeadmapi.ComponentConfigMap{}
125+
126+
localAPIEndpoint := &kubeadmapi.APIEndpoint{}
127+
nodeRegOps := &kubeadmapi.NodeRegistrationOptions{}
128+
componentconfigs.Default(cfg, localAPIEndpoint, nodeRegOps)
129+
cfg.ComponentConfigs[componentconfigs.KubeletGroup].Set(kc)
130+
131+
// Change to a fake function that does patching with string replace.
132+
applyKubeletConfigPatchesFunc = func(b []byte, _ string, _ io.Writer) ([]byte, error) {
133+
b = bytes.ReplaceAll(b, []byte("foobar"), []byte(expectedAddress))
134+
b = bytes.ReplaceAll(b, []byte("1234"), []byte(fmt.Sprintf("%d", expectedPort)))
135+
return b, nil
136+
}
137+
defer func() {
138+
applyKubeletConfigPatchesFunc = applyKubeletConfigPatches
139+
}()
140+
141+
if err := ApplyPatchesToConfig(cfg, "fakedir"); err != nil {
142+
t.Fatalf("unexpected error: %v", err)
143+
}
144+
145+
new := cfg.ComponentConfigs[componentconfigs.KubeletGroup].Get()
146+
newTyped, ok := new.(*kubeletconfig.KubeletConfiguration)
147+
if !ok {
148+
t.Fatal("could not cast kubelet config")
149+
}
150+
if newTyped.HealthzBindAddress != expectedAddress {
151+
t.Fatalf("expected address: %s, got: %s", expectedAddress, newTyped.HealthzBindAddress)
152+
}
153+
if *newTyped.HealthzPort != expectedPort {
154+
t.Fatalf("expected port: %d, got: %d", expectedPort, *newTyped.HealthzPort)
155+
}
156+
}

cmd/kubeadm/app/util/apiclient/wait.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,12 @@ func (w *KubeWaiter) WaitForKubelet(healthzAddress string, healthzPort int32) er
250250
healthzEndpoint = fmt.Sprintf("http://%s:%d/healthz", healthzAddress, healthzPort)
251251
)
252252

253-
fmt.Printf("[kubelet-check] Waiting for a healthy kubelet. This can take up to %v\n", w.timeout)
253+
if healthzPort == 0 {
254+
fmt.Println("[kubelet-check] Skipping the kubelet health check because the healthz port is set to 0")
255+
return nil
256+
}
257+
fmt.Printf("[kubelet-check] Waiting for a healthy kubelet at %s. This can take up to %v\n",
258+
healthzEndpoint, w.timeout)
254259

255260
formatError := func(cause string) error {
256261
return errors.Errorf("The HTTP call equal to 'curl -sSL %s' returned %s\n",

0 commit comments

Comments
 (0)