k8s.io/kubernetes@v1.29.3/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  			framework.ExpectNoError(os.WriteFile(filepath.Join(configDir, "10-kubelet.conf"), contents, 0755))
    75  			contents = []byte(`apiVersion: kubelet.config.k8s.io/v1beta1
    76  kind: KubeletConfiguration
    77  clusterDNS:
    78  - 192.168.1.1
    79  - 192.168.1.5
    80  - 192.168.1.8
    81  port: 8080
    82  cpuManagerReconcilePeriod: 0s
    83  systemReserved:
    84    memory: 2Gi`)
    85  			framework.ExpectNoError(os.WriteFile(filepath.Join(configDir, "20-kubelet.conf"), contents, 0755))
    86  			ginkgo.By("Restarting the kubelet")
    87  			restartKubelet()
    88  			// wait until the kubelet health check will succeed
    89  			gomega.Eventually(ctx, func() bool {
    90  				return kubeletHealthCheck(kubeletHealthCheckURL)
    91  			}, f.Timeouts.PodStart, f.Timeouts.Poll).Should(gomega.BeTrue())
    92  
    93  			mergedConfig, err := getCurrentKubeletConfig(ctx)
    94  			framework.ExpectNoError(err)
    95  
    96  			// Replace specific fields in the initial configuration with expectedConfig values
    97  			initialConfig.Port = int32(8080)                  // not overridden by second file, should be retained.
    98  			initialConfig.ReadOnlyPort = int32(10257)         // overridden by second file.
    99  			initialConfig.SystemReserved = map[string]string{ // overridden by map in second file.
   100  				"memory": "2Gi",
   101  			}
   102  			initialConfig.ClusterDNS = []string{"192.168.1.1", "192.168.1.5", "192.168.1.8"} // overridden by slice in second file.
   103  			// This value was explicitly set in the drop-in, make sure it is retained
   104  			initialConfig.CPUManagerReconcilePeriod = metav1.Duration{Duration: time.Duration(0)}
   105  			// Meanwhile, this value was not explicitly set, but could have been overridden by a "default" of 0 for the type.
   106  			// Ensure the true default persists.
   107  			initialConfig.CPUCFSQuotaPeriod = metav1.Duration{Duration: time.Duration(100000000)}
   108  			// Compare the expected config with the merged config
   109  			gomega.Expect(initialConfig).To(gomega.BeComparableTo(mergedConfig), "Merged kubelet config does not match the expected configuration.")
   110  		})
   111  	})
   112  
   113  })