mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 11:21:47 +00:00
Only mount subpath as readonly if specified in volumeMount
This commit is contained in:
parent
86ae84b10e
commit
f3f1a04705
@ -206,7 +206,6 @@ func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, h
|
|||||||
VolumePath: volumePath,
|
VolumePath: volumePath,
|
||||||
PodDir: podDir,
|
PodDir: podDir,
|
||||||
ContainerName: container.Name,
|
ContainerName: container.Name,
|
||||||
ReadOnly: mount.ReadOnly || vol.Mounter.GetAttributes().ReadOnly,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Don't pass detailed error back to the user because it could give information about host filesystem
|
// Don't pass detailed error back to the user because it could give information about host filesystem
|
||||||
|
@ -136,8 +136,6 @@ type Subpath struct {
|
|||||||
PodDir string
|
PodDir string
|
||||||
// Name of the container
|
// Name of the container
|
||||||
ContainerName string
|
ContainerName string
|
||||||
// True if the mount needs to be readonly
|
|
||||||
ReadOnly bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exec executes command where mount utilities are. This can be either the host,
|
// Exec executes command where mount utilities are. This can be either the host,
|
||||||
|
@ -884,10 +884,6 @@ func doBindSubPath(mounter Interface, subpath Subpath) (hostPath string, err err
|
|||||||
|
|
||||||
// Do the bind mount
|
// Do the bind mount
|
||||||
options := []string{"bind"}
|
options := []string{"bind"}
|
||||||
if subpath.ReadOnly {
|
|
||||||
options = append(options, "ro")
|
|
||||||
}
|
|
||||||
|
|
||||||
glog.V(5).Infof("bind mounting %q at %q", mountSource, bindPathTarget)
|
glog.V(5).Infof("bind mounting %q at %q", mountSource, bindPathTarget)
|
||||||
if err = mounter.Mount(mountSource, bindPathTarget, "" /*fstype*/, options); err != nil {
|
if err = mounter.Mount(mountSource, bindPathTarget, "" /*fstype*/, options); err != nil {
|
||||||
return "", fmt.Errorf("error mounting %s: %s", subpath.Path, err)
|
return "", fmt.Errorf("error mounting %s: %s", subpath.Path, err)
|
||||||
|
@ -1009,7 +1009,6 @@ func getTestPaths(base string) (string, string) {
|
|||||||
|
|
||||||
func TestBindSubPath(t *testing.T) {
|
func TestBindSubPath(t *testing.T) {
|
||||||
defaultPerm := os.FileMode(0750)
|
defaultPerm := os.FileMode(0750)
|
||||||
readOnlyPerm := os.FileMode(0444)
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@ -1017,7 +1016,6 @@ func TestBindSubPath(t *testing.T) {
|
|||||||
// base.
|
// base.
|
||||||
prepare func(base string) ([]string, string, string, error)
|
prepare func(base string) ([]string, string, string, error)
|
||||||
expectError bool
|
expectError bool
|
||||||
readOnly bool
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "subpath-dir",
|
name: "subpath-dir",
|
||||||
@ -1214,55 +1212,6 @@ func TestBindSubPath(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expectError: false,
|
expectError: false,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "subpath-dir-readonly",
|
|
||||||
prepare: func(base string) ([]string, string, string, error) {
|
|
||||||
volpath, _ := getTestPaths(base)
|
|
||||||
subpath := filepath.Join(volpath, "dir0")
|
|
||||||
return nil, volpath, subpath, os.MkdirAll(subpath, defaultPerm)
|
|
||||||
},
|
|
||||||
expectError: false,
|
|
||||||
readOnly: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "subpath-file-readonly",
|
|
||||||
prepare: func(base string) ([]string, string, string, error) {
|
|
||||||
volpath, _ := getTestPaths(base)
|
|
||||||
subpath := filepath.Join(volpath, "file0")
|
|
||||||
if err := os.MkdirAll(volpath, defaultPerm); err != nil {
|
|
||||||
return nil, "", "", err
|
|
||||||
}
|
|
||||||
return nil, volpath, subpath, ioutil.WriteFile(subpath, []byte{}, defaultPerm)
|
|
||||||
},
|
|
||||||
expectError: false,
|
|
||||||
readOnly: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "subpath-dir-and-volume-readonly",
|
|
||||||
prepare: func(base string) ([]string, string, string, error) {
|
|
||||||
volpath, _ := getTestPaths(base)
|
|
||||||
subpath := filepath.Join(volpath, "dir0")
|
|
||||||
if err := os.MkdirAll(subpath, defaultPerm); err != nil {
|
|
||||||
return nil, "", "", err
|
|
||||||
}
|
|
||||||
return nil, volpath, subpath, os.Chmod(subpath, readOnlyPerm)
|
|
||||||
},
|
|
||||||
expectError: false,
|
|
||||||
readOnly: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "subpath-file-and-vol-readonly",
|
|
||||||
prepare: func(base string) ([]string, string, string, error) {
|
|
||||||
volpath, _ := getTestPaths(base)
|
|
||||||
subpath := filepath.Join(volpath, "file0")
|
|
||||||
if err := os.MkdirAll(volpath, defaultPerm); err != nil {
|
|
||||||
return nil, "", "", err
|
|
||||||
}
|
|
||||||
return nil, volpath, subpath, ioutil.WriteFile(subpath, []byte{}, readOnlyPerm)
|
|
||||||
},
|
|
||||||
expectError: false,
|
|
||||||
readOnly: true,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
@ -1287,7 +1236,6 @@ func TestBindSubPath(t *testing.T) {
|
|||||||
VolumePath: volPath,
|
VolumePath: volPath,
|
||||||
PodDir: filepath.Join(base, "pod0"),
|
PodDir: filepath.Join(base, "pod0"),
|
||||||
ContainerName: testContainer,
|
ContainerName: testContainer,
|
||||||
ReadOnly: test.readOnly,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_, subpathMount := getTestPaths(base)
|
_, subpathMount := getTestPaths(base)
|
||||||
@ -1313,39 +1261,12 @@ func TestBindSubPath(t *testing.T) {
|
|||||||
if err = validateFileExists(subpathMount); err != nil {
|
if err = validateFileExists(subpathMount); err != nil {
|
||||||
t.Errorf("test %q failed: %v", test.name, err)
|
t.Errorf("test %q failed: %v", test.name, err)
|
||||||
}
|
}
|
||||||
if err = validateReadOnlyMount(test.readOnly, bindPathTarget, fm); err != nil {
|
|
||||||
t.Errorf("test %q failed: %v", test.name, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
os.RemoveAll(base)
|
os.RemoveAll(base)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateReadOnlyMount(expectedReadOnly bool, bindPathTarget string, mounter *FakeMounter) error {
|
|
||||||
mps, err := mounter.List()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("fakeMounter.List() returned error: %v", err)
|
|
||||||
}
|
|
||||||
for _, mp := range mps {
|
|
||||||
if mp.Path == bindPathTarget {
|
|
||||||
foundReadOnly := false
|
|
||||||
for _, opts := range mp.Opts {
|
|
||||||
if opts == "ro" {
|
|
||||||
foundReadOnly = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if expectedReadOnly != foundReadOnly {
|
|
||||||
return fmt.Errorf("expected readOnly %v, got %v for mount point %v", expectedReadOnly, foundReadOnly, bindPathTarget)
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fmt.Errorf("failed to find mountPoint %v", bindPathTarget)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseMountInfo(t *testing.T) {
|
func TestParseMountInfo(t *testing.T) {
|
||||||
info :=
|
info :=
|
||||||
`62 0 253:0 / / rw,relatime shared:1 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered
|
`62 0 253:0 / / rw,relatime shared:1 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered
|
||||||
|
@ -110,6 +110,13 @@ var _ = utils.SIGDescribe("Subpath", func() {
|
|||||||
testBasicSubpath(f, "configmap-value", pod)
|
testBasicSubpath(f, "configmap-value", pod)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("should support subpaths with configmap pod with mountPath of existing file", func() {
|
||||||
|
pod := testPodSubpath(f, "configmap-key", "configmap", &v1.VolumeSource{ConfigMap: &v1.ConfigMapVolumeSource{LocalObjectReference: v1.LocalObjectReference{Name: "my-configmap"}}})
|
||||||
|
file := "/etc/resolv.conf"
|
||||||
|
pod.Spec.Containers[0].VolumeMounts[0].MountPath = file
|
||||||
|
testBasicSubpathFile(f, "configmap-value", pod, file)
|
||||||
|
})
|
||||||
|
|
||||||
It("should support subpaths with downward pod", func() {
|
It("should support subpaths with downward pod", func() {
|
||||||
pod := testPodSubpath(f, "downward/podname", "downwardAPI", &v1.VolumeSource{
|
pod := testPodSubpath(f, "downward/podname", "downwardAPI", &v1.VolumeSource{
|
||||||
DownwardAPI: &v1.DownwardAPIVolumeSource{
|
DownwardAPI: &v1.DownwardAPIVolumeSource{
|
||||||
@ -199,7 +206,7 @@ var _ = utils.SIGDescribe("Subpath", func() {
|
|||||||
setInitCommand(pod, fmt.Sprintf("ln -s /bin %s", subPathDir))
|
setInitCommand(pod, fmt.Sprintf("ln -s /bin %s", subPathDir))
|
||||||
|
|
||||||
// Pod should fail
|
// Pod should fail
|
||||||
testPodFailSupath(f, pod)
|
testPodFailSubpath(f, pod)
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should fail if subpath file is outside the volume [Slow]", func() {
|
It("should fail if subpath file is outside the volume [Slow]", func() {
|
||||||
@ -207,7 +214,7 @@ var _ = utils.SIGDescribe("Subpath", func() {
|
|||||||
setInitCommand(pod, fmt.Sprintf("ln -s /bin/sh %s", subPathDir))
|
setInitCommand(pod, fmt.Sprintf("ln -s /bin/sh %s", subPathDir))
|
||||||
|
|
||||||
// Pod should fail
|
// Pod should fail
|
||||||
testPodFailSupath(f, pod)
|
testPodFailSubpath(f, pod)
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should fail if non-existent subpath is outside the volume [Slow]", func() {
|
It("should fail if non-existent subpath is outside the volume [Slow]", func() {
|
||||||
@ -215,7 +222,7 @@ var _ = utils.SIGDescribe("Subpath", func() {
|
|||||||
setInitCommand(pod, fmt.Sprintf("ln -s /bin/notanexistingpath %s", subPathDir))
|
setInitCommand(pod, fmt.Sprintf("ln -s /bin/notanexistingpath %s", subPathDir))
|
||||||
|
|
||||||
// Pod should fail
|
// Pod should fail
|
||||||
testPodFailSupath(f, pod)
|
testPodFailSubpath(f, pod)
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should fail if subpath with backstepping is outside the volume [Slow]", func() {
|
It("should fail if subpath with backstepping is outside the volume [Slow]", func() {
|
||||||
@ -223,7 +230,7 @@ var _ = utils.SIGDescribe("Subpath", func() {
|
|||||||
setInitCommand(pod, fmt.Sprintf("ln -s ../ %s", subPathDir))
|
setInitCommand(pod, fmt.Sprintf("ln -s ../ %s", subPathDir))
|
||||||
|
|
||||||
// Pod should fail
|
// Pod should fail
|
||||||
testPodFailSupath(f, pod)
|
testPodFailSubpath(f, pod)
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should support creating multiple subpath from same volumes [Slow]", func() {
|
It("should support creating multiple subpath from same volumes [Slow]", func() {
|
||||||
@ -297,7 +304,7 @@ var _ = utils.SIGDescribe("Subpath", func() {
|
|||||||
testReadFile(f, volumePath, pod, 0)
|
testReadFile(f, volumePath, pod, 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should support readOnly directory specified in the volumeSource", func() {
|
It("should support existing directories when readOnly specified in the volumeSource", func() {
|
||||||
roVol := vol.getReadOnlyVolumeSpec()
|
roVol := vol.getReadOnlyVolumeSpec()
|
||||||
if roVol == nil {
|
if roVol == nil {
|
||||||
framework.Skipf("Volume type %v doesn't support readOnly source", curVolType)
|
framework.Skipf("Volume type %v doesn't support readOnly source", curVolType)
|
||||||
@ -312,6 +319,19 @@ var _ = utils.SIGDescribe("Subpath", func() {
|
|||||||
// Read it from inside the subPath from container 0
|
// Read it from inside the subPath from container 0
|
||||||
testReadFile(f, filePathInSubpath, pod, 0)
|
testReadFile(f, filePathInSubpath, pod, 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("should fail for new directories when readOnly specified in the volumeSource", func() {
|
||||||
|
roVol := vol.getReadOnlyVolumeSpec()
|
||||||
|
if roVol == nil {
|
||||||
|
framework.Skipf("Volume type %v doesn't support readOnly source", curVolType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set volume source to read only
|
||||||
|
pod.Spec.Volumes[0].VolumeSource = *roVol
|
||||||
|
|
||||||
|
// Pod should fail
|
||||||
|
testPodFailSubpathError(f, pod, "")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,7 +339,11 @@ var _ = utils.SIGDescribe("Subpath", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
func testBasicSubpath(f *framework.Framework, contents string, pod *v1.Pod) {
|
func testBasicSubpath(f *framework.Framework, contents string, pod *v1.Pod) {
|
||||||
setReadCommand(volumePath, &pod.Spec.Containers[0])
|
testBasicSubpathFile(f, contents, pod, volumePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBasicSubpathFile(f *framework.Framework, contents string, pod *v1.Pod, filepath string) {
|
||||||
|
setReadCommand(filepath, &pod.Spec.Containers[0])
|
||||||
|
|
||||||
By(fmt.Sprintf("Creating pod %s", pod.Name))
|
By(fmt.Sprintf("Creating pod %s", pod.Name))
|
||||||
f.TestContainerOutput("atomic-volume-subpath", pod, 0, []string{contents})
|
f.TestContainerOutput("atomic-volume-subpath", pod, 0, []string{contents})
|
||||||
@ -412,6 +436,11 @@ func testPodSubpath(f *framework.Framework, subpath, volumeType string, source *
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
SecurityContext: &v1.PodSecurityContext{
|
||||||
|
SELinuxOptions: &v1.SELinuxOptions{
|
||||||
|
Level: "s0:c0,c1",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -473,7 +502,11 @@ func testReadFile(f *framework.Framework, file string, pod *v1.Pod, containerInd
|
|||||||
Expect(err).NotTo(HaveOccurred(), "while deleting pod")
|
Expect(err).NotTo(HaveOccurred(), "while deleting pod")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPodFailSupath(f *framework.Framework, pod *v1.Pod) {
|
func testPodFailSubpath(f *framework.Framework, pod *v1.Pod) {
|
||||||
|
testPodFailSubpathError(f, pod, "subPath")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testPodFailSubpathError(f *framework.Framework, pod *v1.Pod, errorMsg string) {
|
||||||
By(fmt.Sprintf("Creating pod %s", pod.Name))
|
By(fmt.Sprintf("Creating pod %s", pod.Name))
|
||||||
pod, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(pod)
|
pod, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(pod)
|
||||||
Expect(err).ToNot(HaveOccurred(), "while creating pod")
|
Expect(err).ToNot(HaveOccurred(), "while creating pod")
|
||||||
@ -494,7 +527,7 @@ func testPodFailSupath(f *framework.Framework, pod *v1.Pod) {
|
|||||||
events, err := f.ClientSet.CoreV1().Events(f.Namespace.Name).List(options)
|
events, err := f.ClientSet.CoreV1().Events(f.Namespace.Name).List(options)
|
||||||
Expect(err).NotTo(HaveOccurred(), "while getting pod events")
|
Expect(err).NotTo(HaveOccurred(), "while getting pod events")
|
||||||
Expect(len(events.Items)).NotTo(Equal(0), "no events found")
|
Expect(len(events.Items)).NotTo(Equal(0), "no events found")
|
||||||
Expect(events.Items[0].Message).To(ContainSubstring("subPath"), "subpath error not found")
|
Expect(events.Items[0].Message).To(ContainSubstring(errorMsg), fmt.Sprintf("%q error not found", errorMsg))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that the existing subpath mount is detected when a container restarts
|
// Tests that the existing subpath mount is detected when a container restarts
|
||||||
@ -785,6 +818,47 @@ func (s *gcepdPVCSource) createVolume(f *framework.Framework) volInfo {
|
|||||||
s.pvc, err = framework.CreatePVC(f.ClientSet, f.Namespace.Name, pvc)
|
s.pvc, err = framework.CreatePVC(f.ClientSet, f.Namespace.Name, pvc)
|
||||||
framework.ExpectNoError(err, "Error creating PVC")
|
framework.ExpectNoError(err, "Error creating PVC")
|
||||||
|
|
||||||
|
// Launch pod to format the PD first
|
||||||
|
pod := &v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: fmt.Sprintf("gcepd-prep-%s", f.Namespace.Name),
|
||||||
|
},
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: fmt.Sprintf("init-volume-%s", f.Namespace.Name),
|
||||||
|
Image: "busybox",
|
||||||
|
Command: []string{"/bin/sh", "-ec", "echo nothing"},
|
||||||
|
VolumeMounts: []v1.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: volumeName,
|
||||||
|
MountPath: "/vol",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RestartPolicy: v1.RestartPolicyNever,
|
||||||
|
Volumes: []v1.Volume{
|
||||||
|
{
|
||||||
|
Name: volumeName,
|
||||||
|
VolumeSource: v1.VolumeSource{
|
||||||
|
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
|
||||||
|
ClaimName: s.pvc.Name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
pod, err = f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(pod)
|
||||||
|
Expect(err).ToNot(HaveOccurred(), "while creating gce pd init pod")
|
||||||
|
|
||||||
|
err = framework.WaitForPodSuccessInNamespace(f.ClientSet, pod.Name, pod.Namespace)
|
||||||
|
Expect(err).ToNot(HaveOccurred(), "while waiting for gce pd init pod to succeed")
|
||||||
|
|
||||||
|
err = framework.DeletePodWithWait(f, f.ClientSet, pod)
|
||||||
|
Expect(err).ToNot(HaveOccurred(), "while deleting gce pd init pod")
|
||||||
|
|
||||||
return volInfo{
|
return volInfo{
|
||||||
source: &v1.VolumeSource{
|
source: &v1.VolumeSource{
|
||||||
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
|
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
|
||||||
|
Loading…
Reference in New Issue
Block a user