Skip to content

Commit

Permalink
Merge pull request #142 from miaoyq/fixes-131
Browse files Browse the repository at this point in the history
Support mount propagation validation
  • Loading branch information
feiskyer authored Sep 21, 2017
2 parents 4b9205d + 41bb1e3 commit 8962063
Showing 1 changed file with 205 additions and 1 deletion.
206 changes: 205 additions & 1 deletion pkg/validate/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ import (
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"time"

"github.com/docker/docker/pkg/jsonlog"
"github.com/kubernetes-incubator/cri-tools/pkg/framework"
"golang.org/x/sys/unix"
internalapi "k8s.io/kubernetes/pkg/kubelet/apis/cri"
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"

Expand Down Expand Up @@ -201,6 +203,124 @@ var _ = framework.KubeDescribe("Container", func() {
verifyLogContents(podConfig, logPath, expectedLogMessage)
})
})

Context("runtime should support mount propagation", func() {
var podID string
var podConfig *runtimeapi.PodSandboxConfig

BeforeEach(func() {
podID, podConfig = createPrivilegedPodSandbox(rc, true)
})

AfterEach(func() {
By("stop PodSandbox")
rc.StopPodSandbox(podID)
By("delete PodSandbox")
rc.RemovePodSandbox(podID)
})

It("mount with 'rprivate' should not support propagation", func() {
By("create host path and flag file")
mntSource, propagationSrcDir, propagationMntPoint, clearHostPath := createHostPathForMountPropagation(podID, runtimeapi.MountPropagation_PROPAGATION_PRIVATE)
defer clearHostPath() // clean up the TempDir

By("create container with volume")
containerID := createMountPropagationContainer(rc, ic, "mount-propagation-test-", podID, podConfig, mntSource, runtimeapi.MountPropagation_PROPAGATION_PRIVATE)

By("test start container with volume")
testStartContainer(rc, containerID)

By("create a propatation mount point in host")
createPropagationMountPoint(propagationSrcDir, propagationMntPoint)

By("check whether propagationMntPoint contains file or dir in container")
command := []string{"ls", "-A", propagationMntPoint}
output := execSyncContainer(rc, containerID, command)
Expect(len(output)).To(BeZero(), "len(output) should be zero.")

By("create a directory named containerMntPoint as a mount point in container")
containerMntPoint := path.Join(mntSource, "containerMntPoint")
command = []string{"sh", "-c", "mkdir -p " + containerMntPoint}
execSyncContainer(rc, containerID, command)

By("mount /etc to the mount point in container")
command = []string{"sh", "-c", "mount --bind /etc " + containerMntPoint}
execSyncContainer(rc, containerID, command)

By("check whether containerMntPoint contains file or dir in host")
fileInfo, err := ioutil.ReadDir(containerMntPoint)
framework.ExpectNoError(err, "failed to ReadDir %q in Host", containerMntPoint)
Expect(len(fileInfo)).To(BeZero(), "len(fileInfo) should be zero.")
})

It("mount with 'rshared' should support propagation from host to container and vice versa", func() {
By("create host path and flag file")
mntSource, propagationSrcDir, propagationMntPoint, clearHostPath := createHostPathForMountPropagation(podID, runtimeapi.MountPropagation_PROPAGATION_BIDIRECTIONAL)
defer clearHostPath() // clean up the TempDir

By("create container with volume")
containerID := createMountPropagationContainer(rc, ic, "mount-propagation-test-", podID, podConfig, mntSource, runtimeapi.MountPropagation_PROPAGATION_BIDIRECTIONAL)

By("test start container with volume")
testStartContainer(rc, containerID)

By("create a propatation mount point in host")
createPropagationMountPoint(propagationSrcDir, propagationMntPoint)

By("check whether propagationMntPoint contains file or dir in container")
command := []string{"ls", "-A", propagationMntPoint}
output := execSyncContainer(rc, containerID, command)
Expect(len(output)).NotTo(BeZero(), "len(output) should not be zero.")

By("create a directory named containerMntPoint as a mount point in container")
containerMntPoint := path.Join(mntSource, "containerMntPoint")
command = []string{"sh", "-c", "mkdir -p " + containerMntPoint}
execSyncContainer(rc, containerID, command)

By("mount /etc to the mount point in container")
command = []string{"sh", "-c", "mount --bind /etc " + containerMntPoint}
execSyncContainer(rc, containerID, command)

By("check whether containerMntPoint contains file or dir in host")
fileInfo, err := ioutil.ReadDir(containerMntPoint)
framework.ExpectNoError(err, "failed to ReadDir %q in Host", containerMntPoint)
Expect(len(fileInfo)).NotTo(BeZero(), "len(fileInfo) should not be zero.")
})

It("mount with 'rslave' should support propagation from host to container", func() {
By("create host path and flag file")
mntSource, propagationSrcDir, propagationMntPoint, clearHostPath := createHostPathForMountPropagation(podID, runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER)
defer clearHostPath() // clean up the TempDir

By("create container with volume")
containerID := createMountPropagationContainer(rc, ic, "mount-propagation-test-", podID, podConfig, mntSource, runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER)

By("test start container with volume")
testStartContainer(rc, containerID)

By("create a propatation mount point in host")
createPropagationMountPoint(propagationSrcDir, propagationMntPoint)

By("check whether propagationMntPoint contains file or dir in container")
command := []string{"ls", "-A", propagationMntPoint}
output := execSyncContainer(rc, containerID, command)
Expect(len(output)).NotTo(BeZero(), "len(output) should not be zero.")

By("create a directory named containerMntPoint as a mount point in container")
containerMntPoint := path.Join(mntSource, "containerMntPoint")
command = []string{"sh", "-c", "mkdir -p " + containerMntPoint}
execSyncContainer(rc, containerID, command)

By("mount /etc to the mount point in container")
command = []string{"sh", "-c", "mount --bind /etc " + containerMntPoint}
execSyncContainer(rc, containerID, command)

By("check whether containerMntPoint contains file or dir in host")
fileInfo, err := ioutil.ReadDir(containerMntPoint)
framework.ExpectNoError(err, "failed to ReadDir %q in Host", containerMntPoint)
Expect(len(fileInfo)).To(BeZero(), "len(fileInfo) should be zero.")
})
})
})

