Apply _netdev mount option in bind mount if available

_netdev mount option is a userspace mount option and
isn't copied over when bind mount is created and remount
also does not copies it over and hence must be explicitly
used with bind mount
pull/58/head
Hemant Kumar 2018-09-13 12:27:16 -04:00
parent 426ef9d349
commit e881a29107
7 changed files with 95 additions and 20 deletions

View File

@ -68,6 +68,7 @@ go_test(
srcs = [ srcs = [
"exec_mount_test.go", "exec_mount_test.go",
"mount_linux_test.go", "mount_linux_test.go",
"mount_test.go",
"mount_windows_test.go", "mount_windows_test.go",
"nsenter_mount_test.go", "nsenter_mount_test.go",
"safe_format_and_mount_test.go", "safe_format_and_mount_test.go",

View File

@ -44,10 +44,10 @@ var _ Interface = &execMounter{}
// Mount runs mount(8) using given exec interface. // Mount runs mount(8) using given exec interface.
func (m *execMounter) Mount(source string, target string, fstype string, options []string) error { func (m *execMounter) Mount(source string, target string, fstype string, options []string) error {
bind, bindRemountOpts := isBind(options) bind, bindOpts, bindRemountOpts := isBind(options)
if bind { if bind {
err := m.doExecMount(source, target, fstype, []string{"bind"}) err := m.doExecMount(source, target, fstype, bindOpts)
if err != nil { if err != nil {
return err return err
} }

View File

@ -286,7 +286,7 @@ func IsNotMountPoint(mounter Interface, file string) (bool, error) {
// use in case of bind mount, due to the fact that bind mount doesn't respect mount options. // use in case of bind mount, due to the fact that bind mount doesn't respect mount options.
// The list equals: // The list equals:
// options - 'bind' + 'remount' (no duplicate) // options - 'bind' + 'remount' (no duplicate)
func isBind(options []string) (bool, []string) { func isBind(options []string) (bool, []string, []string) {
// Because we have an FD opened on the subpath bind mount, the "bind" option // Because we have an FD opened on the subpath bind mount, the "bind" option
// needs to be included, otherwise the mount target will error as busy if you // needs to be included, otherwise the mount target will error as busy if you
// remount as readonly. // remount as readonly.
@ -295,8 +295,14 @@ func isBind(options []string) (bool, []string) {
// volume mount to be read only. // volume mount to be read only.
bindRemountOpts := []string{"bind", "remount"} bindRemountOpts := []string{"bind", "remount"}
bind := false bind := false
bindOpts := []string{"bind"}
// _netdev is a userspace mount option and does not automatically get added when
// bind mount is created and hence we must carry it over.
if checkForNetDev(options) {
bindOpts = append(bindOpts, "_netdev")
}
if len(options) != 0 {
for _, option := range options { for _, option := range options {
switch option { switch option {
case "bind": case "bind":
@ -308,9 +314,17 @@ func isBind(options []string) (bool, []string) {
bindRemountOpts = append(bindRemountOpts, option) bindRemountOpts = append(bindRemountOpts, option)
} }
} }
}
return bind, bindRemountOpts return bind, bindOpts, bindRemountOpts
}
func checkForNetDev(options []string) bool {
for _, option := range options {
if option == "_netdev" {
return true
}
}
return false
} }
// TODO: this is a workaround for the unmount device issue caused by gci mounter. // TODO: this is a workaround for the unmount device issue caused by gci mounter.

View File

@ -89,9 +89,9 @@ func (mounter *Mounter) Mount(source string, target string, fstype string, optio
// Path to mounter binary if containerized mounter is needed. Otherwise, it is set to empty. // Path to mounter binary if containerized mounter is needed. Otherwise, it is set to empty.
// All Linux distros are expected to be shipped with a mount utility that a support bind mounts. // All Linux distros are expected to be shipped with a mount utility that a support bind mounts.
mounterPath := "" mounterPath := ""
bind, bindRemountOpts := isBind(options) bind, bindOpts, bindRemountOpts := isBind(options)
if bind { if bind {
err := mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, []string{"bind"}) err := mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, bindOpts)
if err != nil { if err != nil {
return err return err
} }

View File

@ -0,0 +1,60 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mount
import (
"reflect"
"testing"
)
func TestIsBind(t *testing.T) {
tests := []struct {
mountOption []string
isBind bool
expectedBindOpts []string
expectedRemountOpts []string
}{
{
[]string{"vers=2", "ro", "_netdev"},
false,
[]string{},
[]string{},
},
{
[]string{"bind", "vers=2", "ro", "_netdev"},
true,
[]string{"bind", "_netdev"},
[]string{"bind", "remount", "vers=2", "ro", "_netdev"},
},
}
for _, test := range tests {
bind, bindOpts, bindRemountOpts := isBind(test.mountOption)
if bind != test.isBind {
t.Errorf("Expected bind to be %v but got %v", test.isBind, bind)
}
if test.isBind {
if !reflect.DeepEqual(test.expectedBindOpts, bindOpts) {
t.Errorf("Expected bind mount options to be %+v got %+v", test.expectedBindOpts, bindOpts)
}
if !reflect.DeepEqual(test.expectedRemountOpts, bindRemountOpts) {
t.Errorf("Expected remount options to be %+v got %+v", test.expectedRemountOpts, bindRemountOpts)
}
}
}
}

View File

@ -68,7 +68,7 @@ func (mounter *Mounter) Mount(source string, target string, fstype string, optio
bindSource := "" bindSource := ""
// tell it's going to mount azure disk or azure file according to options // tell it's going to mount azure disk or azure file according to options
if bind, _ := isBind(options); bind { if bind, _, _ := isBind(options); bind {
// mount azure disk // mount azure disk
bindSource = normalizeWindowsPath(source) bindSource = normalizeWindowsPath(source)
} else { } else {

View File

@ -61,10 +61,10 @@ var _ = Interface(&NsenterMounter{})
// Mount runs mount(8) in the host's root mount namespace. Aside from this // 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() // aspect, Mount has the same semantics as the mounter returned by mount.New()
func (n *NsenterMounter) Mount(source string, target string, fstype string, options []string) error { func (n *NsenterMounter) Mount(source string, target string, fstype string, options []string) error {
bind, bindRemountOpts := isBind(options) bind, bindOpts, bindRemountOpts := isBind(options)
if bind { if bind {
err := n.doNsenterMount(source, target, fstype, []string{"bind"}) err := n.doNsenterMount(source, target, fstype, bindOpts)
if err != nil { if err != nil {
return err return err
} }