k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e_node/kubelet_config_dir_test.go (about) 1 /* 2 Copyright 2023 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package e2enode 18 19 import ( 20 "context" 21 "os" 22 "path/filepath" 23 "time" 24 25 "github.com/onsi/ginkgo/v2" 26 "github.com/onsi/gomega" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config" 29 "k8s.io/kubernetes/test/e2e/framework" 30 "k8s.io/kubernetes/test/e2e/nodefeature" 31 ) 32 33 var _ = SIGDescribe("Kubelet Config", framework.WithSlow(), framework.WithSerial(), framework.WithDisruptive(), nodefeature.KubeletConfigDropInDir, func() { 34 f := framework.NewDefaultFramework("kubelet-config-drop-in-dir-test") 35 ginkgo.Context("when merging drop-in configs", func() { 36 var oldcfg *kubeletconfig.KubeletConfiguration 37 ginkgo.BeforeEach(func(ctx context.Context) { 38 var err error 39 oldcfg, err = getCurrentKubeletConfig(ctx) 40 framework.ExpectNoError(err) 41 }) 42 ginkgo.AfterEach(func(ctx context.Context) { 43 files, err := filepath.Glob(filepath.Join(framework.TestContext.KubeletConfigDropinDir, "*"+".conf")) 44 framework.ExpectNoError(err) 45 for _, file := range files { 46 err := os.Remove(file) 47 framework.ExpectNoError(err) 48 } 49 updateKubeletConfig(ctx, f, oldcfg, true) 50 }) 51 ginkgo.It("should merge kubelet configs correctly", func(ctx context.Context) { 52 // Get the initial kubelet configuration 53 initialConfig, err := getCurrentKubeletConfig(ctx) 54 framework.ExpectNoError(err) 55 56 ginkgo.By("Stopping the kubelet") 57 restartKubelet := stopKubelet() 58 59 // wait until the kubelet health check will fail 60 gomega.Eventually(ctx, func() bool { 61 return kubeletHealthCheck(kubeletHealthCheckURL) 62 }, f.Timeouts.PodStart, f.Timeouts.Poll).Should(gomega.BeFalse()) 63 64 configDir := framework.TestContext.KubeletConfigDropinDir 65 66 contents := []byte(`apiVersion: kubelet.config.k8s.io/v1beta1 67 kind: KubeletConfiguration 68 port: 10255 69 readOnlyPort: 10257 70 clusterDNS: 71 - 192.168.1.10 72 systemReserved: 73 memory: 1Gi 74 authorization: 75 mode: Webhook 76 webhook: 77 cacheAuthorizedTTL: "5m" 78 cacheUnauthorizedTTL: "30s" 79 staticPodURLHeader: 80 kubelet-api-support: 81 - "Authorization: 234APSDFA" 82 - "X-Custom-Header: 123" 83 custom-static-pod: 84 - "Authorization: 223EWRWER" 85 - "X-Custom-Header: 456" 86 shutdownGracePeriodByPodPriority: 87 - priority: 1 88 shutdownGracePeriodSeconds: 60 89 - priority: 2 90 shutdownGracePeriodSeconds: 45 91 - priority: 3 92 shutdownGracePeriodSeconds: 30 93 featureGates: 94 DisableKubeletCloudCredentialProviders: true 95 PodAndContainerStatsFromCRI: true`) 96 framework.ExpectNoError(os.WriteFile(filepath.Join(configDir, "10-kubelet.conf"), contents, 0755)) 97 contents = []byte(`apiVersion: kubelet.config.k8s.io/v1beta1 98 kind: KubeletConfiguration 99 clusterDNS: 100 - 192.168.1.1 101 - 192.168.1.5 102 - 192.168.1.8 103 port: 8080 104 cpuManagerReconcilePeriod: 0s 105 systemReserved: 106 memory: 2Gi 107 authorization: 108 mode: Webhook 109 webhook: 110 cacheAuthorizedTTL: "6m" 111 cacheUnauthorizedTTL: "40s" 112 staticPodURLHeader: 113 kubelet-api-support: 114 - "Authorization: 8945AFSG1" 115 - "X-Custom-Header: 987" 116 custom-static-pod: 117 - "Authorization: 223EWRWER" 118 - "X-Custom-Header: 345" 119 shutdownGracePeriodByPodPriority: 120 - priority: 1 121 shutdownGracePeriodSeconds: 19 122 - priority: 2 123 shutdownGracePeriodSeconds: 41 124 - priority: 6 125 shutdownGracePeriodSeconds: 30 126 featureGates: 127 PodAndContainerStatsFromCRI: false 128 DynamicResourceAllocation: true`) 129 framework.ExpectNoError(os.WriteFile(filepath.Join(configDir, "20-kubelet.conf"), contents, 0755)) 130 ginkgo.By("Restarting the kubelet") 131 restartKubelet() 132 // wait until the kubelet health check will succeed 133 gomega.Eventually(ctx, func() bool { 134 return kubeletHealthCheck(kubeletHealthCheckURL) 135 }, f.Timeouts.PodStart, f.Timeouts.Poll).Should(gomega.BeTrue()) 136 137 mergedConfig, err := getCurrentKubeletConfig(ctx) 138 framework.ExpectNoError(err) 139 140 // Replace specific fields in the initial configuration with expectedConfig values 141 initialConfig.Port = int32(8080) // not overridden by second file, should be retained. 142 initialConfig.ReadOnlyPort = int32(10257) // overridden by second file. 143 initialConfig.SystemReserved = map[string]string{ // overridden by map in second file. 144 "memory": "2Gi", 145 } 146 initialConfig.ClusterDNS = []string{"192.168.1.1", "192.168.1.5", "192.168.1.8"} // overridden by slice in second file. 147 // This value was explicitly set in the drop-in, make sure it is retained 148 initialConfig.CPUManagerReconcilePeriod = metav1.Duration{Duration: time.Duration(0)} 149 // Meanwhile, this value was not explicitly set, but could have been overridden by a "default" of 0 for the type. 150 // Ensure the true default persists. 151 initialConfig.CPUCFSQuotaPeriod = metav1.Duration{Duration: time.Duration(100000000)} 152 // This covers the case for a map with the list of values. 153 initialConfig.StaticPodURLHeader = map[string][]string{ 154 "kubelet-api-support": {"Authorization: 8945AFSG1", "X-Custom-Header: 987"}, 155 "custom-static-pod": {"Authorization: 223EWRWER", "X-Custom-Header: 345"}, 156 } 157 // This covers the case where the fields within the list of structs are overridden. 158 initialConfig.ShutdownGracePeriodByPodPriority = []kubeletconfig.ShutdownGracePeriodByPodPriority{ 159 {Priority: 1, ShutdownGracePeriodSeconds: 19}, 160 {Priority: 2, ShutdownGracePeriodSeconds: 41}, 161 {Priority: 6, ShutdownGracePeriodSeconds: 30}, 162 } 163 // This covers the case where the fields within the struct are overridden. 164 initialConfig.Authorization = kubeletconfig.KubeletAuthorization{ 165 Mode: "Webhook", 166 Webhook: kubeletconfig.KubeletWebhookAuthorization{ 167 CacheAuthorizedTTL: metav1.Duration{Duration: time.Duration(6 * time.Minute)}, 168 CacheUnauthorizedTTL: metav1.Duration{Duration: time.Duration(40 * time.Second)}, 169 }, 170 } 171 // This covers the case where the fields within the map are overridden. 172 initialConfig.FeatureGates = map[string]bool{"DisableKubeletCloudCredentialProviders": true, "PodAndContainerStatsFromCRI": false, "DynamicResourceAllocation": true} 173 // Compare the expected config with the merged config 174 gomega.Expect(initialConfig).To(gomega.BeComparableTo(mergedConfig), "Merged kubelet config does not match the expected configuration.") 175 }) 176 }) 177 178 })