// containerFound returns whether containers is found.
Expand Down Expand Up @@ -339,7 +459,7 @@ func createHostPath(podID string) (string, string) {
return hostPath, flagFile
}

// createVolContainerOrFail creates a container with volume and the prefix of containerName and fails if it gets error.
// createVolumeContainer creates a container with volume and the prefix of containerName and fails if it gets error.
func createVolumeContainer(rc internalapi.RuntimeService, ic internalapi.ImageManagerService, prefix string, podID string, podConfig *runtimeapi.PodSandboxConfig, hostPath, flagFile string) string {
By("create a container with volume and name")
containerName := prefix + framework.NewUUID()
Expand Down Expand Up @@ -446,3 +566,87 @@ func verifyLogContents(podConfig *runtimeapi.PodSandboxConfig, logPath string, e
Expect(string(msg.stream)).To(Equal(string(expectedLogMessage.stream)), "Stream should be %s", string(expectedLogMessage.stream))
}
}

// createHostPath creates the hostPath for mount propagation test.
func createHostPathForMountPropagation(podID string, propagationOpt runtimeapi.MountPropagation) (string, string, string, func()) {
hostPath, err := ioutil.TempDir("", "/test"+podID)
framework.ExpectNoError(err, "failed to create TempDir %q: %v", hostPath, err)

mntSource := filepath.Join(hostPath, "mnt")
propagationMntPoint := filepath.Join(mntSource, "propagationMnt")
err = os.MkdirAll(propagationMntPoint, 0700)
framework.ExpectNoError(err, "failed to create volume dir %q: %v", propagationMntPoint, err)

propagationSrcDir := filepath.Join(hostPath, "propagationSrcDir")
err = os.MkdirAll(propagationSrcDir, 0700)
framework.ExpectNoError(err, "failed to create volume dir %q: %v", propagationSrcDir, err)

_, err = os.Create(filepath.Join(propagationSrcDir, "flagFile"))
framework.ExpectNoError(err, "failed to create volume file \"flagFile\": %v", err)

switch propagationOpt {
case runtimeapi.MountPropagation_PROPAGATION_PRIVATE:
err := unix.Mount(mntSource, mntSource, "bind", unix.MS_BIND|unix.MS_REC, "")
framework.ExpectNoError(err, "failed to mount \"mntSource\": %v", err)
err = unix.Mount("", mntSource, "", unix.MS_PRIVATE|unix.MS_REC, "")
framework.ExpectNoError(err, "failed to set \"mntSource\" to \"rprivate\": %v", err)
case runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER,
runtimeapi.MountPropagation_PROPAGATION_BIDIRECTIONAL:
err := unix.Mount(mntSource, mntSource, "bind", unix.MS_BIND|unix.MS_REC, "")
framework.ExpectNoError(err, "failed to mount \"mntSource\": %v", err)
err = unix.Mount("", mntSource, "", unix.MS_SHARED|unix.MS_REC, "")
framework.ExpectNoError(err, "failed to set \"mntSource\" to \"rprivate\": %v", err)
default:
err := unix.Mount(mntSource, mntSource, "bind", unix.MS_BIND|unix.MS_REC, "")
framework.ExpectNoError(err, "failed to mount \"mntSource\": %v", err)
err = unix.Mount("", mntSource, "", unix.MS_PRIVATE|unix.MS_REC, "")
framework.ExpectNoError(err, "failed to set \"mntSource\" to \"rprivate\": %v", err)
}

clearHostPath := func() {
By("clean up the TempDir")
err := unix.Unmount(propagationMntPoint, unix.MNT_DETACH)
framework.ExpectNoError(err, "failed to unmount \"propagationMntPoint\": %v", err)
err = unix.Unmount(mntSource, unix.MNT_DETACH)
framework.ExpectNoError(err, "failed to unmount \"mntSource\": %v", err)

os.RemoveAll(hostPath)
framework.ExpectNoError(err, "failed to remove \"hostPath\": %v", err)
}

return mntSource, propagationSrcDir, propagationMntPoint, clearHostPath
}

