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.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.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/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"`
 | 
						ReadOnly bool `json:"readOnly,omitempty"`
 | 
				
			||||||
	// Required. Must not contain ':'.
 | 
						// Required. Must not contain ':'.
 | 
				
			||||||
	MountPath string `json:"mountPath"`
 | 
						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.
 | 
					// 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
 | 
						// Path within the container at which the volume should be mounted.  Must
 | 
				
			||||||
	// not contain ':'.
 | 
						// not contain ':'.
 | 
				
			||||||
	MountPath string `json:"mountPath" protobuf:"bytes,3,opt,name=mountPath"`
 | 
						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.
 | 
					// EnvVar represents an environment variable present in a Container.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -746,6 +746,28 @@ func validateDownwardAPIVolumeSource(downwardAPIVolume *api.DownwardAPIVolumeSou
 | 
				
			|||||||
	return allErrs
 | 
						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:
 | 
					// This validate will make sure targetPath:
 | 
				
			||||||
// 1. is not abs path
 | 
					// 1. is not abs path
 | 
				
			||||||
// 2. does not contain '..'
 | 
					// 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"))
 | 
								allErrs = append(allErrs, field.Invalid(idxPath.Child("mountPath"), mnt.MountPath, "must be unique"))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		mountpoints.Insert(mnt.MountPath)
 | 
							mountpoints.Insert(mnt.MountPath)
 | 
				
			||||||
 | 
							if len(mnt.SubPath) > 0 {
 | 
				
			||||||
 | 
								allErrs = append(allErrs, validateSubPath(mnt.SubPath, fldPath.Child("subPath"))...)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return allErrs
 | 
						return allErrs
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1274,6 +1274,10 @@ func TestValidateVolumeMounts(t *testing.T) {
 | 
				
			|||||||
		{Name: "abc", MountPath: "/foo"},
 | 
							{Name: "abc", MountPath: "/foo"},
 | 
				
			||||||
		{Name: "123", MountPath: "/bar"},
 | 
							{Name: "123", MountPath: "/bar"},
 | 
				
			||||||
		{Name: "abc-123", MountPath: "/baz"},
 | 
							{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 {
 | 
						if errs := validateVolumeMounts(successCase, volumes, field.NewPath("field")); len(errs) != 0 {
 | 
				
			||||||
		t.Errorf("expected success: %v", errs)
 | 
							t.Errorf("expected success: %v", errs)
 | 
				
			||||||
@@ -1285,6 +1289,10 @@ func TestValidateVolumeMounts(t *testing.T) {
 | 
				
			|||||||
		"empty mountpath":     {{Name: "abc", MountPath: ""}},
 | 
							"empty mountpath":     {{Name: "abc", MountPath: ""}},
 | 
				
			||||||
		"colon mountpath":     {{Name: "abc", MountPath: "foo:bar"}},
 | 
							"colon mountpath":     {{Name: "abc", MountPath: "foo:bar"}},
 | 
				
			||||||
		"mountpath collision": {{Name: "foo", MountPath: "/path/a"}, {Name: "bar", MountPath: "/path/a"}},
 | 
							"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 {
 | 
						for k, v := range errorCases {
 | 
				
			||||||
		if errs := validateVolumeMounts(v, volumes, field.NewPath("field")); len(errs) == 0 {
 | 
							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
 | 
								vol.SELinuxLabeled = true
 | 
				
			||||||
			relabelVolume = true
 | 
								relabelVolume = true
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							hostPath := vol.Mounter.GetPath()
 | 
				
			||||||
 | 
							if mount.SubPath != "" {
 | 
				
			||||||
 | 
								hostPath = filepath.Join(hostPath, mount.SubPath)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		mounts = append(mounts, kubecontainer.Mount{
 | 
							mounts = append(mounts, kubecontainer.Mount{
 | 
				
			||||||
			Name:           mount.Name,
 | 
								Name:           mount.Name,
 | 
				
			||||||
			ContainerPath:  mount.MountPath,
 | 
								ContainerPath:  mount.MountPath,
 | 
				
			||||||
			HostPath:       vol.Mounter.GetPath(),
 | 
								HostPath:       hostPath,
 | 
				
			||||||
			ReadOnly:       mount.ReadOnly,
 | 
								ReadOnly:       mount.ReadOnly,
 | 
				
			||||||
			SELinuxRelabel: relabelVolume,
 | 
								SELinuxRelabel: relabelVolume,
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -88,6 +88,37 @@ var _ = framework.KubeDescribe("hostPath", func() {
 | 
				
			|||||||
		}, namespace.Name,
 | 
							}, 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.
 | 
					//These constants are borrowed from the other test.
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user