diff --git a/pkg/kubelet/dockershim/helpers.go b/pkg/kubelet/dockershim/helpers.go index c82da109280..5586e2a79c7 100644 --- a/pkg/kubelet/dockershim/helpers.go +++ b/pkg/kubelet/dockershim/helpers.go @@ -124,29 +124,40 @@ func extractLabels(input map[string]string) (map[string]string, map[string]strin // generateMountBindings converts the mount list to a list of strings that // can be understood by docker. -// Each element in the string is in the form of: -// ':', or -// '::ro', if the path is read only, or -// '::Z', if the volume requires SELinux -// relabeling and the pod provides an SELinux label +// ':[:options]', where 'options' +// is a comma-separated list of the following strings: +// 'ro', if the path is read only +// 'Z', if the volume requires SELinux relabeling +// propagation mode such as 'rslave' func generateMountBindings(mounts []*runtimeapi.Mount) []string { result := make([]string, 0, len(mounts)) for _, m := range mounts { bind := fmt.Sprintf("%s:%s", m.HostPath, m.ContainerPath) - readOnly := m.Readonly - if readOnly { - bind += ":ro" + var attrs []string + if m.Readonly { + attrs = append(attrs, "ro") } // Only request relabeling if the pod provides an SELinux context. If the pod // does not provide an SELinux context relabeling will label the volume with // the container's randomly allocated MCS label. This would restrict access // to the volume to the container which mounts it first. if m.SelinuxRelabel { - if readOnly { - bind += ",Z" - } else { - bind += ":Z" - } + attrs = append(attrs, "Z") + } + switch m.Propagation { + case runtimeapi.MountPropagation_PROPAGATION_PRIVATE: + // noop, private is default + case runtimeapi.MountPropagation_PROPAGATION_BIDIRECTIONAL: + attrs = append(attrs, "rshared") + case runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER: + attrs = append(attrs, "rslave") + default: + glog.Warningf("unknown propagation mode for hostPath %q", m.HostPath) + // Falls back to "private" + } + + if len(attrs) > 0 { + bind = fmt.Sprintf("%s:%s", bind, strings.Join(attrs, ",")) } result = append(result, bind) } diff --git a/pkg/kubelet/dockershim/helpers_test.go b/pkg/kubelet/dockershim/helpers_test.go index 935cda889c9..7dd519e02b2 100644 --- a/pkg/kubelet/dockershim/helpers_test.go +++ b/pkg/kubelet/dockershim/helpers_test.go @@ -323,3 +323,70 @@ func TestMakePortsAndBindings(t *testing.T) { assert.Equal(t, test.portmappings, actualPortMappings) } } + +func TestGenerateMountBindings(t *testing.T) { + mounts := []*runtimeapi.Mount{ + // everything default + { + HostPath: "/mnt/1", + ContainerPath: "/var/lib/mysql/1", + }, + // readOnly + { + HostPath: "/mnt/2", + ContainerPath: "/var/lib/mysql/2", + Readonly: true, + }, + // SELinux + { + HostPath: "/mnt/3", + ContainerPath: "/var/lib/mysql/3", + SelinuxRelabel: true, + }, + // Propagation private + { + HostPath: "/mnt/4", + ContainerPath: "/var/lib/mysql/4", + Propagation: runtimeapi.MountPropagation_PROPAGATION_PRIVATE, + }, + // Propagation rslave + { + HostPath: "/mnt/5", + ContainerPath: "/var/lib/mysql/5", + Propagation: runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER, + }, + // Propagation rshared + { + HostPath: "/mnt/6", + ContainerPath: "/var/lib/mysql/6", + Propagation: runtimeapi.MountPropagation_PROPAGATION_BIDIRECTIONAL, + }, + // Propagation unknown (falls back to private) + { + HostPath: "/mnt/7", + ContainerPath: "/var/lib/mysql/7", + Propagation: runtimeapi.MountPropagation(42), + }, + // Everything + { + HostPath: "/mnt/8", + ContainerPath: "/var/lib/mysql/8", + Readonly: true, + SelinuxRelabel: true, + Propagation: runtimeapi.MountPropagation_PROPAGATION_BIDIRECTIONAL, + }, + } + expectedResult := []string{ + "/mnt/1:/var/lib/mysql/1", + "/mnt/2:/var/lib/mysql/2:ro", + "/mnt/3:/var/lib/mysql/3:Z", + "/mnt/4:/var/lib/mysql/4", + "/mnt/5:/var/lib/mysql/5:rslave", + "/mnt/6:/var/lib/mysql/6:rshared", + "/mnt/7:/var/lib/mysql/7", + "/mnt/8:/var/lib/mysql/8:ro,Z,rshared", + } + result := generateMountBindings(mounts) + + assert.Equal(t, result, expectedResult) +}