// createMountPropagationContainer creates a container with volume and Privileged and fails if it gets error.
func createMountPropagationContainer(rc internalapi.RuntimeService, ic internalapi.ImageManagerService, prefix string, podID string,
podConfig *runtimeapi.PodSandboxConfig, hostPath string, PropagationOpt runtimeapi.MountPropagation) string {
By("create a container with volume and name")
containerName := prefix + framework.NewUUID()
containerConfig := &runtimeapi.ContainerConfig{
Metadata: framework.BuildContainerMetadata(containerName, framework.DefaultAttempt),
Image: &runtimeapi.ImageSpec{Image: framework.DefaultContainerImage},
Command: []string{"sh", "-c", "top"},
// Set Privileged in order to executing mount command in container
Linux: &runtimeapi.LinuxContainerConfig{
SecurityContext: &runtimeapi.LinuxContainerSecurityContext{
Privileged: true,
},
},
Mounts: []*runtimeapi.Mount{
{
HostPath: hostPath,
ContainerPath: hostPath,
Propagation: PropagationOpt,
},
},
}

return framework.CreateContainer(rc, ic, containerConfig, podID, podConfig)
}

// createPropagationMountPoint mount "propagationSrcDir" at "propagationMntPoint",
// this will be used to check whether mount can be propagated from host to container or not.
func createPropagationMountPoint(propagationSrcDir, propagationMntPoint string) {
err := unix.Mount(propagationSrcDir, propagationMntPoint, "bind", unix.MS_BIND|unix.MS_REC, "")
framework.ExpectNoError(err, "failed to mount \"propagationMntPoint\": %v", err)
}

0 comments on commit 8962063

Please sign in to comment.