diff --git a/pkg/apis/core/types.go b/pkg/apis/core/types.go index f861925cc74..e74469d9bb8 100644 --- a/pkg/apis/core/types.go +++ b/pkg/apis/core/types.go @@ -1530,7 +1530,9 @@ type VolumeMount struct { // Optional: Defaults to false (read-write). // +optional ReadOnly bool - // Required. Must not contain ':'. + // Required. If the path is not an absolute path (e.g. some/path) it + // will be prepended with the appropriate root prefix for the operating + // system. On Linux this is '/', on Windows this is 'C:\'. MountPath string // Path within the volume from which the container's volume should be mounted. // Defaults to "" (volume's root). diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go index 8fe283ddfeb..1dd944f10e7 100644 --- a/pkg/apis/core/validation/validation.go +++ b/pkg/apis/core/validation/validation.go @@ -1965,13 +1965,6 @@ func ValidateVolumeMounts(mounts []core.VolumeMount, volumes sets.String, contai if mountpoints.Has(mnt.MountPath) { allErrs = append(allErrs, field.Invalid(idxPath.Child("mountPath"), mnt.MountPath, "must be unique")) } - if !path.IsAbs(mnt.MountPath) { - // also allow windows absolute path - p := mnt.MountPath - if len(p) < 2 || ((p[0] < 'A' || p[0] > 'Z') && (p[0] < 'a' || p[0] > 'z')) || p[1] != ':' { - allErrs = append(allErrs, field.Invalid(idxPath.Child("mountPath"), mnt.MountPath, "must be an absolute path")) - } - } mountpoints.Insert(mnt.MountPath) if len(mnt.SubPath) > 0 { allErrs = append(allErrs, validateLocalDescendingPath(mnt.SubPath, fldPath.Child("subPath"))...) diff --git a/pkg/apis/core/validation/validation_test.go b/pkg/apis/core/validation/validation_test.go index 3a6121715ce..43cdd8a3ead 100644 --- a/pkg/apis/core/validation/validation_test.go +++ b/pkg/apis/core/validation/validation_test.go @@ -3849,10 +3849,8 @@ func TestValidateVolumeMounts(t *testing.T) { "empty name": {{Name: "", MountPath: "/foo"}}, "name not found": {{Name: "", MountPath: "/foo"}}, "empty mountpath": {{Name: "abc", MountPath: ""}}, - "relative mountpath": {{Name: "abc", MountPath: "bar"}}, "mountpath collision": {{Name: "foo", MountPath: "/path/a"}, {Name: "bar", MountPath: "/path/a"}}, "absolute subpath": {{Name: "abc", MountPath: "/bar", SubPath: "/baz"}}, - "windows absolute subpath": {{Name: "abc", MountPath: "D", SubPath: ""}}, "subpath in ..": {{Name: "abc", MountPath: "/bar", SubPath: "../baz"}}, "subpath contains ..": {{Name: "abc", MountPath: "/bar", SubPath: "baz/../bat"}}, "subpath ends in ..": {{Name: "abc", MountPath: "/bar", SubPath: "./.."}}, diff --git a/pkg/kubelet/kubelet_pods.go b/pkg/kubelet/kubelet_pods.go index bf2dede98a9..8a180747423 100644 --- a/pkg/kubelet/kubelet_pods.go +++ b/pkg/kubelet/kubelet_pods.go @@ -111,6 +111,23 @@ func (kl *Kubelet) makeDevices(pod *v1.Pod, container *v1.Container) ([]kubecont return devices, nil } +func makeAbsolutePath(goos, path string) string { + if goos != "windows" { + return "/" + path + } + // These are all for windows + // If there is a colon, give up. + if strings.Contains(path, ":") { + return path + } + // If there is a slash, but no drive, add 'c:' + if strings.HasPrefix(path, "/") || strings.HasPrefix(path, "\\") { + return "c:" + path + } + // Otherwise, add 'c:\' + return "c:\\" + path +} + // 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) ([]kubecontainer.Mount, error) { // Kubernetes only mounts on /etc/hosts if: @@ -187,9 +204,9 @@ func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, h if (strings.HasPrefix(hostPath, "/") || strings.HasPrefix(hostPath, "\\")) && !strings.Contains(hostPath, ":") { hostPath = "c:" + hostPath } - if (strings.HasPrefix(containerPath, "/") || strings.HasPrefix(containerPath, "\\")) && !strings.Contains(containerPath, ":") { - containerPath = "c:" + containerPath - } + } + if !filepath.IsAbs(containerPath) { + containerPath = makeAbsolutePath(runtime.GOOS, containerPath) } propagation, err := translateMountPropagation(mount.MountPropagation) diff --git a/pkg/kubelet/kubelet_pods_test.go b/pkg/kubelet/kubelet_pods_test.go index 5b759d5d1a5..79e1f15361a 100644 --- a/pkg/kubelet/kubelet_pods_test.go +++ b/pkg/kubelet/kubelet_pods_test.go @@ -49,6 +49,52 @@ import ( "k8s.io/kubernetes/pkg/kubelet/server/remotecommand" ) +func TestMakeAbsolutePath(t *testing.T) { + tests := []struct { + goos string + path string + expectedPath string + name string + }{ + { + goos: "linux", + path: "non-absolute/path", + expectedPath: "/non-absolute/path", + name: "basic linux", + }, + { + goos: "windows", + path: "some\\path", + expectedPath: "c:\\some\\path", + name: "basic windows", + }, + { + goos: "windows", + path: "/some/path", + expectedPath: "c:/some/path", + name: "linux path on windows", + }, + { + goos: "windows", + path: "\\some\\path", + expectedPath: "c:\\some\\path", + name: "windows path no drive", + }, + { + goos: "windows", + path: "\\:\\some\\path", + expectedPath: "\\:\\some\\path", + name: "windows path with colon", + }, + } + for _, test := range tests { + path := makeAbsolutePath(test.goos, test.path) + if path != test.expectedPath { + t.Errorf("[%s] Expected %s saw %s", test.name, test.expectedPath, path) + } + } +} + func TestMakeMounts(t *testing.T) { bTrue := true propagationHostToContainer := v1.MountPropagationHostToContainer