mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Introduce subPath in VolumeMount
This commit is contained in:
		@@ -92,3 +92,4 @@ test/e2e/host_path.go:			fmt.Sprintf("--retry_time=%d", retryDuration),
 | 
			
		||||
test/images/mount-tester/mt.go:	flag.BoolVar(&breakOnExpectedContent, "break_on_expected_content", true, "Break out of loop on expected content, (use with --file_content_in_loop flag only)")
 | 
			
		||||
test/images/mount-tester/mt.go:	flag.IntVar(&retryDuration, "retry_time", 180, "Retry time during the loop")
 | 
			
		||||
test/images/mount-tester/mt.go:	flag.StringVar(&readFileContentInLoopPath, "file_content_in_loop", "", "Path to read the file content in loop from")
 | 
			
		||||
test/e2e/host_path.go:			fmt.Sprintf("--file_content_in_loop=%v", filePathInReader),
 | 
			
		||||
 
 | 
			
		||||
@@ -758,6 +758,9 @@ type VolumeMount struct {
 | 
			
		||||
	ReadOnly bool `json:"readOnly,omitempty"`
 | 
			
		||||
	// Required. Must not contain ':'.
 | 
			
		||||
	MountPath string `json:"mountPath"`
 | 
			
		||||
	// Path within the volume from which the container's volume should be mounted.
 | 
			
		||||
	// Defaults to "" (volume's root).
 | 
			
		||||
	SubPath string `json:"subPath,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EnvVar represents an environment variable present in a Container.
 | 
			
		||||
 
 | 
			
		||||
@@ -883,6 +883,9 @@ type VolumeMount struct {
 | 
			
		||||
	// Path within the container at which the volume should be mounted.  Must
 | 
			
		||||
	// not contain ':'.
 | 
			
		||||
	MountPath string `json:"mountPath" protobuf:"bytes,3,opt,name=mountPath"`
 | 
			
		||||
	// Path within the volume from which the container's volume should be mounted.
 | 
			
		||||
	// Defaults to "" (volume's root).
 | 
			
		||||
	SubPath string `json:"subPath,omitempty" protobuf:"bytes,4,opt,name=subPath"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EnvVar represents an environment variable present in a Container.
 | 
			
		||||
 
 | 
			
		||||
@@ -746,6 +746,28 @@ func validateDownwardAPIVolumeSource(downwardAPIVolume *api.DownwardAPIVolumeSou
 | 
			
		||||
	return allErrs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This validate will make sure targetPath:
 | 
			
		||||
// 1. is not abs path
 | 
			
		||||
// 2. does not start with '../'
 | 
			
		||||
// 3. does not contain '/../'
 | 
			
		||||
// 4. does not end with '/..'
 | 
			
		||||
func validateSubPath(targetPath string, fldPath *field.Path) field.ErrorList {
 | 
			
		||||
	allErrs := field.ErrorList{}
 | 
			
		||||
	if path.IsAbs(targetPath) {
 | 
			
		||||
		allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must be a relative path"))
 | 
			
		||||
	}
 | 
			
		||||
	if strings.HasPrefix(targetPath, "../") {
 | 
			
		||||
		allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must not start with '../'"))
 | 
			
		||||
	}
 | 
			
		||||
	if strings.Contains(targetPath, "/../") {
 | 
			
		||||
		allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must not contain '/../'"))
 | 
			
		||||
	}
 | 
			
		||||
	if strings.HasSuffix(targetPath, "/..") {
 | 
			
		||||
		allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must not end with '/..'"))
 | 
			
		||||
	}
 | 
			
		||||
	return allErrs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This validate will make sure targetPath:
 | 
			
		||||
// 1. is not abs path
 | 
			
		||||
// 2. does not contain '..'
 | 
			
		||||
@@ -1168,6 +1190,9 @@ func validateVolumeMounts(mounts []api.VolumeMount, volumes sets.String, fldPath
 | 
			
		||||
			allErrs = append(allErrs, field.Invalid(idxPath.Child("mountPath"), mnt.MountPath, "must be unique"))
 | 
			
		||||
		}
 | 
			
		||||
		mountpoints.Insert(mnt.MountPath)
 | 
			
		||||
		if len(mnt.SubPath) > 0 {
 | 
			
		||||
			allErrs = append(allErrs, validateSubPath(mnt.SubPath, fldPath.Child("subPath"))...)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return allErrs
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1274,6 +1274,10 @@ func TestValidateVolumeMounts(t *testing.T) {
 | 
			
		||||
		{Name: "abc", MountPath: "/foo"},
 | 
			
		||||
		{Name: "123", MountPath: "/bar"},
 | 
			
		||||
		{Name: "abc-123", MountPath: "/baz"},
 | 
			
		||||
		{Name: "abc-123", MountPath: "/baa", SubPath: ""},
 | 
			
		||||
		{Name: "abc-123", MountPath: "/bab", SubPath: "baz"},
 | 
			
		||||
		{Name: "abc-123", MountPath: "/bac", SubPath: ".baz"},
 | 
			
		||||
		{Name: "abc-123", MountPath: "/bad", SubPath: "..baz"},
 | 
			
		||||
	}
 | 
			
		||||
	if errs := validateVolumeMounts(successCase, volumes, field.NewPath("field")); len(errs) != 0 {
 | 
			
		||||
		t.Errorf("expected success: %v", errs)
 | 
			
		||||
@@ -1285,6 +1289,10 @@ func TestValidateVolumeMounts(t *testing.T) {
 | 
			
		||||
		"empty mountpath":     {{Name: "abc", MountPath: ""}},
 | 
			
		||||
		"colon mountpath":     {{Name: "abc", MountPath: "foo:bar"}},
 | 
			
		||||
		"mountpath collision": {{Name: "foo", MountPath: "/path/a"}, {Name: "bar", MountPath: "/path/a"}},
 | 
			
		||||
		"absolute subpath":    {{Name: "abc", MountPath: "/bar", SubPath: "/baz"}},
 | 
			
		||||
		"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: "./.."}},
 | 
			
		||||
	}
 | 
			
		||||
	for k, v := range errorCases {
 | 
			
		||||
		if errs := validateVolumeMounts(v, volumes, field.NewPath("field")); len(errs) == 0 {
 | 
			
		||||
 
 | 
			
		||||
@@ -1176,10 +1176,14 @@ func makeMounts(pod *api.Pod, podDir string, container *api.Container, hostName,
 | 
			
		||||
			vol.SELinuxLabeled = true
 | 
			
		||||
			relabelVolume = true
 | 
			
		||||
		}
 | 
			
		||||
		hostPath := vol.Mounter.GetPath()
 | 
			
		||||
		if mount.SubPath != "" {
 | 
			
		||||
			hostPath = filepath.Join(hostPath, mount.SubPath)
 | 
			
		||||
		}
 | 
			
		||||
		mounts = append(mounts, kubecontainer.Mount{
 | 
			
		||||
			Name:           mount.Name,
 | 
			
		||||
			ContainerPath:  mount.MountPath,
 | 
			
		||||
			HostPath:       vol.Mounter.GetPath(),
 | 
			
		||||
			HostPath:       hostPath,
 | 
			
		||||
			ReadOnly:       mount.ReadOnly,
 | 
			
		||||
			SELinuxRelabel: relabelVolume,
 | 
			
		||||
		})
 | 
			
		||||
 
 | 
			
		||||
@@ -88,6 +88,37 @@ var _ = framework.KubeDescribe("hostPath", func() {
 | 
			
		||||
		}, namespace.Name,
 | 
			
		||||
		)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	It("should support subPath [Conformance]", func() {
 | 
			
		||||
		volumePath := "/test-volume"
 | 
			
		||||
		subPath := "sub-path"
 | 
			
		||||
		fileName := "test-file"
 | 
			
		||||
		retryDuration := 180
 | 
			
		||||
 | 
			
		||||
		filePathInWriter := path.Join(volumePath, fileName)
 | 
			
		||||
		filePathInReader := path.Join(volumePath, subPath, fileName)
 | 
			
		||||
 | 
			
		||||
		source := &api.HostPathVolumeSource{
 | 
			
		||||
			Path: "/tmp",
 | 
			
		||||
		}
 | 
			
		||||
		pod := testPodWithHostVol(volumePath, source)
 | 
			
		||||
		// Write the file in the subPath from container 0
 | 
			
		||||
		container := &pod.Spec.Containers[0]
 | 
			
		||||
		container.VolumeMounts[0].SubPath = subPath
 | 
			
		||||
		container.Args = []string{
 | 
			
		||||
			fmt.Sprintf("--new_file_0644=%v", filePathInWriter),
 | 
			
		||||
			fmt.Sprintf("--file_mode=%v", filePathInWriter),
 | 
			
		||||
		}
 | 
			
		||||
		// Read it from outside the subPath from container 1
 | 
			
		||||
		pod.Spec.Containers[1].Args = []string{
 | 
			
		||||
			fmt.Sprintf("--file_content_in_loop=%v", filePathInReader),
 | 
			
		||||
			fmt.Sprintf("--retry_time=%d", retryDuration),
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		framework.TestContainerOutput("hostPath subPath", c, pod, 1, []string{
 | 
			
		||||
			"content of file \"" + filePathInReader + "\": mount-tester new file",
 | 
			
		||||
		}, namespace.Name)
 | 
			
		||||
	})
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
//These constants are borrowed from the other test.
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user