diff --git a/pkg/kubelet/kubelet_pods.go b/pkg/kubelet/kubelet_pods.go index 6703e3e8b0..39c073b77d 100644 --- a/pkg/kubelet/kubelet_pods.go +++ b/pkg/kubelet/kubelet_pods.go @@ -128,7 +128,6 @@ func (kl *Kubelet) makeBlockVolumes(pod *v1.Pod, container *v1.Container, podVol // makeMounts determines the mount points for the given container. func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, hostDomain, podIP string, podVolumes kubecontainer.VolumeMap, mounter mountutil.Interface, expandEnvs []kubecontainer.EnvVar) ([]kubecontainer.Mount, func(), error) { - // Kubernetes only mounts on /etc/hosts if: // - container is not an infrastructure (pause) container // - container is not already mounting on /etc/hosts @@ -216,13 +215,13 @@ func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, h } // Docker Volume Mounts fail on Windows if it is not of the form C:/ - containerPath := mount.MountPath - if runtime.GOOS == "windows" { - if (strings.HasPrefix(hostPath, "/") || strings.HasPrefix(hostPath, "\\")) && !strings.Contains(hostPath, ":") { - hostPath = "c:" + hostPath - } + if volumeutil.IsWindowsLocalPath(runtime.GOOS, hostPath) { + hostPath = volumeutil.MakeAbsolutePath(runtime.GOOS, hostPath) } - if !filepath.IsAbs(containerPath) { + + containerPath := mount.MountPath + // IsAbs returns false for UNC path/SMB shares/named pipes in Windows. So check for those specifically and skip MakeAbsolutePath + if !volumeutil.IsWindowsUNCPath(runtime.GOOS, containerPath) && !filepath.IsAbs(containerPath) { containerPath = volumeutil.MakeAbsolutePath(runtime.GOOS, containerPath) } diff --git a/pkg/kubelet/kubelet_pods_windows_test.go b/pkg/kubelet/kubelet_pods_windows_test.go index 628c2ecdd7..41a2cf6dc0 100644 --- a/pkg/kubelet/kubelet_pods_windows_test.go +++ b/pkg/kubelet/kubelet_pods_windows_test.go @@ -1,5 +1,3 @@ -// +build windows - /* Copyright 2017 The Kubernetes Authors. @@ -50,6 +48,21 @@ func TestMakeMountsWindows(t *testing.T) { Name: "disk5", ReadOnly: false, }, + { + MountPath: `\mnt\path6`, + Name: "disk6", + ReadOnly: false, + }, + { + MountPath: `/mnt/path7`, + Name: "disk7", + ReadOnly: false, + }, + { + MountPath: `\\.\pipe\pipe1`, + Name: "pipe1", + ReadOnly: false, + }, }, } @@ -57,6 +70,9 @@ func TestMakeMountsWindows(t *testing.T) { "disk": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "c:/mnt/disk"}}, "disk4": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "c:/mnt/host"}}, "disk5": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "c:/var/lib/kubelet/podID/volumes/empty/disk5"}}, + "disk6": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: `/mnt/disk6`}}, + "disk7": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: `\mnt\disk7`}}, + "pipe1": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: `\\.\pipe\pipe1`}}, } pod := v1.Pod{ @@ -97,6 +113,27 @@ func TestMakeMountsWindows(t *testing.T) { ReadOnly: false, SELinuxRelabel: false, }, + { + Name: "disk6", + ContainerPath: `c:\mnt\path6`, + HostPath: `c:/mnt/disk6`, + ReadOnly: false, + SELinuxRelabel: false, + }, + { + Name: "disk7", + ContainerPath: `c:/mnt/path7`, + HostPath: `c:\mnt\disk7`, + ReadOnly: false, + SELinuxRelabel: false, + }, + { + Name: "pipe1", + ContainerPath: `\\.\pipe\pipe1`, + HostPath: `\\.\pipe\pipe1`, + ReadOnly: false, + SELinuxRelabel: false, + }, } assert.Equal(t, expectedMounts, mounts, "mounts of container %+v", container) } diff --git a/pkg/volume/util/util.go b/pkg/volume/util/util.go index 7106b6c1f7..faf0d34b61 100644 --- a/pkg/volume/util/util.go +++ b/pkg/volume/util/util.go @@ -947,6 +947,38 @@ func CheckPersistentVolumeClaimModeBlock(pvc *v1.PersistentVolumeClaim) bool { return utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) && pvc.Spec.VolumeMode != nil && *pvc.Spec.VolumeMode == v1.PersistentVolumeBlock } +// IsWindowsUNCPath checks if path is prefixed with \\ +// This can be used to skip any processing of paths +// that point to SMB shares, local named pipes and local UNC path +func IsWindowsUNCPath(goos, path string) bool { + if goos != "windows" { + return false + } + // Check for UNC prefix \\ + if strings.HasPrefix(path, `\\`) { + return true + } + return false +} + +// IsWindowsLocalPath checks if path is a local path +// prefixed with "/" or "\" like "/foo/bar" or "\foo\bar" +func IsWindowsLocalPath(goos, path string) bool { + if goos != "windows" { + return false + } + if IsWindowsUNCPath(goos, path) { + return false + } + if strings.Contains(path, ":") { + return false + } + if !(strings.HasPrefix(path, `/`) || strings.HasPrefix(path, `\`)) { + return false + } + return true +} + // MakeAbsolutePath convert path to absolute path according to GOOS func MakeAbsolutePath(goos, path string) string { if goos != "windows" { diff --git a/pkg/volume/util/util_test.go b/pkg/volume/util/util_test.go index 30d980921e..c5e4e8574f 100644 --- a/pkg/volume/util/util_test.go +++ b/pkg/volume/util/util_test.go @@ -2373,6 +2373,148 @@ func TestGetWindowsPath(t *testing.T) { } } +func TestIsWindowsUNCPath(t *testing.T) { + tests := []struct { + goos string + path string + isUNCPath bool + }{ + { + goos: "linux", + path: `/usr/bin`, + isUNCPath: false, + }, + { + goos: "linux", + path: `\\.\pipe\foo`, + isUNCPath: false, + }, + { + goos: "windows", + path: `C:\foo`, + isUNCPath: false, + }, + { + goos: "windows", + path: `\\server\share\foo`, + isUNCPath: true, + }, + { + goos: "windows", + path: `\\?\server\share`, + isUNCPath: true, + }, + { + goos: "windows", + path: `\\?\c:\`, + isUNCPath: true, + }, + { + goos: "windows", + path: `\\.\pipe\valid_pipe`, + isUNCPath: true, + }, + } + + for _, test := range tests { + result := IsWindowsUNCPath(test.goos, test.path) + if result != test.isUNCPath { + t.Errorf("IsWindowsUNCPath(%v) returned (%v), expected (%v)", test.path, result, test.isUNCPath) + } + } +} + +func TestIsWindowsLocalPath(t *testing.T) { + tests := []struct { + goos string + path string + isWindowsLocalPath bool + }{ + { + goos: "linux", + path: `/usr/bin`, + isWindowsLocalPath: false, + }, + { + goos: "linux", + path: `\\.\pipe\foo`, + isWindowsLocalPath: false, + }, + { + goos: "windows", + path: `C:\foo`, + isWindowsLocalPath: false, + }, + { + goos: "windows", + path: `:\foo`, + isWindowsLocalPath: false, + }, + { + goos: "windows", + path: `X:\foo`, + isWindowsLocalPath: false, + }, + { + goos: "windows", + path: `\\server\share\foo`, + isWindowsLocalPath: false, + }, + { + goos: "windows", + path: `\\?\server\share`, + isWindowsLocalPath: false, + }, + { + goos: "windows", + path: `\\?\c:\`, + isWindowsLocalPath: false, + }, + { + goos: "windows", + path: `\\.\pipe\valid_pipe`, + isWindowsLocalPath: false, + }, + { + goos: "windows", + path: `foo`, + isWindowsLocalPath: false, + }, + { + goos: "windows", + path: `:foo`, + isWindowsLocalPath: false, + }, + { + goos: "windows", + path: `\foo`, + isWindowsLocalPath: true, + }, + { + goos: "windows", + path: `\foo\bar`, + isWindowsLocalPath: true, + }, + { + goos: "windows", + path: `/foo`, + isWindowsLocalPath: true, + }, + { + goos: "windows", + path: `/foo/bar`, + isWindowsLocalPath: true, + }, + } + + for _, test := range tests { + result := IsWindowsLocalPath(test.goos, test.path) + if result != test.isWindowsLocalPath { + t.Errorf("isWindowsLocalPath(%v) returned (%v), expected (%v)", test.path, result, test.isWindowsLocalPath) + } + } +} + func TestMakeAbsolutePath(t *testing.T) { tests := []struct { goos string