diff --git a/pkg/volume/metrics_du_test.go b/pkg/volume/metrics_du_test.go index 0e9ad7fb668..b4c72924e7a 100644 --- a/pkg/volume/metrics_du_test.go +++ b/pkg/volume/metrics_du_test.go @@ -1,6 +1,3 @@ -//go:build linux -// +build linux - /* Copyright 2015 The Kubernetes Authors. @@ -25,22 +22,11 @@ import ( "path/filepath" "testing" - "golang.org/x/sys/unix" utiltesting "k8s.io/client-go/util/testing" . "k8s.io/kubernetes/pkg/volume" volumetest "k8s.io/kubernetes/pkg/volume/testing" ) -func getExpectedBlockSize(path string) int64 { - statfs := &unix.Statfs_t{} - err := unix.Statfs(path, statfs) - if err != nil { - return 0 - } - - return int64(statfs.Bsize) -} - // TestMetricsDuGetCapacity tests that MetricsDu can read disk usage // for path func TestMetricsDuGetCapacity(t *testing.T) { diff --git a/pkg/volume/metrics_du_unix_test.go b/pkg/volume/metrics_du_unix_test.go new file mode 100644 index 00000000000..ea35b669170 --- /dev/null +++ b/pkg/volume/metrics_du_unix_test.go @@ -0,0 +1,34 @@ +//go:build !windows +// +build !windows + +/* +Copyright 2023 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 volume_test + +import ( + "golang.org/x/sys/unix" +) + +func getExpectedBlockSize(path string) int64 { + statfs := &unix.Statfs_t{} + err := unix.Statfs(path, statfs) + if err != nil { + return 0 + } + + return int64(statfs.Bsize) +} diff --git a/pkg/volume/metrics_du_windows_test.go b/pkg/volume/metrics_du_windows_test.go new file mode 100644 index 00000000000..42217e2b6e4 --- /dev/null +++ b/pkg/volume/metrics_du_windows_test.go @@ -0,0 +1,32 @@ +//go:build windows +// +build windows + +/* +Copyright 2023 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 volume_test + +import ( + "k8s.io/kubernetes/pkg/volume/util/fs" +) + +func getExpectedBlockSize(path string) int64 { + usage, err := fs.DiskUsage(path) + if err != nil { + return 0 + } + return usage.Bytes +} diff --git a/pkg/volume/testing/testing.go b/pkg/volume/testing/testing.go index b1040fd8d19..c39e876e04a 100644 --- a/pkg/volume/testing/testing.go +++ b/pkg/volume/testing/testing.go @@ -20,6 +20,7 @@ import ( "fmt" "os" "path/filepath" + goruntime "runtime" "strings" "sync" "testing" @@ -1269,6 +1270,11 @@ func (fv *FakeVolumePathHandler) GetLoopDevice(path string) (string, error) { // FindEmptyDirectoryUsageOnTmpfs finds the expected usage of an empty directory existing on // a tmpfs filesystem on this system. func FindEmptyDirectoryUsageOnTmpfs() (*resource.Quantity, error) { + // The command below does not exist on Windows. Additionally, empty folders have size 0 on Windows. + if goruntime.GOOS == "windows" { + used, err := resource.ParseQuantity("0") + return &used, err + } tmpDir, err := utiltesting.MkTmpdir("metrics_du_test") if err != nil { return nil, err diff --git a/pkg/volume/util/hostutil/hostutil_linux_test.go b/pkg/volume/util/hostutil/hostutil_linux_test.go index cc2007e3caf..3995362f866 100644 --- a/pkg/volume/util/hostutil/hostutil_linux_test.go +++ b/pkg/volume/util/hostutil/hostutil_linux_test.go @@ -20,15 +20,10 @@ limitations under the License. package hostutil import ( - "fmt" "io/ioutil" - "net" "os" "path/filepath" - "strings" "testing" - - "k8s.io/utils/exec" ) func TestIsSharedSuccess(t *testing.T) { @@ -197,127 +192,6 @@ func TestGetSELinuxSupport(t *testing.T) { } } -func createSocketFile(socketDir string) (string, error) { - testSocketFile := filepath.Join(socketDir, "mt.sock") - - // Switch to volume path and create the socket file - // socket file can not have length of more than 108 character - // and hence we must use relative path - oldDir, _ := os.Getwd() - - err := os.Chdir(socketDir) - if err != nil { - return "", err - } - defer func() { - os.Chdir(oldDir) - }() - _, socketCreateError := net.Listen("unix", "mt.sock") - return testSocketFile, socketCreateError -} - -func TestGetFileType(t *testing.T) { - hu := NewHostUtil() - - testCase := []struct { - name string - expectedType FileType - setUp func() (string, string, error) - }{ - { - "Directory Test", - FileTypeDirectory, - func() (string, string, error) { - tempDir, err := ioutil.TempDir("", "test-get-filetype-") - return tempDir, tempDir, err - }, - }, - { - "File Test", - FileTypeFile, - func() (string, string, error) { - tempFile, err := ioutil.TempFile("", "test-get-filetype") - if err != nil { - return "", "", err - } - tempFile.Close() - return tempFile.Name(), tempFile.Name(), nil - }, - }, - { - "Socket Test", - FileTypeSocket, - func() (string, string, error) { - tempDir, err := ioutil.TempDir("", "test-get-filetype-") - if err != nil { - return "", "", err - } - tempSocketFile, err := createSocketFile(tempDir) - return tempSocketFile, tempDir, err - }, - }, - { - "Block Device Test", - FileTypeBlockDev, - func() (string, string, error) { - tempDir, err := ioutil.TempDir("", "test-get-filetype-") - if err != nil { - return "", "", err - } - - tempBlockFile := filepath.Join(tempDir, "test_blk_dev") - outputBytes, err := exec.New().Command("mknod", tempBlockFile, "b", "89", "1").CombinedOutput() - if err != nil { - err = fmt.Errorf("%v: %s ", err, outputBytes) - } - return tempBlockFile, tempDir, err - }, - }, - { - "Character Device Test", - FileTypeCharDev, - func() (string, string, error) { - tempDir, err := ioutil.TempDir("", "test-get-filetype-") - if err != nil { - return "", "", err - } - - tempCharFile := filepath.Join(tempDir, "test_char_dev") - outputBytes, err := exec.New().Command("mknod", tempCharFile, "c", "89", "1").CombinedOutput() - if err != nil { - err = fmt.Errorf("%v: %s ", err, outputBytes) - } - return tempCharFile, tempDir, err - }, - }, - } - - for idx, tc := range testCase { - path, cleanUpPath, err := tc.setUp() - defer os.RemoveAll(cleanUpPath) // RemoveAll can deal with a empty path "" - if err != nil { - // Locally passed, but upstream CI is not friendly to create such device files - // Leave "Operation not permitted" out, which can be covered in an e2e test - if isOperationNotPermittedError(err) { - continue - } - t.Fatalf("[%d-%s] unexpected error : %v", idx, tc.name, err) - } - - fileType, err := hu.GetFileType(path) - if err != nil { - t.Fatalf("[%d-%s] unexpected error : %v", idx, tc.name, err) - } - if fileType != tc.expectedType { - t.Fatalf("[%d-%s] expected %s, but got %s", idx, tc.name, tc.expectedType, fileType) - } - } -} - -func isOperationNotPermittedError(err error) bool { - return strings.Contains(err.Error(), "Operation not permitted") -} - func writeFile(content string) (string, string, error) { tempDir, err := ioutil.TempDir("", "mounter_shared_test") if err != nil { diff --git a/pkg/volume/util/hostutil/hostutil_test.go b/pkg/volume/util/hostutil/hostutil_test.go new file mode 100644 index 00000000000..4e68c6f1a9a --- /dev/null +++ b/pkg/volume/util/hostutil/hostutil_test.go @@ -0,0 +1,230 @@ +/* +Copyright 2023 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 hostutil + +import ( + "errors" + "fmt" + "net" + "os" + "path/filepath" + goruntime "runtime" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "k8s.io/mount-utils" + "k8s.io/utils/exec" +) + +// fakeMounter implements mount.Interface for tests. +type fakeMounter struct { + mount.FakeMounter + mountRefs []string + raiseError bool +} + +// GetMountRefs finds all mount references to the path, returns a +// list of paths. +func (f *fakeMounter) GetMountRefs(pathname string) ([]string, error) { + if f.raiseError { + return nil, errors.New("Expected error.") + } + + return f.mountRefs, nil +} + +func TestDeviceNameFromMount(t *testing.T) { + hu := NewHostUtil() + path := "/tmp/foo" + if goruntime.GOOS == "windows" { + path = "C:" + path + } + + testCases := map[string]struct { + mountRefs []string + expectedPath string + raiseError bool + expectedError string + }{ + "GetMountRefs error": { + raiseError: true, + expectedError: "Expected error.", + }, + "No Refs error": { + expectedError: fmt.Sprintf("directory %s is not mounted", path), + }, + "No Matching refs": { + mountRefs: []string{filepath.Join("foo", "lish")}, + expectedPath: filepath.Base(path), + }, + "Matched ref": { + mountRefs: []string{filepath.Join(path, "lish")}, + expectedPath: "lish", + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + mounter := &fakeMounter{ + mountRefs: tc.mountRefs, + raiseError: tc.raiseError, + } + + path, err := hu.GetDeviceNameFromMount(mounter, path, path) + if tc.expectedError != "" { + if err == nil || err.Error() != tc.expectedError { + t.Fatalf("expected error message `%s` but got `%v`", tc.expectedError, err) + } + return + } + + expectedPath := filepath.FromSlash(tc.expectedPath) + assert.Equal(t, expectedPath, path) + }) + } +} + +func createSocketFile(socketDir string) (string, error) { + testSocketFile := filepath.Join(socketDir, "mt.sock") + + // Switch to volume path and create the socket file + // socket file can not have length of more than 108 character + // and hence we must use relative path + oldDir, _ := os.Getwd() + + err := os.Chdir(socketDir) + if err != nil { + return "", err + } + defer func() { + os.Chdir(oldDir) + }() + _, socketCreateError := net.Listen("unix", "mt.sock") + return testSocketFile, socketCreateError +} + +func TestGetFileType(t *testing.T) { + hu := NewHostUtil() + + testCase := []struct { + name string + skipWindows bool + expectedType FileType + setUp func() (string, string, error) + }{ + { + "Directory Test", + false, + FileTypeDirectory, + func() (string, string, error) { + tempDir, err := os.MkdirTemp("", "test-get-filetype-") + return tempDir, tempDir, err + }, + }, + { + "File Test", + false, + FileTypeFile, + func() (string, string, error) { + tempFile, err := os.CreateTemp("", "test-get-filetype") + if err != nil { + return "", "", err + } + tempFile.Close() + return tempFile.Name(), tempFile.Name(), nil + }, + }, + { + "Socket Test", + false, + FileTypeSocket, + func() (string, string, error) { + tempDir, err := os.MkdirTemp("", "test-get-filetype-") + if err != nil { + return "", "", err + } + tempSocketFile, err := createSocketFile(tempDir) + return tempSocketFile, tempDir, err + }, + }, + { + "Block Device Test", + true, + FileTypeBlockDev, + func() (string, string, error) { + tempDir, err := os.MkdirTemp("", "test-get-filetype-") + if err != nil { + return "", "", err + } + + tempBlockFile := filepath.Join(tempDir, "test_blk_dev") + outputBytes, err := exec.New().Command("mknod", tempBlockFile, "b", "89", "1").CombinedOutput() + if err != nil { + err = fmt.Errorf("%v: %s ", err, outputBytes) + } + return tempBlockFile, tempDir, err + }, + }, + { + "Character Device Test", + true, + FileTypeCharDev, + func() (string, string, error) { + tempDir, err := os.MkdirTemp("", "test-get-filetype-") + if err != nil { + return "", "", err + } + + tempCharFile := filepath.Join(tempDir, "test_char_dev") + outputBytes, err := exec.New().Command("mknod", tempCharFile, "c", "89", "1").CombinedOutput() + if err != nil { + err = fmt.Errorf("%v: %s ", err, outputBytes) + } + return tempCharFile, tempDir, err + }, + }, + } + + for idx, tc := range testCase { + if goruntime.GOOS == "windows" && tc.skipWindows { + continue + } + path, cleanUpPath, err := tc.setUp() + defer os.RemoveAll(cleanUpPath) // RemoveAll can deal with a empty path "" + if err != nil { + // Locally passed, but upstream CI is not friendly to create such device files + // Leave "Operation not permitted" out, which can be covered in an e2e test + if isOperationNotPermittedError(err) { + continue + } + t.Fatalf("[%d-%s] unexpected error : %v", idx, tc.name, err) + } + + fileType, err := hu.GetFileType(path) + if err != nil { + t.Fatalf("[%d-%s] unexpected error : %v", idx, tc.name, err) + } + if fileType != tc.expectedType { + t.Fatalf("[%d-%s] expected %s, but got %s", idx, tc.name, tc.expectedType, fileType) + } + } +} + +func isOperationNotPermittedError(err error) bool { + return strings.Contains(err.Error(), "Operation not permitted") +} diff --git a/pkg/volume/util/hostutil/hostutil_windows_test.go b/pkg/volume/util/hostutil/hostutil_windows_test.go deleted file mode 100644 index 16c7dc37362..00000000000 --- a/pkg/volume/util/hostutil/hostutil_windows_test.go +++ /dev/null @@ -1,75 +0,0 @@ -//go:build windows -// +build windows - -/* -Copyright 2017 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 hostutil - -import ( - "io/ioutil" - "os" - "testing" -) - -func TestGetFileType(t *testing.T) { - hu := NewHostUtil() - - testCase := []struct { - name string - expectedType FileType - setUp func() (string, string, error) - }{ - { - "Directory Test", - FileTypeDirectory, - func() (string, string, error) { - tempDir, err := ioutil.TempDir("", "test-get-filetype-") - return tempDir, tempDir, err - }, - }, - { - "File Test", - FileTypeFile, - func() (string, string, error) { - tempFile, err := ioutil.TempFile("", "test-get-filetype") - if err != nil { - return "", "", err - } - tempFile.Close() - return tempFile.Name(), tempFile.Name(), nil - }, - }, - } - - for idx, tc := range testCase { - path, cleanUpPath, err := tc.setUp() - if err != nil { - t.Fatalf("[%d-%s] unexpected error : %v", idx, tc.name, err) - } - if len(cleanUpPath) > 0 { - defer os.RemoveAll(cleanUpPath) - } - - fileType, err := hu.GetFileType(path) - if err != nil { - t.Fatalf("[%d-%s] unexpected error : %v", idx, tc.name, err) - } - if fileType != tc.expectedType { - t.Fatalf("[%d-%s] expected %s, but got %s", idx, tc.name, tc.expectedType, fileType) - } - } -}