Change:
What happened: Windows documentation claims: V1.VolumeMount.mountPropagation - only MountPropagationHostToContainer is available. However, the internals suggest otherwise; is always returned on Windows: Either the docs should be corrected, or the Kubelet should be updated, or we should have a comment on why this does in fact enable MountPropagationHostToContainer on Windows. /cc /sig windows
/cc
/cc
I think the doc is not accurate. The doc claims the same thing. For MountPropagationHostToContainer option, we can test it whether it is working or not.
i created two PRs to address the documentation gap. we will also test this and get back to this thread.
For 1.15 we may need to update Kubelet code to allow HostToContainer.

Discussion:
mounter := mount.New() if s.Containerized { glog.V(2).Info("Running kubelet in containerized mode (experimental)") <del> mounter = &mount.NsenterMounter{} </del> <ins> mounter = mount.NewNsenterMounter() </ins> } var dockerExecHandler dockertools.ExecHandler

package mount import ( <ins> "os" </ins> "path/filepath" "strings"

//     performed in the host's mount namespace do not propagate out to the //     bind-mount in this docker version. // 2.  The host's root filesystem must be available at /rootfs <del> // 3.  The nsenter binary must be at /nsenter in the container's filesystem. </del> <ins> // 3.  The nsenter binary must be on the Kubelet process' PATH in the container's //     filesystem. </ins> // 4.  The Kubelet process must have CAP_SYS_ADMIN (required by nsenter); at //     the present, this effectively means that the kubelet is running in a //     privileged container. <ins> // 5.  The volume path used by the Kubelet must be the same inside and outside //     the container and be writable by the container (to initialize volume) //     contents. TODO: remove this requirement. // 6.  The host image must have mount, findmnt, and umount binaries in /bin, //     /usr/sbin, or /usr/bin </ins> // // For more information about mount propagation modes, see: //   https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt <del> type NsenterMounter struct{} </del> <ins> type NsenterMounter struct { // a map of commands to their paths on the host filesystem paths map[string]string } func NewNsenterMounter() *NsenterMounter { m := &NsenterMounter{ paths: map[string]string{ "mount":   "", "findmnt": "", "umount":  "", }, } // search for the mount command in other locations besides /usr/bin for binary := range m.paths { // default to root m.paths[binary] = filepath.Join("/", binary) for _, path := range []string{"/bin", "/usr/sbin", "/usr/bin"} { binPath := filepath.Join(hostRootFsPath, path, binary) if _, err := os.Stat(binPath); err != nil { continue } m.paths[binary] = binPath break } // TODO: error, so that the kubelet can stop if the mounts don't exist } return m } </ins> // NsenterMounter implements mount.Interface var _ = Interface(&NsenterMounter{})

const ( hostRootFsPath     = "/rootfs" hostProcMountsPath = "/rootfs/proc/mounts" <del> nsenterPath        = "/nsenter" </del> <ins> nsenterPath        = "nsenter" </ins> ) // Mount runs mount(8) in the host's root mount namespace.  Aside from this // aspect, Mount has the same semantics as the mounter returned by mount.New() <del> func (*NsenterMounter) Mount(source string, target string, fstype string, options []string) error { </del> <ins> func (n *NsenterMounter) Mount(source string, target string, fstype string, options []string) error { </ins> bind, bindRemountOpts := isBind(options) if bind { <del> err := doNsenterMount(source, target, fstype, []string{"bind"}) </del> <ins> err := n.doNsenterMount(source, target, fstype, []string{"bind"}) </ins> if err != nil { return err } <del> return doNsenterMount(source, target, fstype, bindRemountOpts) </del> <ins> return n.doNsenterMount(source, target, fstype, bindRemountOpts) </ins> } <del> return doNsenterMount(source, target, fstype, options) </del> <ins> return n.doNsenterMount(source, target, fstype, options) </ins> } // doNsenterMount nsenters the host's mount namespace and performs the // requested mount. <del> func doNsenterMount(source, target, fstype string, options []string) error { </del> <ins> func (n *NsenterMounter) doNsenterMount(source, target, fstype string, options []string) error { </ins> glog.V(5).Infof("nsenter Mounting %s %s %s %v", source, target, fstype, options) <del> args := makeNsenterArgs(source, target, fstype, options) </del> <ins> args := n.makeNsenterArgs(source, target, fstype, options) </ins> glog.V(5).Infof("Mount command: %v %v", nsenterPath, args) exec := exec.New()

// makeNsenterArgs makes a list of argument to nsenter in order to do the // requested mount. <del> func makeNsenterArgs(source, target, fstype string, options []string) []string { </del> <ins> func (n *NsenterMounter) makeNsenterArgs(source, target, fstype string, options []string) []string { </ins> nsenterArgs := []string{ "--mount=/rootfs/proc/1/ns/mnt", <del> "/usr/bin/mount", </del> <ins> "--", n.absHostPath("mount"), </ins> } args := makeMountArgs(source, target, fstype, options)

} // Unmount runs umount(8) in the host's mount namespace. <del> func (*NsenterMounter) Unmount(target string) error { </del> <ins> func (n *NsenterMounter) Unmount(target string) error { </ins> args := []string{ "--mount=/rootfs/proc/1/ns/mnt", <del> "/usr/bin/umount", </del> <ins> "--", n.absHostPath("umount"), </ins> target, }

// IsMountPoint determines whether a path is a mountpoint by calling findmnt // in the host's root mount namespace. <del> func (*NsenterMounter) IsMountPoint(file string) (bool, error) { </del> <ins> func (n *NsenterMounter) IsMountPoint(file string) (bool, error) { </ins> file, err := filepath.Abs(file) if err != nil { return false, err } <del> args := []string{"--mount=/rootfs/proc/1/ns/mnt", "/usr/bin/findmnt", "-o", "target", "--noheadings", "--target", file} </del> <ins> args := []string{"--mount=/rootfs/proc/1/ns/mnt", "--", n.absHostPath("findmnt"), "-o", "target", "--noheadings", "--target", file} </ins> glog.V(5).Infof("findmnt command: %v %v", nsenterPath, args) exec := exec.New()

return false, nil } <ins> func (n *NsenterMounter) absHostPath(command string) string { path, ok := n.paths[command] if !ok { return command } return path } </ins>

type NsenterMounter struct{} <ins> func NewNsenterMounter() *NsenterMounter { return &NsenterMounter{} } </ins> var _ = Interface(&NsenterMounter{}) func (*NsenterMounter) Mount(source string, target string, fstype string, options []string) error {