Commit 6897095e authored by Deyuan Deng's avatar Deyuan Deng Committed by Deyuan Deng

Change mount.Interface.Mount to exec('mount'), instead of syscall

parent 8fa21ebd
...@@ -38,13 +38,13 @@ func (f *FakeMounter) ResetLog() { ...@@ -38,13 +38,13 @@ func (f *FakeMounter) ResetLog() {
f.Log = []FakeAction{} f.Log = []FakeAction{}
} }
func (f *FakeMounter) Mount(source string, target string, fstype string, flags uintptr, data string) error { func (f *FakeMounter) Mount(source string, target string, fstype string, options []string) error {
f.MountPoints = append(f.MountPoints, MountPoint{Device: source, Path: target, Type: fstype}) f.MountPoints = append(f.MountPoints, MountPoint{Device: source, Path: target, Type: fstype})
f.Log = append(f.Log, FakeAction{Action: FakeActionMount, Target: target, Source: source, FSType: fstype}) f.Log = append(f.Log, FakeAction{Action: FakeActionMount, Target: target, Source: source, FSType: fstype})
return nil return nil
} }
func (f *FakeMounter) Unmount(target string, flags int) error { func (f *FakeMounter) Unmount(target string) error {
newMountpoints := []MountPoint{} newMountpoints := []MountPoint{}
for _, mp := range f.MountPoints { for _, mp := range f.MountPoints {
if mp.Path != target { if mp.Path != target {
......
...@@ -18,14 +18,11 @@ limitations under the License. ...@@ -18,14 +18,11 @@ limitations under the License.
// an alternate platform, we will need to abstract further. // an alternate platform, we will need to abstract further.
package mount package mount
// Each supported platform must define the following flags:
// - FlagBind: specifies a bind mount
// - FlagReadOnly: the mount will be read-only
type Interface interface { type Interface interface {
// Mount wraps syscall.Mount(). // Mount mounts source to target as fstype with given options.
Mount(source string, target string, fstype string, flags uintptr, data string) error Mount(source string, target string, fstype string, options []string) error
// Umount wraps syscall.Mount(). // Unmount unmounts given target.
Unmount(target string, flags int) error Unmount(target string) error
// List returns a list of all mounted filesystems. This can be large. // List returns a list of all mounted filesystems. This can be large.
// On some platforms, reading mounts is not guaranteed consistent (i.e. // On some platforms, reading mounts is not guaranteed consistent (i.e.
// it could change between chunked reads). This is guaranteed to be // it could change between chunked reads). This is guaranteed to be
......
...@@ -24,6 +24,7 @@ import ( ...@@ -24,6 +24,7 @@ import (
"hash/adler32" "hash/adler32"
"io" "io"
"os" "os"
"os/exec"
"strconv" "strconv"
"strings" "strings"
"syscall" "syscall"
...@@ -31,28 +32,91 @@ import ( ...@@ -31,28 +32,91 @@ import (
"github.com/golang/glog" "github.com/golang/glog"
) )
const FlagBind = syscall.MS_BIND const (
const FlagReadOnly = syscall.MS_RDONLY // How many times to retry for a consistent read of /proc/mounts.
maxListTries = 3
// Number of fields per line in "/proc/mounts", as per the fstab man page.
expectedNumFieldsPerLine = 6
)
// Mounter implements mount.Interface for linux platform. // Mounter implements mount.Interface for linux platform.
type Mounter struct{} type Mounter struct{}
// Mount wraps syscall.Mount() // Mount mounts source to target as fstype with given options. 'source' and 'fstype' must
func (mounter *Mounter) Mount(source string, target string, fstype string, flags uintptr, data string) error { // be an emtpy string in case it's not required, e.g. for remount, or for auto filesystem
glog.V(5).Infof("Mounting %s %s %s %d %s", source, target, fstype, flags, data) // type, where kernel handles fs type for you. The mount 'options' is a list of options,
return syscall.Mount(source, target, fstype, flags, data) // currently come from mount(8), e.g. "ro", "remount", "bind", etc. If no more option is
// required, call Mount with an empty string list or nil.
func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
// The remount options to use in case of bind mount, due to the fact that bind mount doesn't
// respect mount options. The list equals:
// options - 'bind' + 'remount' (no duplicate)
bindRemountOpts := []string{"remount"}
bind := false
if len(options) != 0 {
for _, option := range options {
switch option {
case "bind":
bind = true
break
case "remount":
break
default:
bindRemountOpts = append(bindRemountOpts, option)
}
}
}
if bind {
err := doMount(source, target, fstype, []string{"bind"})
if err != nil {
return err
}
return doMount(source, target, fstype, bindRemountOpts)
} else {
return doMount(source, target, fstype, options)
}
} }
// Unmount wraps syscall.Unmount() func doMount(source string, target string, fstype string, options []string) error {
func (mounter *Mounter) Unmount(target string, flags int) error { glog.V(5).Infof("Mounting %s %s %s %v", source, target, fstype, options)
return syscall.Unmount(target, flags) // Build mount command as follows:
// mount [-t $fstype] [-o $options] [$source] $target
mountArgs := []string{}
if len(fstype) > 0 {
mountArgs = append(mountArgs, "-t", fstype)
}
if len(options) > 0 {
mountArgs = append(mountArgs, "-o", strings.Join(options, ","))
}
if len(source) > 0 {
mountArgs = append(mountArgs, source)
}
mountArgs = append(mountArgs, target)
command := exec.Command("mount", mountArgs...)
output, err := command.CombinedOutput()
if err != nil {
glog.Errorf("Mount failed: %v\nMounting arguments: %s %s %s %v\nOutput: %s\n",
err, source, target, fstype, options, string(output))
}
return err
} }
// How many times to retry for a consistent read of /proc/mounts. // Unmount unmounts target with given options.
const maxListTries = 3 func (mounter *Mounter) Unmount(target string) error {
glog.V(5).Infof("Unmounting %s %v")
command := exec.Command("umount", target)
output, err := command.CombinedOutput()
if err != nil {
glog.Errorf("Unmount failed: %v\nUnmounting arguments: %s\nOutput: %s\n", err, target, string(output))
return err
}
return nil
}
// List returns a list of all mounted filesystems. // List returns a list of all mounted filesystems.
func (*Mounter) List() ([]MountPoint, error) { func (mounter *Mounter) List() ([]MountPoint, error) {
hash1, err := readProcMounts(nil) hash1, err := readProcMounts(nil)
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -89,9 +153,6 @@ func (mounter *Mounter) IsMountPoint(file string) (bool, error) { ...@@ -89,9 +153,6 @@ func (mounter *Mounter) IsMountPoint(file string) (bool, error) {
return stat.Sys().(*syscall.Stat_t).Dev != rootStat.Sys().(*syscall.Stat_t).Dev, nil return stat.Sys().(*syscall.Stat_t).Dev != rootStat.Sys().(*syscall.Stat_t).Dev, nil
} }
// As per the fstab man page.
const expectedNumFieldsPerLine = 6
// readProcMounts reads /proc/mounts and produces a hash of the contents. If the out // readProcMounts reads /proc/mounts and produces a hash of the contents. If the out
// argument is not nil, this fills it with MountPoint structs. // argument is not nil, this fills it with MountPoint structs.
func readProcMounts(out *[]MountPoint) (uint32, error) { func readProcMounts(out *[]MountPoint) (uint32, error) {
......
...@@ -18,16 +18,13 @@ limitations under the License. ...@@ -18,16 +18,13 @@ limitations under the License.
package mount package mount
const FlagBind = 0
const FlagReadOnly = 0
type Mounter struct{} type Mounter struct{}
func (mounter *Mounter) Mount(source string, target string, fstype string, flags uintptr, data string) error { func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
return nil return nil
} }
func (mounter *Mounter) Unmount(target string, flags int) error { func (mounter *Mounter) Unmount(target string) error {
return nil return nil
} }
......
...@@ -192,11 +192,6 @@ func (pd *awsElasticBlockStore) SetUpAt(dir string) error { ...@@ -192,11 +192,6 @@ func (pd *awsElasticBlockStore) SetUpAt(dir string) error {
return err return err
} }
flags := uintptr(0)
if pd.readOnly {
flags = mount.FlagReadOnly
}
if err := os.MkdirAll(dir, 0750); err != nil { if err := os.MkdirAll(dir, 0750); err != nil {
// TODO: we should really eject the attach/detach out into its own control loop. // TODO: we should really eject the attach/detach out into its own control loop.
detachDiskLogError(pd) detachDiskLogError(pd)
...@@ -204,7 +199,11 @@ func (pd *awsElasticBlockStore) SetUpAt(dir string) error { ...@@ -204,7 +199,11 @@ func (pd *awsElasticBlockStore) SetUpAt(dir string) error {
} }
// Perform a bind mount to the full path to allow duplicate mounts of the same PD. // Perform a bind mount to the full path to allow duplicate mounts of the same PD.
err = pd.mounter.Mount(globalPDPath, dir, "", mount.FlagBind|flags, "") options := []string{"bind"}
if pd.readOnly {
options = append(options, "ro")
}
err = pd.mounter.Mount(globalPDPath, dir, "", options)
if err != nil { if err != nil {
mountpoint, mntErr := pd.mounter.IsMountPoint(dir) mountpoint, mntErr := pd.mounter.IsMountPoint(dir)
if mntErr != nil { if mntErr != nil {
...@@ -212,7 +211,7 @@ func (pd *awsElasticBlockStore) SetUpAt(dir string) error { ...@@ -212,7 +211,7 @@ func (pd *awsElasticBlockStore) SetUpAt(dir string) error {
return err return err
} }
if mountpoint { if mountpoint {
if mntErr = pd.mounter.Unmount(dir, 0); mntErr != nil { if mntErr = pd.mounter.Unmount(dir); mntErr != nil {
glog.Errorf("Failed to unmount: %v", mntErr) glog.Errorf("Failed to unmount: %v", mntErr)
return err return err
} }
...@@ -291,7 +290,7 @@ func (pd *awsElasticBlockStore) TearDownAt(dir string) error { ...@@ -291,7 +290,7 @@ func (pd *awsElasticBlockStore) TearDownAt(dir string) error {
return err return err
} }
// Unmount the bind-mount inside this pod // Unmount the bind-mount inside this pod
if err := pd.mounter.Unmount(dir, 0); err != nil { if err := pd.mounter.Unmount(dir); err != nil {
glog.V(2).Info("Error unmounting dir ", dir, ": ", err) glog.V(2).Info("Error unmounting dir ", dir, ": ", err)
return err return err
} }
......
...@@ -36,10 +36,6 @@ func (util *AWSDiskUtil) AttachAndMountDisk(pd *awsElasticBlockStore, globalPDPa ...@@ -36,10 +36,6 @@ func (util *AWSDiskUtil) AttachAndMountDisk(pd *awsElasticBlockStore, globalPDPa
if err != nil { if err != nil {
return err return err
} }
flags := uintptr(0)
if pd.readOnly {
flags = mount.FlagReadOnly
}
devicePath, err := volumes.AttachDisk("", pd.volumeID, pd.readOnly) devicePath, err := volumes.AttachDisk("", pd.volumeID, pd.readOnly)
if err != nil { if err != nil {
return err return err
...@@ -76,8 +72,12 @@ func (util *AWSDiskUtil) AttachAndMountDisk(pd *awsElasticBlockStore, globalPDPa ...@@ -76,8 +72,12 @@ func (util *AWSDiskUtil) AttachAndMountDisk(pd *awsElasticBlockStore, globalPDPa
return err return err
} }
} }
options := []string{}
if pd.readOnly {
options = append(options, "ro")
}
if !mountpoint { if !mountpoint {
err = pd.diskMounter.Mount(devicePath, globalPDPath, pd.fsType, flags, "") err = pd.diskMounter.Mount(devicePath, globalPDPath, pd.fsType, options)
if err != nil { if err != nil {
os.Remove(globalPDPath) os.Remove(globalPDPath)
return err return err
...@@ -90,7 +90,7 @@ func (util *AWSDiskUtil) AttachAndMountDisk(pd *awsElasticBlockStore, globalPDPa ...@@ -90,7 +90,7 @@ func (util *AWSDiskUtil) AttachAndMountDisk(pd *awsElasticBlockStore, globalPDPa
func (util *AWSDiskUtil) DetachDisk(pd *awsElasticBlockStore) error { func (util *AWSDiskUtil) DetachDisk(pd *awsElasticBlockStore) error {
// Unmount the global PD mount, which should be the only one. // Unmount the global PD mount, which should be the only one.
globalPDPath := makeGlobalPDPath(pd.plugin.host, pd.volumeID) globalPDPath := makeGlobalPDPath(pd.plugin.host, pd.volumeID)
if err := pd.mounter.Unmount(globalPDPath, 0); err != nil { if err := pd.mounter.Unmount(globalPDPath); err != nil {
glog.V(2).Info("Error unmount dir ", globalPDPath, ": ", err) glog.V(2).Info("Error unmount dir ", globalPDPath, ": ", err)
return err return err
} }
...@@ -121,18 +121,21 @@ type awsSafeFormatAndMount struct { ...@@ -121,18 +121,21 @@ type awsSafeFormatAndMount struct {
} }
// uses /usr/share/google/safe_format_and_mount to optionally mount, and format a disk // uses /usr/share/google/safe_format_and_mount to optionally mount, and format a disk
func (mounter *awsSafeFormatAndMount) Mount(source string, target string, fstype string, flags uintptr, data string) error { func (mounter *awsSafeFormatAndMount) Mount(source string, target string, fstype string, options []string) error {
// Don't attempt to format if mounting as readonly. Go straight to mounting.
// Don't attempt to format if mounting as readonly. Go straight to mounting. // Don't attempt to format if mounting as readonly. Go straight to mounting.
if (flags & mount.FlagReadOnly) != 0 { for _, option := range options {
return mounter.Interface.Mount(source, target, fstype, flags, data) if option == "ro" {
return mounter.Interface.Mount(source, target, fstype, options)
}
} }
args := []string{} args := []string{}
// ext4 is the default for safe_format_and_mount // ext4 is the default for safe_format_and_mount
if len(fstype) > 0 && fstype != "ext4" { if len(fstype) > 0 && fstype != "ext4" {
args = append(args, "-m", fmt.Sprintf("mkfs.%s", fstype)) args = append(args, "-m", fmt.Sprintf("mkfs.%s", fstype))
} }
args = append(args, options...)
args = append(args, source, target) args = append(args, source, target)
// TODO: Accept other options here?
glog.V(5).Infof("exec-ing: /usr/share/google/safe_format_and_mount %v", args) glog.V(5).Infof("exec-ing: /usr/share/google/safe_format_and_mount %v", args)
cmd := mounter.runner.Command("/usr/share/google/safe_format_and_mount", args...) cmd := mounter.runner.Command("/usr/share/google/safe_format_and_mount", args...)
dataOut, err := cmd.CombinedOutput() dataOut, err := cmd.CombinedOutput()
......
...@@ -64,7 +64,7 @@ func TestSafeFormatAndMount(t *testing.T) { ...@@ -64,7 +64,7 @@ func TestSafeFormatAndMount(t *testing.T) {
runner: &fake, runner: &fake,
} }
err := mounter.Mount("/dev/foo", "/mnt/bar", test.fstype, 0, "") err := mounter.Mount("/dev/foo", "/mnt/bar", test.fstype, nil)
if test.err == nil && err != nil { if test.err == nil && err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
} }
......
...@@ -197,6 +197,7 @@ func (ed *emptyDir) setupTmpfs(dir string) error { ...@@ -197,6 +197,7 @@ func (ed *emptyDir) setupTmpfs(dir string) error {
if isMnt && medium == mediumMemory { if isMnt && medium == mediumMemory {
return nil return nil
} }
// By default a tmpfs mount will receive a different SELinux context // By default a tmpfs mount will receive a different SELinux context
// from that of the Kubelet root directory which is not readable from // from that of the Kubelet root directory which is not readable from
// the SELinux context of a docker container. // the SELinux context of a docker container.
...@@ -206,15 +207,15 @@ func (ed *emptyDir) setupTmpfs(dir string) error { ...@@ -206,15 +207,15 @@ func (ed *emptyDir) setupTmpfs(dir string) error {
// the container. // the container.
opts := ed.getTmpfsMountOptions() opts := ed.getTmpfsMountOptions()
glog.V(3).Infof("pod %v: mounting tmpfs for volume %v with opts %v", ed.podUID, ed.volName, opts) glog.V(3).Infof("pod %v: mounting tmpfs for volume %v with opts %v", ed.podUID, ed.volName, opts)
return ed.mounter.Mount("tmpfs", dir, "tmpfs", 0, opts) return ed.mounter.Mount("tmpfs", dir, "tmpfs", opts)
} }
func (ed *emptyDir) getTmpfsMountOptions() string { func (ed *emptyDir) getTmpfsMountOptions() []string {
if ed.rootContext == "" { if ed.rootContext == "" {
return "" return []string{""}
} }
return fmt.Sprintf("rootcontext=\"%v\"", ed.rootContext) return []string{fmt.Sprintf("rootcontext=\"%v\"", ed.rootContext)}
} }
func (ed *emptyDir) GetPath() string { func (ed *emptyDir) GetPath() string {
...@@ -261,7 +262,7 @@ func (ed *emptyDir) teardownTmpfs(dir string) error { ...@@ -261,7 +262,7 @@ func (ed *emptyDir) teardownTmpfs(dir string) error {
if ed.mounter == nil { if ed.mounter == nil {
return fmt.Errorf("memory storage requested, but mounter is nil") return fmt.Errorf("memory storage requested, but mounter is nil")
} }
if err := ed.mounter.Unmount(dir, 0); err != nil { if err := ed.mounter.Unmount(dir); err != nil {
return err return err
} }
if err := os.RemoveAll(dir); err != nil { if err := os.RemoveAll(dir); err != nil {
......
...@@ -201,11 +201,6 @@ func (pd *gcePersistentDisk) SetUpAt(dir string) error { ...@@ -201,11 +201,6 @@ func (pd *gcePersistentDisk) SetUpAt(dir string) error {
return err return err
} }
flags := uintptr(0)
if pd.readOnly {
flags = mount.FlagReadOnly
}
if err := os.MkdirAll(dir, 0750); err != nil { if err := os.MkdirAll(dir, 0750); err != nil {
// TODO: we should really eject the attach/detach out into its own control loop. // TODO: we should really eject the attach/detach out into its own control loop.
detachDiskLogError(pd) detachDiskLogError(pd)
...@@ -213,7 +208,11 @@ func (pd *gcePersistentDisk) SetUpAt(dir string) error { ...@@ -213,7 +208,11 @@ func (pd *gcePersistentDisk) SetUpAt(dir string) error {
} }
// Perform a bind mount to the full path to allow duplicate mounts of the same PD. // Perform a bind mount to the full path to allow duplicate mounts of the same PD.
err = pd.mounter.Mount(globalPDPath, dir, "", mount.FlagBind|flags, "") options := []string{"bind"}
if pd.readOnly {
options = append(options, "ro")
}
err = pd.mounter.Mount(globalPDPath, dir, "", options)
if err != nil { if err != nil {
mountpoint, mntErr := pd.mounter.IsMountPoint(dir) mountpoint, mntErr := pd.mounter.IsMountPoint(dir)
if mntErr != nil { if mntErr != nil {
...@@ -221,7 +220,7 @@ func (pd *gcePersistentDisk) SetUpAt(dir string) error { ...@@ -221,7 +220,7 @@ func (pd *gcePersistentDisk) SetUpAt(dir string) error {
return err return err
} }
if mountpoint { if mountpoint {
if mntErr = pd.mounter.Unmount(dir, 0); mntErr != nil { if mntErr = pd.mounter.Unmount(dir); mntErr != nil {
glog.Errorf("Failed to unmount: %v", mntErr) glog.Errorf("Failed to unmount: %v", mntErr)
return err return err
} }
...@@ -279,7 +278,7 @@ func (pd *gcePersistentDisk) TearDownAt(dir string) error { ...@@ -279,7 +278,7 @@ func (pd *gcePersistentDisk) TearDownAt(dir string) error {
return err return err
} }
// Unmount the bind-mount inside this pod // Unmount the bind-mount inside this pod
if err := pd.mounter.Unmount(dir, 0); err != nil { if err := pd.mounter.Unmount(dir); err != nil {
return err return err
} }
// If len(refs) is 1, then all bind mounts have been removed, and the // If len(refs) is 1, then all bind mounts have been removed, and the
......
...@@ -80,7 +80,7 @@ func (fake *fakePDManager) AttachAndMountDisk(pd *gcePersistentDisk, globalPDPat ...@@ -80,7 +80,7 @@ func (fake *fakePDManager) AttachAndMountDisk(pd *gcePersistentDisk, globalPDPat
fake.attachCalled = true fake.attachCalled = true
// Simulate the global mount so that the fakeMounter returns the // Simulate the global mount so that the fakeMounter returns the
// expected number of mounts for the attached disk. // expected number of mounts for the attached disk.
pd.mounter.Mount(globalPath, globalPath, pd.fsType, 0, "") pd.mounter.Mount(globalPath, globalPath, pd.fsType, nil)
return nil return nil
} }
......
...@@ -39,10 +39,6 @@ func (util *GCEDiskUtil) AttachAndMountDisk(pd *gcePersistentDisk, globalPDPath ...@@ -39,10 +39,6 @@ func (util *GCEDiskUtil) AttachAndMountDisk(pd *gcePersistentDisk, globalPDPath
if err != nil { if err != nil {
return err return err
} }
flags := uintptr(0)
if pd.readOnly {
flags = mount.FlagReadOnly
}
if err := gce.(*gce_cloud.GCECloud).AttachDisk(pd.pdName, pd.readOnly); err != nil { if err := gce.(*gce_cloud.GCECloud).AttachDisk(pd.pdName, pd.readOnly); err != nil {
return err return err
} }
...@@ -79,8 +75,12 @@ func (util *GCEDiskUtil) AttachAndMountDisk(pd *gcePersistentDisk, globalPDPath ...@@ -79,8 +75,12 @@ func (util *GCEDiskUtil) AttachAndMountDisk(pd *gcePersistentDisk, globalPDPath
return err return err
} }
} }
options := []string{}
if pd.readOnly {
options = append(options, "ro")
}
if !mountpoint { if !mountpoint {
err = pd.diskMounter.Mount(devicePath, globalPDPath, pd.fsType, flags, "") err = pd.diskMounter.Mount(devicePath, globalPDPath, pd.fsType, options)
if err != nil { if err != nil {
os.Remove(globalPDPath) os.Remove(globalPDPath)
return err return err
...@@ -93,7 +93,7 @@ func (util *GCEDiskUtil) AttachAndMountDisk(pd *gcePersistentDisk, globalPDPath ...@@ -93,7 +93,7 @@ func (util *GCEDiskUtil) AttachAndMountDisk(pd *gcePersistentDisk, globalPDPath
func (util *GCEDiskUtil) DetachDisk(pd *gcePersistentDisk) error { func (util *GCEDiskUtil) DetachDisk(pd *gcePersistentDisk) error {
// Unmount the global PD mount, which should be the only one. // Unmount the global PD mount, which should be the only one.
globalPDPath := makeGlobalPDName(pd.plugin.host, pd.pdName) globalPDPath := makeGlobalPDName(pd.plugin.host, pd.pdName)
if err := pd.mounter.Unmount(globalPDPath, 0); err != nil { if err := pd.mounter.Unmount(globalPDPath); err != nil {
return err return err
} }
if err := os.Remove(globalPDPath); err != nil { if err := os.Remove(globalPDPath); err != nil {
...@@ -120,18 +120,20 @@ type gceSafeFormatAndMount struct { ...@@ -120,18 +120,20 @@ type gceSafeFormatAndMount struct {
} }
// uses /usr/share/google/safe_format_and_mount to optionally mount, and format a disk // uses /usr/share/google/safe_format_and_mount to optionally mount, and format a disk
func (mounter *gceSafeFormatAndMount) Mount(source string, target string, fstype string, flags uintptr, data string) error { func (mounter *gceSafeFormatAndMount) Mount(source string, target string, fstype string, options []string) error {
// Don't attempt to format if mounting as readonly. Go straight to mounting. // Don't attempt to format if mounting as readonly. Go straight to mounting.
if (flags & mount.FlagReadOnly) != 0 { for _, option := range options {
return mounter.Interface.Mount(source, target, fstype, flags, data) if option == "ro" {
return mounter.Interface.Mount(source, target, fstype, options)
}
} }
args := []string{} args := []string{}
// ext4 is the default for safe_format_and_mount // ext4 is the default for safe_format_and_mount
if len(fstype) > 0 && fstype != "ext4" { if len(fstype) > 0 && fstype != "ext4" {
args = append(args, "-m", fmt.Sprintf("mkfs.%s", fstype)) args = append(args, "-m", fmt.Sprintf("mkfs.%s", fstype))
} }
args = append(args, options...)
args = append(args, source, target) args = append(args, source, target)
// TODO: Accept other options here?
glog.V(5).Infof("exec-ing: /usr/share/google/safe_format_and_mount %v", args) glog.V(5).Infof("exec-ing: /usr/share/google/safe_format_and_mount %v", args)
cmd := mounter.runner.Command("/usr/share/google/safe_format_and_mount", args...) cmd := mounter.runner.Command("/usr/share/google/safe_format_and_mount", args...)
dataOut, err := cmd.CombinedOutput() dataOut, err := cmd.CombinedOutput()
......
...@@ -64,7 +64,7 @@ func TestSafeFormatAndMount(t *testing.T) { ...@@ -64,7 +64,7 @@ func TestSafeFormatAndMount(t *testing.T) {
runner: &fake, runner: &fake,
} }
err := mounter.Mount("/dev/foo", "/mnt/bar", test.fstype, 0, "") err := mounter.Mount("/dev/foo", "/mnt/bar", test.fstype, nil)
if test.err == nil && err != nil { if test.err == nil && err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
} }
......
...@@ -19,7 +19,6 @@ package glusterfs ...@@ -19,7 +19,6 @@ package glusterfs
import ( import (
"math/rand" "math/rand"
"os" "os"
"strings"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/types" "github.com/GoogleCloudPlatform/kubernetes/pkg/types"
...@@ -129,16 +128,15 @@ func (glusterfsVolume *glusterfs) SetUpAt(dir string) error { ...@@ -129,16 +128,15 @@ func (glusterfsVolume *glusterfs) SetUpAt(dir string) error {
if mountpoint { if mountpoint {
return nil return nil
} }
path := glusterfsVolume.path
os.MkdirAll(dir, 0750) os.MkdirAll(dir, 0750)
err = glusterfsVolume.execMount(glusterfsVolume.hosts, path, dir, glusterfsVolume.readonly) err = glusterfsVolume.setUpAtInternal(dir)
if err == nil { if err == nil {
return nil return nil
} }
// cleanup upon failure // Cleanup upon failure.
glusterfsVolume.cleanup(dir) glusterfsVolume.cleanup(dir)
// return error
return err return err
} }
...@@ -165,7 +163,7 @@ func (glusterfsVolume *glusterfs) cleanup(dir string) error { ...@@ -165,7 +163,7 @@ func (glusterfsVolume *glusterfs) cleanup(dir string) error {
return os.RemoveAll(dir) return os.RemoveAll(dir)
} }
if err := glusterfsVolume.mounter.Unmount(dir, 0); err != nil { if err := glusterfsVolume.mounter.Unmount(dir); err != nil {
glog.Errorf("Glusterfs: Unmounting failed: %v", err) glog.Errorf("Glusterfs: Unmounting failed: %v", err)
return err return err
} }
...@@ -183,30 +181,21 @@ func (glusterfsVolume *glusterfs) cleanup(dir string) error { ...@@ -183,30 +181,21 @@ func (glusterfsVolume *glusterfs) cleanup(dir string) error {
return nil return nil
} }
func (glusterfsVolume *glusterfs) execMount(hosts *api.Endpoints, path string, mountpoint string, readonly bool) error { func (glusterfsVolume *glusterfs) setUpAtInternal(dir string) error {
var errs error var errs error
var command exec.Cmd
var mountArgs []string options := []string{}
var opt []string if glusterfsVolume.readonly {
options = append(options, "ro")
// build option array
if readonly == true {
opt = []string{"-o", "ro"}
} else {
opt = []string{"-o", "rw"}
} }
l := len(hosts.Subsets) l := len(glusterfsVolume.hosts.Subsets)
// avoid mount storm, pick a host randomly // Avoid mount storm, pick a host randomly.
start := rand.Int() % l start := rand.Int() % l
// iterate all hosts until mount succeeds. // Iterate all hosts until mount succeeds.
for i := start; i < start+l; i++ { for i := start; i < start+l; i++ {
arg := []string{"-t", "glusterfs", hosts.Subsets[i%l].Addresses[0].IP + ":" + path, mountpoint} hostIP := glusterfsVolume.hosts.Subsets[i%l].Addresses[0].IP
mountArgs = append(arg, opt...) errs = glusterfsVolume.mounter.Mount(hostIP+":"+glusterfsVolume.path, dir, "glusterfs", options)
glog.V(1).Infof("Glusterfs: mount cmd: mount %v", strings.Join(mountArgs, " "))
command = glusterfsVolume.exe.Command("mount", mountArgs...)
_, errs = command.CombinedOutput()
if errs == nil { if errs == nil {
return nil return nil
} }
......
...@@ -55,11 +55,11 @@ func diskSetUp(manager diskManager, disk iscsiDisk, volPath string, mounter moun ...@@ -55,11 +55,11 @@ func diskSetUp(manager diskManager, disk iscsiDisk, volPath string, mounter moun
return err return err
} }
// Perform a bind mount to the full path to allow duplicate mounts of the same disk. // Perform a bind mount to the full path to allow duplicate mounts of the same disk.
flags := uintptr(0) options := []string{"bind"}
if disk.readOnly { if disk.readOnly {
flags = mount.FlagReadOnly options = append(options, "ro")
} }
err = mounter.Mount(globalPDPath, volPath, "", mount.FlagBind|flags, "") err = mounter.Mount(globalPDPath, volPath, "", options)
if err != nil { if err != nil {
glog.Errorf("failed to bind mount:%s", globalPDPath) glog.Errorf("failed to bind mount:%s", globalPDPath)
return err return err
...@@ -83,8 +83,8 @@ func diskTearDown(manager diskManager, disk iscsiDisk, volPath string, mounter m ...@@ -83,8 +83,8 @@ func diskTearDown(manager diskManager, disk iscsiDisk, volPath string, mounter m
glog.Errorf("failed to get reference count %s", volPath) glog.Errorf("failed to get reference count %s", volPath)
return err return err
} }
if err := mounter.Unmount(volPath, 0); err != nil { if err := mounter.Unmount(volPath); err != nil {
glog.Errorf("failed to umount %s", volPath) glog.Errorf("failed to unmount %s", volPath)
return err return err
} }
// If len(refs) is 1, then all bind mounts have been removed, and the // If len(refs) is 1, then all bind mounts have been removed, and the
......
...@@ -110,6 +110,11 @@ func (plugin *ISCSIPlugin) newCleanerInternal(volName string, podUID types.UID, ...@@ -110,6 +110,11 @@ func (plugin *ISCSIPlugin) newCleanerInternal(volName string, podUID types.UID,
}, nil }, nil
} }
func (plugin *ISCSIPlugin) execCommand(command string, args []string) ([]byte, error) {
cmd := plugin.exe.Command(command, args...)
return cmd.CombinedOutput()
}
type iscsiDisk struct { type iscsiDisk struct {
volName string volName string
podUID types.UID podUID types.UID
...@@ -142,15 +147,13 @@ func (iscsi *iscsiDisk) SetUpAt(dir string) error { ...@@ -142,15 +147,13 @@ func (iscsi *iscsiDisk) SetUpAt(dir string) error {
return err return err
} }
globalPDPath := iscsi.manager.MakeGlobalPDName(*iscsi) globalPDPath := iscsi.manager.MakeGlobalPDName(*iscsi)
// make mountpoint rw/ro work as expected var options []string
//FIXME revisit pkg/util/mount and ensure rw/ro is implemented as expected
mode := "rw"
if iscsi.readOnly { if iscsi.readOnly {
mode = "ro" options = []string{"remount", "ro"}
} else {
options = []string{"remount", "rw"}
} }
iscsi.plugin.execCommand("mount", []string{"-o", "remount," + mode, globalPDPath, dir}) return iscsi.mounter.Mount(globalPDPath, dir, "", options)
return nil
} }
// Unmounts the bind mount, and detaches the disk only if the disk // Unmounts the bind mount, and detaches the disk only if the disk
...@@ -162,8 +165,3 @@ func (iscsi *iscsiDisk) TearDown() error { ...@@ -162,8 +165,3 @@ func (iscsi *iscsiDisk) TearDown() error {
func (iscsi *iscsiDisk) TearDownAt(dir string) error { func (iscsi *iscsiDisk) TearDownAt(dir string) error {
return diskTearDown(iscsi.manager, *iscsi, dir, iscsi.mounter) return diskTearDown(iscsi.manager, *iscsi, dir, iscsi.mounter)
} }
func (plugin *ISCSIPlugin) execCommand(command string, args []string) ([]byte, error) {
cmd := plugin.exe.Command(command, args...)
return cmd.CombinedOutput()
}
...@@ -55,7 +55,7 @@ func (fake *fakeDiskManager) AttachDisk(disk iscsiDisk) error { ...@@ -55,7 +55,7 @@ func (fake *fakeDiskManager) AttachDisk(disk iscsiDisk) error {
} }
// Simulate the global mount so that the fakeMounter returns the // Simulate the global mount so that the fakeMounter returns the
// expected number of mounts for the attached disk. // expected number of mounts for the attached disk.
disk.mounter.Mount(globalPath, globalPath, disk.fsType, 0, "") disk.mounter.Mount(globalPath, globalPath, disk.fsType, nil)
fake.attachCalled = true fake.attachCalled = true
return nil return nil
......
...@@ -112,7 +112,7 @@ func (util *ISCSIUtil) AttachDisk(iscsi iscsiDisk) error { ...@@ -112,7 +112,7 @@ func (util *ISCSIUtil) AttachDisk(iscsi iscsiDisk) error {
return err return err
} }
err = iscsi.mounter.Mount(devicePath, globalPDPath, iscsi.fsType, uintptr(0), "") err = iscsi.mounter.Mount(devicePath, globalPDPath, iscsi.fsType, nil)
if err != nil { if err != nil {
glog.Errorf("iscsi: failed to mount iscsi volume %s [%s] to %s, error %v", devicePath, iscsi.fsType, globalPDPath, err) glog.Errorf("iscsi: failed to mount iscsi volume %s [%s] to %s, error %v", devicePath, iscsi.fsType, globalPDPath, err)
} }
...@@ -126,8 +126,8 @@ func (util *ISCSIUtil) DetachDisk(iscsi iscsiDisk, mntPath string) error { ...@@ -126,8 +126,8 @@ func (util *ISCSIUtil) DetachDisk(iscsi iscsiDisk, mntPath string) error {
glog.Errorf("iscsi detach disk: failed to get device from mnt: %s\nError: %v", mntPath, err) glog.Errorf("iscsi detach disk: failed to get device from mnt: %s\nError: %v", mntPath, err)
return err return err
} }
if err = iscsi.mounter.Unmount(mntPath, 0); err != nil { if err = iscsi.mounter.Unmount(mntPath); err != nil {
glog.Errorf("iscsi detach disk: failed to umount: %s\nError: %v", mntPath, err) glog.Errorf("iscsi detach disk: failed to unmount: %s\nError: %v", mntPath, err)
return err return err
} }
cnt-- cnt--
......
...@@ -17,23 +17,25 @@ limitations under the License. ...@@ -17,23 +17,25 @@ limitations under the License.
package nfs package nfs
import ( import (
"fmt"
"os" "os"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/types" "github.com/GoogleCloudPlatform/kubernetes/pkg/types"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/mount"
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume" "github.com/GoogleCloudPlatform/kubernetes/pkg/volume"
"github.com/golang/glog" "github.com/golang/glog"
) )
// This is the primary entrypoint for volume plugins. // This is the primary entrypoint for volume plugins.
func ProbeVolumePlugins() []volume.VolumePlugin { func ProbeVolumePlugins() []volume.VolumePlugin {
return []volume.VolumePlugin{&nfsPlugin{nil, newNFSMounter()}} return []volume.VolumePlugin{&nfsPlugin{nil, mount.New()}}
} }
type nfsPlugin struct { type nfsPlugin struct {
host volume.VolumeHost host volume.VolumeHost
mounter nfsMountInterface mounter mount.Interface
} }
var _ volume.VolumePlugin = &nfsPlugin{} var _ volume.VolumePlugin = &nfsPlugin{}
...@@ -66,7 +68,7 @@ func (plugin *nfsPlugin) NewBuilder(spec *volume.Spec, podRef *api.ObjectReferen ...@@ -66,7 +68,7 @@ func (plugin *nfsPlugin) NewBuilder(spec *volume.Spec, podRef *api.ObjectReferen
return plugin.newBuilderInternal(spec, podRef, plugin.mounter) return plugin.newBuilderInternal(spec, podRef, plugin.mounter)
} }
func (plugin *nfsPlugin) newBuilderInternal(spec *volume.Spec, podRef *api.ObjectReference, mounter nfsMountInterface) (volume.Builder, error) { func (plugin *nfsPlugin) newBuilderInternal(spec *volume.Spec, podRef *api.ObjectReference, mounter mount.Interface) (volume.Builder, error) {
return &nfs{ return &nfs{
volName: spec.Name, volName: spec.Name,
server: spec.VolumeSource.NFS.Server, server: spec.VolumeSource.NFS.Server,
...@@ -82,7 +84,7 @@ func (plugin *nfsPlugin) NewCleaner(volName string, podUID types.UID) (volume.Cl ...@@ -82,7 +84,7 @@ func (plugin *nfsPlugin) NewCleaner(volName string, podUID types.UID) (volume.Cl
return plugin.newCleanerInternal(volName, podUID, plugin.mounter) return plugin.newCleanerInternal(volName, podUID, plugin.mounter)
} }
func (plugin *nfsPlugin) newCleanerInternal(volName string, podUID types.UID, mounter nfsMountInterface) (volume.Cleaner, error) { func (plugin *nfsPlugin) newCleanerInternal(volName string, podUID types.UID, mounter mount.Interface) (volume.Cleaner, error) {
return &nfs{ return &nfs{
volName: volName, volName: volName,
server: "", server: "",
...@@ -101,7 +103,7 @@ type nfs struct { ...@@ -101,7 +103,7 @@ type nfs struct {
server string server string
exportPath string exportPath string
readOnly bool readOnly bool
mounter nfsMountInterface mounter mount.Interface
plugin *nfsPlugin plugin *nfsPlugin
} }
...@@ -119,9 +121,13 @@ func (nfsVolume *nfs) SetUpAt(dir string) error { ...@@ -119,9 +121,13 @@ func (nfsVolume *nfs) SetUpAt(dir string) error {
if mountpoint { if mountpoint {
return nil return nil
} }
exportDir := nfsVolume.exportPath
os.MkdirAll(dir, 0750) os.MkdirAll(dir, 0750)
err = nfsVolume.mounter.Mount(nfsVolume.server, exportDir, dir, nfsVolume.readOnly) source := fmt.Sprintf("%s:%s", nfsVolume.server, nfsVolume.exportPath)
options := []string{}
if nfsVolume.readOnly {
options = append(options, "ro")
}
err = nfsVolume.mounter.Mount(source, dir, "nfs", options)
if err != nil { if err != nil {
mountpoint, mntErr := nfsVolume.mounter.IsMountPoint(dir) mountpoint, mntErr := nfsVolume.mounter.IsMountPoint(dir)
if mntErr != nil { if mntErr != nil {
......
/*
Copyright 2014 Google Inc. All rights reserved.
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 nfs
import (
"os/exec"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/mount"
"github.com/golang/glog"
)
type nfsMountInterface interface {
// Mount takes an NFS host ip or hostname, a source directory (the exported directory), a target directory where the source directory will be mounted, and a boolean readOnly
Mount(server string, source string, target string, readOnly bool) error
// Umount wraps syscall.Mount().
Unmount(target string) error
List() ([]mount.MountPoint, error)
IsMountPoint(dir string) (bool, error)
}
// newNFSMounter returns an nfsMountInterface for the current system.
func newNFSMounter() nfsMountInterface {
return &nfsMounter{}
}
type nfsMounter struct{}
func (mounter *nfsMounter) Mount(server string, exportDir string, mountDir string, readOnly bool) error {
mountOptions := "rw"
if readOnly {
mountOptions = "ro"
}
mountArgs := []string{"-t", "nfs", server + ":" + exportDir, mountDir, "-o", mountOptions}
command := exec.Command("mount", mountArgs...)
output, errs := command.CombinedOutput()
if errs != nil {
glog.Errorf("NFS mounting failed: %v\n\tMount args are: %v\n\texportDir is: %v\n\tmountDir is: %v\n\tserver is: %v\n\tmount output is: %v", errs, mountArgs, exportDir, mountDir, server, string(output))
return errs
}
return nil
}
func (mounter *nfsMounter) Unmount(target string) error {
unmounter := mount.New()
return unmounter.Unmount(target, 0)
}
func (mounter *nfsMounter) List() ([]mount.MountPoint, error) {
return nil, nil
}
func (mounter *nfsMounter) IsMountPoint(dir string) (bool, error) {
isMounter := mount.New()
return isMounter.IsMountPoint(dir)
}
...@@ -17,7 +17,6 @@ limitations under the License. ...@@ -17,7 +17,6 @@ limitations under the License.
package nfs package nfs
import ( import (
"fmt"
"os" "os"
"testing" "testing"
...@@ -67,35 +66,6 @@ func contains(modes []api.AccessModeType, mode api.AccessModeType) bool { ...@@ -67,35 +66,6 @@ func contains(modes []api.AccessModeType, mode api.AccessModeType) bool {
return false return false
} }
type fakeNFSMounter struct {
FakeMounter mount.FakeMounter
}
func (fake *fakeNFSMounter) Mount(server string, source string, target string, readOnly bool) error {
flags := 0
if readOnly {
flags |= mount.FlagReadOnly
}
fake.FakeMounter.MountPoints = append(fake.FakeMounter.MountPoints, mount.MountPoint{Device: server, Path: target, Type: "nfs", Opts: nil, Freq: 0, Pass: 0})
return fake.FakeMounter.Mount(fmt.Sprintf("%s:%s", server, source), target, "nfs", 0, "")
}
func (fake *fakeNFSMounter) Unmount(target string) error {
fake.FakeMounter.MountPoints = []mount.MountPoint{}
return fake.FakeMounter.Unmount(target, 0)
}
func (fake *fakeNFSMounter) List() ([]mount.MountPoint, error) {
list, _ := fake.FakeMounter.List()
return list, nil
}
func (fake *fakeNFSMounter) IsMountPoint(dir string) (bool, error) {
list, _ := fake.FakeMounter.List()
isMount := len(list) > 0
return isMount, nil
}
func TestPlugin(t *testing.T) { func TestPlugin(t *testing.T) {
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volume.NewFakeVolumeHost("/tmp/fake", nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), volume.NewFakeVolumeHost("/tmp/fake", nil, nil))
...@@ -107,7 +77,7 @@ func TestPlugin(t *testing.T) { ...@@ -107,7 +77,7 @@ func TestPlugin(t *testing.T) {
Name: "vol1", Name: "vol1",
VolumeSource: api.VolumeSource{NFS: &api.NFSVolumeSource{"localhost", "/tmp", false}}, VolumeSource: api.VolumeSource{NFS: &api.NFSVolumeSource{"localhost", "/tmp", false}},
} }
fake := &fakeNFSMounter{} fake := &mount.FakeMounter{}
builder, err := plug.(*nfsPlugin).newBuilderInternal(volume.NewSpecFromVolume(spec), &api.ObjectReference{UID: types.UID("poduid")}, fake) builder, err := plug.(*nfsPlugin).newBuilderInternal(volume.NewSpecFromVolume(spec), &api.ObjectReference{UID: types.UID("poduid")}, fake)
volumePath := builder.GetPath() volumePath := builder.GetPath()
if err != nil { if err != nil {
...@@ -133,14 +103,14 @@ func TestPlugin(t *testing.T) { ...@@ -133,14 +103,14 @@ func TestPlugin(t *testing.T) {
if builder.(*nfs).readOnly { if builder.(*nfs).readOnly {
t.Errorf("The volume source should not be read-only and it is.") t.Errorf("The volume source should not be read-only and it is.")
} }
if len(fake.FakeMounter.Log) != 1 { if len(fake.Log) != 1 {
t.Errorf("Mount was not called exactly one time. It was called %d times.", len(fake.FakeMounter.Log)) t.Errorf("Mount was not called exactly one time. It was called %d times.", len(fake.Log))
} else { } else {
if fake.FakeMounter.Log[0].Action != mount.FakeActionMount { if fake.Log[0].Action != mount.FakeActionMount {
t.Errorf("Unexpected mounter action: %#v", fake.FakeMounter.Log[0]) t.Errorf("Unexpected mounter action: %#v", fake.Log[0])
} }
} }
fake.FakeMounter.ResetLog() fake.ResetLog()
cleaner, err := plug.(*nfsPlugin).newCleanerInternal("vol1", types.UID("poduid"), fake) cleaner, err := plug.(*nfsPlugin).newCleanerInternal("vol1", types.UID("poduid"), fake)
if err != nil { if err != nil {
...@@ -157,13 +127,13 @@ func TestPlugin(t *testing.T) { ...@@ -157,13 +127,13 @@ func TestPlugin(t *testing.T) {
} else if !os.IsNotExist(err) { } else if !os.IsNotExist(err) {
t.Errorf("SetUp() failed: %v", err) t.Errorf("SetUp() failed: %v", err)
} }
if len(fake.FakeMounter.Log) != 1 { if len(fake.Log) != 1 {
t.Errorf("Unmount was not called exactly one time. It was called %d times.", len(fake.FakeMounter.Log)) t.Errorf("Unmount was not called exactly one time. It was called %d times.", len(fake.Log))
} else { } else {
if fake.FakeMounter.Log[0].Action != mount.FakeActionUnmount { if fake.Log[0].Action != mount.FakeActionUnmount {
t.Errorf("Unexpected mounter action: %#v", fake.FakeMounter.Log[0]) t.Errorf("Unexpected mounter action: %#v", fake.Log[0])
} }
} }
fake.FakeMounter.ResetLog() fake.ResetLog()
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment