From 6f2eeed2e860bc219530246c75064fe5df646af3 Mon Sep 17 00:00:00 2001 From: Claudiu Belu Date: Mon, 23 May 2022 16:53:00 +0300 Subject: [PATCH] unittests: Fixes unit tests for Windows Currently, there are some unit tests that are failing on Windows due to various reasons: - config options not supported on Windows. - files not closed, which means that they cannot be removed / renamed. - paths not properly joined (filepath.Join should be used). - time.Now() is not as precise on Windows, which means that 2 consecutive calls may return the same timestamp. - different error messages on Windows. - files have \r\n line endings on Windows. - /tmp directory being used, which might not exist on Windows. Instead, the OS-specific Temp directory should be used. - the default value for Kubelet's EvictionHard field was containing OS-specific fields. This is now moved, the field is now set during Kubelet's initialization, after the config file is read. --- cmd/kubelet/app/server.go | 7 +++++ pkg/kubelet/apis/config/fuzzer/fuzzer.go | 3 ++- .../KubeletConfiguration/after/v1beta1.yaml | 5 ---- .../roundtrip/default/v1beta1.yaml | 5 ---- pkg/kubelet/apis/config/types.go | 1 + pkg/kubelet/apis/config/v1beta1/defaults.go | 3 --- .../apis/config/v1beta1/defaults_test.go | 4 +-- .../validation/validation_others_test.go | 25 ++++++++++++++++++ .../apis/config/validation/validation_test.go | 4 +-- .../validation/validation_windows_test.go | 26 +++++++++++++++++++ .../certificate/bootstrap/bootstrap_test.go | 21 +++++++++------ pkg/kubelet/cm/cpumanager/cpu_manager_test.go | 4 +-- pkg/kubelet/cm/devicemanager/endpoint_test.go | 9 ++++--- .../cm/memorymanager/memory_manager_test.go | 2 +- .../v1beta1 => eviction}/defaults_linux.go | 2 +- .../v1beta1 => eviction}/defaults_others.go | 2 +- pkg/kubelet/images/image_gc_manager_test.go | 4 ++- pkg/kubelet/kubelet_pods_windows_test.go | 16 +++++++++++- .../kubeletconfig/util/files/files_test.go | 13 +++++----- .../util/files/files_unix_test.go | 25 ++++++++++++++++++ .../util/files/files_windows_test.go | 25 ++++++++++++++++++ .../kuberuntime/kuberuntime_container_test.go | 1 + .../logs/container_log_manager_test.go | 2 ++ .../server/stats/summary_windows_test.go | 6 +++-- pkg/kubelet/status/status_manager_test.go | 6 ++++- 25 files changed, 175 insertions(+), 46 deletions(-) create mode 100644 pkg/kubelet/apis/config/validation/validation_others_test.go create mode 100644 pkg/kubelet/apis/config/validation/validation_windows_test.go rename pkg/kubelet/{apis/config/v1beta1 => eviction}/defaults_linux.go (98%) rename pkg/kubelet/{apis/config/v1beta1 => eviction}/defaults_others.go (97%) create mode 100644 pkg/kubelet/kubeletconfig/util/files/files_unix_test.go create mode 100644 pkg/kubelet/kubeletconfig/util/files/files_windows_test.go diff --git a/cmd/kubelet/app/server.go b/cmd/kubelet/app/server.go index c8a67942b17..9c669420458 100644 --- a/cmd/kubelet/app/server.go +++ b/cmd/kubelet/app/server.go @@ -357,6 +357,13 @@ func loadConfigFile(name string) (*kubeletconfiginternal.KubeletConfiguration, e if err != nil { return nil, fmt.Errorf(errFmt, name, err) } + + // EvictionHard may be nil if it was not set in kubelet's config file. + // EvictionHard can have OS-specific fields, which is why there's no default value for it. + // See: https://github.com/kubernetes/kubernetes/pull/110263 + if kc.EvictionHard == nil { + kc.EvictionHard = eviction.DefaultEvictionHard + } return kc, err } diff --git a/pkg/kubelet/apis/config/fuzzer/fuzzer.go b/pkg/kubelet/apis/config/fuzzer/fuzzer.go index b1ad59b0ebc..643cc1b6947 100644 --- a/pkg/kubelet/apis/config/fuzzer/fuzzer.go +++ b/pkg/kubelet/apis/config/fuzzer/fuzzer.go @@ -28,6 +28,7 @@ import ( "k8s.io/kubernetes/pkg/cluster/ports" kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config" kubeletconfigv1beta1 "k8s.io/kubernetes/pkg/kubelet/apis/config/v1beta1" + "k8s.io/kubernetes/pkg/kubelet/eviction" "k8s.io/kubernetes/pkg/kubelet/qos" kubetypes "k8s.io/kubernetes/pkg/kubelet/types" utilpointer "k8s.io/utils/pointer" @@ -91,7 +92,7 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} { obj.KubeAPIQPS = 5 obj.KubeAPIBurst = 10 obj.HairpinMode = v1beta1.PromiscuousBridge - obj.EvictionHard = kubeletconfigv1beta1.DefaultEvictionHard + obj.EvictionHard = eviction.DefaultEvictionHard obj.EvictionPressureTransitionPeriod = metav1.Duration{Duration: 5 * time.Minute} obj.MakeIPTablesUtilChains = true obj.IPTablesMasqueradeBit = kubeletconfigv1beta1.DefaultIPTablesMasqueradeBit diff --git a/pkg/kubelet/apis/config/scheme/testdata/KubeletConfiguration/after/v1beta1.yaml b/pkg/kubelet/apis/config/scheme/testdata/KubeletConfiguration/after/v1beta1.yaml index a1a6cd6367e..cac43f8e1f7 100644 --- a/pkg/kubelet/apis/config/scheme/testdata/KubeletConfiguration/after/v1beta1.yaml +++ b/pkg/kubelet/apis/config/scheme/testdata/KubeletConfiguration/after/v1beta1.yaml @@ -32,11 +32,6 @@ enforceNodeAllocatable: - pods eventBurst: 10 eventRecordQPS: 5 -evictionHard: - imagefs.available: 15% - memory.available: 100Mi - nodefs.available: 10% - nodefs.inodesFree: 5% evictionPressureTransitionPeriod: 5m0s failSwapOn: true fileCheckFrequency: 20s diff --git a/pkg/kubelet/apis/config/scheme/testdata/KubeletConfiguration/roundtrip/default/v1beta1.yaml b/pkg/kubelet/apis/config/scheme/testdata/KubeletConfiguration/roundtrip/default/v1beta1.yaml index a1a6cd6367e..cac43f8e1f7 100644 --- a/pkg/kubelet/apis/config/scheme/testdata/KubeletConfiguration/roundtrip/default/v1beta1.yaml +++ b/pkg/kubelet/apis/config/scheme/testdata/KubeletConfiguration/roundtrip/default/v1beta1.yaml @@ -32,11 +32,6 @@ enforceNodeAllocatable: - pods eventBurst: 10 eventRecordQPS: 5 -evictionHard: - imagefs.available: 15% - memory.available: 100Mi - nodefs.available: 10% - nodefs.inodesFree: 5% evictionPressureTransitionPeriod: 5m0s failSwapOn: true fileCheckFrequency: 20s diff --git a/pkg/kubelet/apis/config/types.go b/pkg/kubelet/apis/config/types.go index 0ec23416fab..de488bb71bd 100644 --- a/pkg/kubelet/apis/config/types.go +++ b/pkg/kubelet/apis/config/types.go @@ -289,6 +289,7 @@ type KubeletConfiguration struct { // serializeImagePulls when enabled, tells the Kubelet to pull images one at a time. SerializeImagePulls bool // Map of signal names to quantities that defines hard eviction thresholds. For example: {"memory.available": "300Mi"}. + // Some default signals are Linux only: nodefs.inodesFree EvictionHard map[string]string // Map of signal names to quantities that defines soft eviction thresholds. For example: {"memory.available": "300Mi"}. EvictionSoft map[string]string diff --git a/pkg/kubelet/apis/config/v1beta1/defaults.go b/pkg/kubelet/apis/config/v1beta1/defaults.go index 82315403081..4b9397b734f 100644 --- a/pkg/kubelet/apis/config/v1beta1/defaults.go +++ b/pkg/kubelet/apis/config/v1beta1/defaults.go @@ -208,9 +208,6 @@ func SetDefaults_KubeletConfiguration(obj *kubeletconfigv1beta1.KubeletConfigura if obj.SerializeImagePulls == nil { obj.SerializeImagePulls = utilpointer.BoolPtr(true) } - if obj.EvictionHard == nil { - obj.EvictionHard = DefaultEvictionHard - } if obj.EvictionPressureTransitionPeriod == zeroDuration { obj.EvictionPressureTransitionPeriod = metav1.Duration{Duration: 5 * time.Minute} } diff --git a/pkg/kubelet/apis/config/v1beta1/defaults_test.go b/pkg/kubelet/apis/config/v1beta1/defaults_test.go index e16b3698624..447e5f5dad3 100644 --- a/pkg/kubelet/apis/config/v1beta1/defaults_test.go +++ b/pkg/kubelet/apis/config/v1beta1/defaults_test.go @@ -99,7 +99,7 @@ func TestSetDefaultsKubeletConfiguration(t *testing.T) { KubeAPIQPS: utilpointer.Int32Ptr(5), KubeAPIBurst: 10, SerializeImagePulls: utilpointer.BoolPtr(true), - EvictionHard: DefaultEvictionHard, + EvictionHard: nil, EvictionPressureTransitionPeriod: metav1.Duration{Duration: 5 * time.Minute}, EnableControllerAttachDetach: utilpointer.BoolPtr(true), MakeIPTablesUtilChains: utilpointer.BoolPtr(true), @@ -698,7 +698,7 @@ func TestSetDefaultsKubeletConfiguration(t *testing.T) { KubeAPIQPS: utilpointer.Int32Ptr(5), KubeAPIBurst: 10, SerializeImagePulls: utilpointer.BoolPtr(true), - EvictionHard: DefaultEvictionHard, + EvictionHard: nil, EvictionPressureTransitionPeriod: metav1.Duration{Duration: 5 * time.Minute}, EnableControllerAttachDetach: utilpointer.BoolPtr(true), MakeIPTablesUtilChains: utilpointer.BoolPtr(true), diff --git a/pkg/kubelet/apis/config/validation/validation_others_test.go b/pkg/kubelet/apis/config/validation/validation_others_test.go new file mode 100644 index 00000000000..a99c4042625 --- /dev/null +++ b/pkg/kubelet/apis/config/validation/validation_others_test.go @@ -0,0 +1,25 @@ +//go:build !windows +// +build !windows + +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package validation_test + +var ( + cgroupsPerQOS = true + enforceNodeAllocatable = []string{"pods", "system-reserved", "kube-reserved"} +) diff --git a/pkg/kubelet/apis/config/validation/validation_test.go b/pkg/kubelet/apis/config/validation/validation_test.go index 2ff921ec9bf..81de56d554b 100644 --- a/pkg/kubelet/apis/config/validation/validation_test.go +++ b/pkg/kubelet/apis/config/validation/validation_test.go @@ -34,8 +34,8 @@ import ( var ( successConfig = kubeletconfig.KubeletConfiguration{ - CgroupsPerQOS: true, - EnforceNodeAllocatable: []string{"pods", "system-reserved", "kube-reserved"}, + CgroupsPerQOS: cgroupsPerQOS, + EnforceNodeAllocatable: enforceNodeAllocatable, SystemReservedCgroup: "/system.slice", KubeReservedCgroup: "/kubelet.service", SystemCgroups: "", diff --git a/pkg/kubelet/apis/config/validation/validation_windows_test.go b/pkg/kubelet/apis/config/validation/validation_windows_test.go new file mode 100644 index 00000000000..73435915b3b --- /dev/null +++ b/pkg/kubelet/apis/config/validation/validation_windows_test.go @@ -0,0 +1,26 @@ +//go:build windows +// +build windows + +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package validation_test + +var ( + // These config options are not supported on Windows. + cgroupsPerQOS = false + enforceNodeAllocatable = []string{} +) diff --git a/pkg/kubelet/certificate/bootstrap/bootstrap_test.go b/pkg/kubelet/certificate/bootstrap/bootstrap_test.go index 51808e8dd55..81f4c816f77 100644 --- a/pkg/kubelet/certificate/bootstrap/bootstrap_test.go +++ b/pkg/kubelet/certificate/bootstrap/bootstrap_test.go @@ -21,6 +21,7 @@ import ( "fmt" "io" "os" + "path/filepath" "reflect" "testing" @@ -104,6 +105,8 @@ users: if err != nil { t.Fatal(err) } + // os.CreateTemp also opens the file, and removing it without closing it will result in a failure. + defer filevalid.Close() os.WriteFile(filevalid.Name(), testDataValid, os.FileMode(0755)) testDataInvalid := []byte(` @@ -144,6 +147,7 @@ users: if err != nil { t.Fatal(err) } + defer fileinvalid.Close() os.WriteFile(fileinvalid.Name(), testDataInvalid, os.FileMode(0755)) testDatabootstrap := []byte(` @@ -181,6 +185,7 @@ users: if err != nil { t.Fatal(err) } + defer fileboot.Close() os.WriteFile(fileboot.Name(), testDatabootstrap, os.FileMode(0755)) dir, err := os.MkdirTemp(fileDir, "k8s-test-certstore-current") @@ -209,16 +214,16 @@ users: expectedCertConfig: &restclient.Config{ Host: "https://cluster-b.com", TLSClientConfig: restclient.TLSClientConfig{ - CertFile: fileDir + "/mycertvalid.crt", - KeyFile: fileDir + "/mycertvalid.key", + CertFile: filepath.Join(fileDir, "mycertvalid.crt"), + KeyFile: filepath.Join(fileDir, "mycertvalid.key"), }, BearerToken: "", }, expectedClientConfig: &restclient.Config{ Host: "https://cluster-b.com", TLSClientConfig: restclient.TLSClientConfig{ - CertFile: fileDir + "/mycertvalid.crt", - KeyFile: fileDir + "/mycertvalid.key", + CertFile: filepath.Join(fileDir, "mycertvalid.crt"), + KeyFile: filepath.Join(fileDir, "mycertvalid.key"), }, BearerToken: "", }, @@ -231,16 +236,16 @@ users: expectedCertConfig: &restclient.Config{ Host: "https://cluster-b.com", TLSClientConfig: restclient.TLSClientConfig{ - CertFile: fileDir + "/mycertvalid.crt", - KeyFile: fileDir + "/mycertvalid.key", + CertFile: filepath.Join(fileDir, "mycertvalid.crt"), + KeyFile: filepath.Join(fileDir, "mycertvalid.key"), }, BearerToken: "", }, expectedClientConfig: &restclient.Config{ Host: "https://cluster-b.com", TLSClientConfig: restclient.TLSClientConfig{ - CertFile: fileDir + "/mycertvalid.crt", - KeyFile: fileDir + "/mycertvalid.key", + CertFile: filepath.Join(fileDir, "mycertvalid.crt"), + KeyFile: filepath.Join(fileDir, "mycertvalid.key"), }, BearerToken: "", }, diff --git a/pkg/kubelet/cm/cpumanager/cpu_manager_test.go b/pkg/kubelet/cm/cpumanager/cpu_manager_test.go index dea926d0da7..197bcb9d8e4 100644 --- a/pkg/kubelet/cm/cpumanager/cpu_manager_test.go +++ b/pkg/kubelet/cm/cpumanager/cpu_manager_test.go @@ -627,7 +627,7 @@ func TestCPUManagerGenerate(t *testing.T) { if testCase.isTopologyBroken { machineInfo = &cadvisorapi.MachineInfo{} } - sDir, err := os.MkdirTemp("/tmp/", "cpu_manager_test") + sDir, err := os.MkdirTemp("", "cpu_manager_test") if err != nil { t.Errorf("cannot create state file: %s", err.Error()) } @@ -1347,7 +1347,7 @@ func TestCPUManagerHandlePolicyOptions(t *testing.T) { t.Run(testCase.description, func(t *testing.T) { machineInfo := &mockedMachineInfo nodeAllocatableReservation := v1.ResourceList{} - sDir, err := os.MkdirTemp("/tmp/", "cpu_manager_test") + sDir, err := os.MkdirTemp("", "cpu_manager_test") if err != nil { t.Errorf("cannot create state file: %s", err.Error()) } diff --git a/pkg/kubelet/cm/devicemanager/endpoint_test.go b/pkg/kubelet/cm/devicemanager/endpoint_test.go index 419002f06c0..d9da8bfbeb5 100644 --- a/pkg/kubelet/cm/devicemanager/endpoint_test.go +++ b/pkg/kubelet/cm/devicemanager/endpoint_test.go @@ -18,6 +18,7 @@ package devicemanager import ( "fmt" + "os" "path" "sync" "testing" @@ -71,7 +72,7 @@ func esocketName() string { } func TestNewEndpoint(t *testing.T) { - socket := path.Join("/tmp", esocketName()) + socket := path.Join(os.TempDir(), esocketName()) devs := []*pluginapi.Device{ {ID: "ADeviceId", Health: pluginapi.Healthy}, @@ -82,7 +83,7 @@ func TestNewEndpoint(t *testing.T) { } func TestRun(t *testing.T) { - socket := path.Join("/tmp", esocketName()) + socket := path.Join(os.TempDir(), esocketName()) devs := []*pluginapi.Device{ {ID: "ADeviceId", Health: pluginapi.Healthy}, @@ -147,7 +148,7 @@ func TestRun(t *testing.T) { } func TestAllocate(t *testing.T) { - socket := path.Join("/tmp", esocketName()) + socket := path.Join(os.TempDir(), esocketName()) devs := []*pluginapi.Device{ {ID: "ADeviceId", Health: pluginapi.Healthy}, } @@ -200,7 +201,7 @@ func TestAllocate(t *testing.T) { } func TestGetPreferredAllocation(t *testing.T) { - socket := path.Join("/tmp", esocketName()) + socket := path.Join(os.TempDir(), esocketName()) callbackCount := 0 callbackChan := make(chan int) p, e := esetup(t, []*pluginapi.Device{}, socket, "mock", func(n string, d []pluginapi.Device) { diff --git a/pkg/kubelet/cm/memorymanager/memory_manager_test.go b/pkg/kubelet/cm/memorymanager/memory_manager_test.go index 96076c42b09..7376588f349 100644 --- a/pkg/kubelet/cm/memorymanager/memory_manager_test.go +++ b/pkg/kubelet/cm/memorymanager/memory_manager_test.go @@ -1981,7 +1981,7 @@ func TestNewManager(t *testing.T) { } for _, testCase := range testCases { t.Run(testCase.description, func(t *testing.T) { - stateFileDirectory, err := os.MkdirTemp("/tmp/", "memory_manager_tests") + stateFileDirectory, err := os.MkdirTemp("", "memory_manager_tests") if err != nil { t.Errorf("Cannot create state file: %s", err.Error()) } diff --git a/pkg/kubelet/apis/config/v1beta1/defaults_linux.go b/pkg/kubelet/eviction/defaults_linux.go similarity index 98% rename from pkg/kubelet/apis/config/v1beta1/defaults_linux.go rename to pkg/kubelet/eviction/defaults_linux.go index 6d3ec77dc7c..c0650ea72d5 100644 --- a/pkg/kubelet/apis/config/v1beta1/defaults_linux.go +++ b/pkg/kubelet/eviction/defaults_linux.go @@ -17,7 +17,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1beta1 +package eviction // DefaultEvictionHard includes default options for hard eviction. var DefaultEvictionHard = map[string]string{ diff --git a/pkg/kubelet/apis/config/v1beta1/defaults_others.go b/pkg/kubelet/eviction/defaults_others.go similarity index 97% rename from pkg/kubelet/apis/config/v1beta1/defaults_others.go rename to pkg/kubelet/eviction/defaults_others.go index 2b8ac3d4306..b226a210f45 100644 --- a/pkg/kubelet/apis/config/v1beta1/defaults_others.go +++ b/pkg/kubelet/eviction/defaults_others.go @@ -17,7 +17,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1beta1 +package eviction // DefaultEvictionHard includes default options for hard eviction. var DefaultEvictionHard = map[string]string{ diff --git a/pkg/kubelet/images/image_gc_manager_test.go b/pkg/kubelet/images/image_gc_manager_test.go index ed65d79d81e..af5a4d22c04 100644 --- a/pkg/kubelet/images/image_gc_manager_test.go +++ b/pkg/kubelet/images/image_gc_manager_test.go @@ -462,7 +462,9 @@ func TestFreeSpaceRemoveByLeastRecentlyUsed(t *testing.T) { require.NoError(t, err) require.Equal(t, manager.imageRecordsLen(), 2) - spaceFreed, err := manager.freeSpace(1024, time.Now()) + // We're setting the delete time one minute in the future, so the time the image + // was first detected and the delete time are different. + spaceFreed, err := manager.freeSpace(1024, time.Now().Add(time.Minute)) assert := assert.New(t) require.NoError(t, err) assert.EqualValues(1024, spaceFreed) diff --git a/pkg/kubelet/kubelet_pods_windows_test.go b/pkg/kubelet/kubelet_pods_windows_test.go index cb0bab98bfe..29a38bb0265 100644 --- a/pkg/kubelet/kubelet_pods_windows_test.go +++ b/pkg/kubelet/kubelet_pods_windows_test.go @@ -17,9 +17,12 @@ limitations under the License. package kubelet import ( + "os" + "path/filepath" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" v1 "k8s.io/api/core/v1" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" "k8s.io/kubernetes/pkg/volume/util/hostutil" @@ -84,7 +87,11 @@ func TestMakeMountsWindows(t *testing.T) { fhu := hostutil.NewFakeHostUtil(nil) fsp := &subpath.FakeSubpath{} - mounts, _, _ := makeMounts(&pod, "/pod", &container, "fakepodname", "", []string{""}, podVolumes, fhu, fsp, nil) + podDir, err := os.MkdirTemp("", "test-rotate-logs") + require.NoError(t, err) + defer os.RemoveAll(podDir) + mounts, _, err := makeMounts(&pod, podDir, &container, "fakepodname", "", []string{""}, podVolumes, fhu, fsp, nil) + require.NoError(t, err) expectedMounts := []kubecontainer.Mount{ { @@ -136,6 +143,13 @@ func TestMakeMountsWindows(t *testing.T) { ReadOnly: false, SELinuxRelabel: false, }, + { + Name: "k8s-managed-etc-hosts", + ContainerPath: `C:\Windows\System32\drivers\etc\hosts`, + HostPath: filepath.Join(podDir, "etc-hosts"), + ReadOnly: false, + SELinuxRelabel: true, + }, } assert.Equal(t, expectedMounts, mounts, "mounts of container %+v", container) } diff --git a/pkg/kubelet/kubeletconfig/util/files/files_test.go b/pkg/kubelet/kubeletconfig/util/files/files_test.go index 36665a15b27..eb8f31bd4ec 100644 --- a/pkg/kubelet/kubeletconfig/util/files/files_test.go +++ b/pkg/kubelet/kubeletconfig/util/files/files_test.go @@ -57,10 +57,11 @@ func (f *file) write(fs utilfs.Filesystem, dir string) error { return err } _, err = handle.Write([]byte(f.data)) + // The file should always be closed, not just in error cases. + if cerr := handle.Close(); cerr != nil { + return fmt.Errorf("error closing file: %v", cerr) + } if err != nil { - if cerr := handle.Close(); cerr != nil { - return fmt.Errorf("error %v closing file after error: %v", cerr, err) - } return err } } else { @@ -195,12 +196,12 @@ func TestHelpers(t *testing.T) { { desc: "missing file", expects: []file{{name: "foo", data: "bar"}}, - err: "no such file or directory", + err: missingFileError, }, { desc: "missing directory", expects: []file{{name: "foo/bar", mode: os.ModeDir}}, - err: "no such file or directory", + err: missingFolderError, }, } for _, c := range cases { @@ -312,7 +313,7 @@ func TestReplaceFile(t *testing.T) { return nil }, desc: "neither parent nor file exists", - err: "no such file or directory", + err: missingFileError, }, } for _, c := range cases { diff --git a/pkg/kubelet/kubeletconfig/util/files/files_unix_test.go b/pkg/kubelet/kubeletconfig/util/files/files_unix_test.go new file mode 100644 index 00000000000..3dc2b089a52 --- /dev/null +++ b/pkg/kubelet/kubeletconfig/util/files/files_unix_test.go @@ -0,0 +1,25 @@ +//go:build !windows +// +build !windows + +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package files + +const ( + missingFileError = "no such file or directory" + missingFolderError = "no such file or directory" +) diff --git a/pkg/kubelet/kubeletconfig/util/files/files_windows_test.go b/pkg/kubelet/kubeletconfig/util/files/files_windows_test.go new file mode 100644 index 00000000000..27fe09abc28 --- /dev/null +++ b/pkg/kubelet/kubeletconfig/util/files/files_windows_test.go @@ -0,0 +1,25 @@ +//go:build windows +// +build windows + +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package files + +const ( + missingFileError = "The system cannot find the file specified." + missingFolderError = "The system cannot find the path specified." +) diff --git a/pkg/kubelet/kuberuntime/kuberuntime_container_test.go b/pkg/kubelet/kuberuntime/kuberuntime_container_test.go index 208c20db7bf..6b5a574955b 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_container_test.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_container_test.go @@ -65,6 +65,7 @@ func TestRemoveContainer(t *testing.T) { fakeOS := m.osInterface.(*containertest.FakeOS) fakeOS.GlobFn = func(pattern, path string) bool { pattern = strings.Replace(pattern, "*", ".*", -1) + pattern = strings.Replace(pattern, "\\", "\\\\", -1) return regexp.MustCompile(pattern).MatchString(path) } expectedContainerLogPath := filepath.Join(podLogsRootDirectory, "new_bar_12345678", "foo", "0.log") diff --git a/pkg/kubelet/logs/container_log_manager_test.go b/pkg/kubelet/logs/container_log_manager_test.go index 4f5dc8eb18c..6478088494c 100644 --- a/pkg/kubelet/logs/container_log_manager_test.go +++ b/pkg/kubelet/logs/container_log_manager_test.go @@ -328,6 +328,7 @@ func TestCompressLog(t *testing.T) { testContent := "test log content" _, err = testFile.Write([]byte(testContent)) require.NoError(t, err) + testFile.Close() testLog := testFile.Name() c := &containerLogManager{osInterface: container.RealOS{}} @@ -388,6 +389,7 @@ func TestRotateLatestLog(t *testing.T) { } testFile, err := os.CreateTemp(dir, "test-rotate-latest-log") require.NoError(t, err) + testFile.Close() defer testFile.Close() testLog := testFile.Name() rotatedLog := fmt.Sprintf("%s.%s", testLog, now.Format(timestampFormat)) diff --git a/pkg/kubelet/server/stats/summary_windows_test.go b/pkg/kubelet/server/stats/summary_windows_test.go index ddedee3bcef..27fe25c7618 100644 --- a/pkg/kubelet/server/stats/summary_windows_test.go +++ b/pkg/kubelet/server/stats/summary_windows_test.go @@ -67,12 +67,14 @@ func TestSummaryProvider(t *testing.T) { mockStatsProvider.EXPECT().RlimitStats().Return(nil, nil).AnyTimes() mockStatsProvider.EXPECT().GetCgroupStats("/", true).Return(cgroupStatsMap["/"].cs, cgroupStatsMap["/"].ns, nil).AnyTimes() - provider := NewSummaryProvider(mockStatsProvider) + kubeletCreationTime := metav1.Now() + systemBootTime := metav1.Now() + provider := summaryProviderImpl{kubeletCreationTime: kubeletCreationTime, systemBootTime: systemBootTime, provider: mockStatsProvider} summary, err := provider.Get(true) assert.NoError(err) assert.Equal(summary.Node.NodeName, "test-node") - assert.Equal(summary.Node.StartTime, cgroupStatsMap["/"].cs.StartTime) + assert.Equal(summary.Node.StartTime, systemBootTime) assert.Equal(summary.Node.CPU, cgroupStatsMap["/"].cs.CPU) assert.Equal(summary.Node.Memory, cgroupStatsMap["/"].cs.Memory) assert.Equal(summary.Node.Network, cgroupStatsMap["/"].ns) diff --git a/pkg/kubelet/status/status_manager_test.go b/pkg/kubelet/status/status_manager_test.go index 938b1d6ea25..ccc64ecaa5a 100644 --- a/pkg/kubelet/status/status_manager_test.go +++ b/pkg/kubelet/status/status_manager_test.go @@ -1319,7 +1319,11 @@ func TestDoNotDeleteMirrorPods(t *testing.T) { } func TestUpdateLastTransitionTime(t *testing.T) { - old := metav1.Now() + // On Windows, time.Now() is not as precise, which means that 2 consecutive calls may + // return the same timestamp. This test expects the old timestamp to be updated with a + // newer one, so we set the old timestamp to one second in the past. + // See: https://github.com/golang/go/issues/8687 + old := metav1.NewTime(time.Now().Add(-time.Second)) for desc, test := range map[string]struct { condition *v1.PodCondition oldCondition *v1.PodCondition