Merge pull request #6269 from ddysher/extend-mount

Abstract IsMountPoint and improve FakeMounter
This commit is contained in:
Vish Kannan 2015-04-02 15:29:38 -07:00
commit ee98731a2a
12 changed files with 70 additions and 92 deletions

View File

@ -39,11 +39,19 @@ func (f *FakeMounter) ResetLog() {
} }
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, flags uintptr, data string) error {
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, flags int) error {
newMountpoints := []MountPoint{}
for _, mp := range f.MountPoints {
if mp.Path != target {
newMountpoints = append(newMountpoints, MountPoint{Device: mp.Device, Path: mp.Path, Type: mp.Type})
}
}
f.MountPoints = newMountpoints
f.Log = append(f.Log, FakeAction{Action: FakeActionUnmount, Target: target}) f.Log = append(f.Log, FakeAction{Action: FakeActionUnmount, Target: target})
return nil return nil
} }
@ -51,3 +59,12 @@ func (f *FakeMounter) Unmount(target string, flags int) error {
func (f *FakeMounter) List() ([]MountPoint, error) { func (f *FakeMounter) List() ([]MountPoint, error) {
return f.MountPoints, nil return f.MountPoints, nil
} }
func (f *FakeMounter) IsMountPoint(file string) (bool, error) {
for _, mp := range f.MountPoints {
if mp.Path == file {
return true, nil
}
}
return false, nil
}

View File

@ -24,20 +24,15 @@ package mount
type Interface interface { type Interface interface {
// Mount wraps syscall.Mount(). // Mount wraps syscall.Mount().
Mount(source string, target string, fstype string, flags uintptr, data string) error Mount(source string, target string, fstype string, flags uintptr, data string) error
// Umount wraps syscall.Mount(). // Umount wraps syscall.Mount().
Unmount(target string, flags int) error Unmount(target string, flags int) 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
// consistent. // consistent.
List() ([]MountPoint, error) List() ([]MountPoint, error)
} // IsMountPoint determines if a directory is a mountpoint.
IsMountPoint(file string) (bool, error)
// New returns a mount.Interface for the current system.
func New() Interface {
return &Mounter{}
} }
// This represents a single line in /proc/mounts or /etc/fstab. // This represents a single line in /proc/mounts or /etc/fstab.
@ -50,7 +45,12 @@ type MountPoint struct {
Pass int Pass int
} }
// Examines /proc/mounts to find all other references to the device referenced // New returns a mount.Interface for the current system.
func New() Interface {
return &Mounter{}
}
// GetMountRefs finds all other references to the device referenced
// by mountPath; returns a list of paths. // by mountPath; returns a list of paths.
func GetMountRefs(mounter Interface, mountPath string) ([]string, error) { func GetMountRefs(mounter Interface, mountPath string) ([]string, error) {
mps, err := mounter.List() mps, err := mounter.List()

View File

@ -34,15 +34,16 @@ import (
const FlagBind = syscall.MS_BIND const FlagBind = syscall.MS_BIND
const FlagReadOnly = syscall.MS_RDONLY const FlagReadOnly = syscall.MS_RDONLY
// Mounter implements mount.Interface for linux platform.
type Mounter struct{} type Mounter struct{}
// Wraps syscall.Mount() // Mount wraps syscall.Mount()
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, flags uintptr, data string) error {
glog.V(5).Infof("Mounting %s %s %s %d %s", source, target, fstype, flags, data) glog.V(5).Infof("Mounting %s %s %s %d %s", source, target, fstype, flags, data)
return syscall.Mount(source, target, fstype, flags, data) return syscall.Mount(source, target, fstype, flags, data)
} }
// Wraps syscall.Unmount() // Unmount wraps syscall.Unmount()
func (mounter *Mounter) Unmount(target string, flags int) error { func (mounter *Mounter) Unmount(target string, flags int) error {
return syscall.Unmount(target, flags) return syscall.Unmount(target, flags)
} }
@ -50,6 +51,7 @@ func (mounter *Mounter) Unmount(target string, flags int) error {
// How many times to retry for a consistent read of /proc/mounts. // How many times to retry for a consistent read of /proc/mounts.
const maxListTries = 3 const maxListTries = 3
// List returns a list of all mounted filesystems.
func (*Mounter) List() ([]MountPoint, error) { func (*Mounter) List() ([]MountPoint, error) {
hash1, err := readProcMounts(nil) hash1, err := readProcMounts(nil)
if err != nil { if err != nil {
@ -71,11 +73,27 @@ func (*Mounter) List() ([]MountPoint, error) {
return nil, fmt.Errorf("failed to get a consistent snapshot of /proc/mounts after %d tries", maxListTries) return nil, fmt.Errorf("failed to get a consistent snapshot of /proc/mounts after %d tries", maxListTries)
} }
// IsMountPoint determines if a directory is a mountpoint, by comparing the device for the
// directory with the device for it's parent. If they are the same, it's not a mountpoint,
// if they're different, it is.
func (mounter *Mounter) IsMountPoint(file string) (bool, error) {
stat, err := os.Stat(file)
if err != nil {
return false, err
}
rootStat, err := os.Lstat(file + "/..")
if err != nil {
return false, err
}
// If the directory has the same device as parent, then it's not a mountpoint.
return stat.Sys().(*syscall.Stat_t).Dev != rootStat.Sys().(*syscall.Stat_t).Dev, nil
}
// As per the fstab man page. // As per the fstab man page.
const expectedNumFieldsPerLine = 6 const expectedNumFieldsPerLine = 6
// Read /proc/mounts and produces a hash of the contents. If the out argument // readProcMounts reads /proc/mounts and produces a hash of the contents. If the out
// 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) {
file, err := os.Open("/proc/mounts") file, err := os.Open("/proc/mounts")
if err != nil { if err != nil {

View File

@ -34,3 +34,7 @@ func (mounter *Mounter) Unmount(target string, flags int) error {
func (mounter *Mounter) List() ([]MountPoint, error) { func (mounter *Mounter) List() ([]MountPoint, error) {
return []MountPoint{}, nil return []MountPoint{}, nil
} }
func (mounter *Mounter) IsMountPoint(file string) (bool, error) {
return false, nil
}

View File

@ -1,40 +0,0 @@
// +build !windows
/*
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 mount
import (
"os"
"syscall"
)
// Determine if a directory is a mountpoint, by comparing the device for the directory
// with the device for it's parent. If they are the same, it's not a mountpoint, if they're
// different, it is.
func IsMountPoint(file string) (bool, error) {
stat, err := os.Stat(file)
if err != nil {
return false, err
}
rootStat, err := os.Lstat(file + "/..")
if err != nil {
return false, err
}
// If the directory has the same device as parent, then it's not a mountpoint.
return stat.Sys().(*syscall.Stat_t).Dev != rootStat.Sys().(*syscall.Stat_t).Dev, nil
}

View File

@ -1,28 +0,0 @@
// +build windows
/*
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 mount
import (
"fmt"
)
// Dummy implementation for Windows
func IsMountPoint(file string) (bool, error) {
return false, fmt.Errorf("unimplemented")
}

View File

@ -82,7 +82,7 @@ func (plugin *emptyDirPlugin) CanSupport(spec *api.Volume) bool {
func (plugin *emptyDirPlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference) (volume.Builder, error) { func (plugin *emptyDirPlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference) (volume.Builder, error) {
// Inject real implementations here, test through the internal function. // Inject real implementations here, test through the internal function.
return plugin.newBuilderInternal(spec, podRef, plugin.mounter, &realMountDetector{}) return plugin.newBuilderInternal(spec, podRef, plugin.mounter, &realMountDetector{plugin.mounter})
} }
func (plugin *emptyDirPlugin) newBuilderInternal(spec *api.Volume, podRef *api.ObjectReference, mounter mount.Interface, mountDetector mountDetector) (volume.Builder, error) { func (plugin *emptyDirPlugin) newBuilderInternal(spec *api.Volume, podRef *api.ObjectReference, mounter mount.Interface, mountDetector mountDetector) (volume.Builder, error) {
@ -107,7 +107,7 @@ func (plugin *emptyDirPlugin) newBuilderInternal(spec *api.Volume, podRef *api.O
func (plugin *emptyDirPlugin) NewCleaner(volName string, podUID types.UID) (volume.Cleaner, error) { func (plugin *emptyDirPlugin) NewCleaner(volName string, podUID types.UID) (volume.Cleaner, error) {
// Inject real implementations here, test through the internal function. // Inject real implementations here, test through the internal function.
return plugin.newCleanerInternal(volName, podUID, plugin.mounter, &realMountDetector{}) return plugin.newCleanerInternal(volName, podUID, plugin.mounter, &realMountDetector{plugin.mounter})
} }
func (plugin *emptyDirPlugin) newCleanerInternal(volName string, podUID types.UID, mounter mount.Interface, mountDetector mountDetector) (volume.Cleaner, error) { func (plugin *emptyDirPlugin) newCleanerInternal(volName string, podUID types.UID, mounter mount.Interface, mountDetector mountDetector) (volume.Cleaner, error) {

View File

@ -27,10 +27,12 @@ import (
const linuxTmpfsMagic = 0x01021994 const linuxTmpfsMagic = 0x01021994
// realMountDetector implements mountDetector in terms of syscalls. // realMountDetector implements mountDetector in terms of syscalls.
type realMountDetector struct{} type realMountDetector struct {
mounter mount.Interface
}
func (m *realMountDetector) GetMountMedium(path string) (storageMedium, bool, error) { func (m *realMountDetector) GetMountMedium(path string) (storageMedium, bool, error) {
isMnt, err := mount.IsMountPoint(path) isMnt, err := m.mounter.IsMountPoint(path)
if err != nil { if err != nil {
return 0, false, fmt.Errorf("IsMountPoint(%q): %v", path, err) return 0, false, fmt.Errorf("IsMountPoint(%q): %v", path, err)
} }

View File

@ -18,8 +18,12 @@ limitations under the License.
package empty_dir package empty_dir
import "github.com/GoogleCloudPlatform/kubernetes/pkg/util/mount"
// realMountDetector pretends to implement mediumer. // realMountDetector pretends to implement mediumer.
type realMountDetector struct{} type realMountDetector struct {
mounter mount.Interface
}
func (m *realMountDetector) GetMountMedium(path string) (storageMedium, bool, error) { func (m *realMountDetector) GetMountMedium(path string) (storageMedium, bool, error) {
return mediumUnknown, false, nil return mediumUnknown, false, nil

View File

@ -183,7 +183,7 @@ func (pd *gcePersistentDisk) SetUpAt(dir string) error {
} }
// TODO: handle failed mounts here. // TODO: handle failed mounts here.
mountpoint, err := mount.IsMountPoint(dir) mountpoint, err := pd.mounter.IsMountPoint(dir)
glog.V(4).Infof("PersistentDisk set up: %s %v %v", dir, mountpoint, err) glog.V(4).Infof("PersistentDisk set up: %s %v %v", dir, mountpoint, err)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
return err return err
@ -211,7 +211,7 @@ 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, "") err = pd.mounter.Mount(globalPDPath, dir, "", mount.FlagBind|flags, "")
if err != nil { if err != nil {
mountpoint, mntErr := mount.IsMountPoint(dir) mountpoint, mntErr := pd.mounter.IsMountPoint(dir)
if mntErr != nil { if mntErr != nil {
glog.Errorf("isMountpoint check failed: %v", mntErr) glog.Errorf("isMountpoint check failed: %v", mntErr)
return err return err
@ -221,7 +221,7 @@ func (pd *gcePersistentDisk) SetUpAt(dir string) error {
glog.Errorf("Failed to unmount: %v", mntErr) glog.Errorf("Failed to unmount: %v", mntErr)
return err return err
} }
mountpoint, mntErr := mount.IsMountPoint(dir) mountpoint, mntErr := pd.mounter.IsMountPoint(dir)
if mntErr != nil { if mntErr != nil {
glog.Errorf("isMountpoint check failed: %v", mntErr) glog.Errorf("isMountpoint check failed: %v", mntErr)
return err return err
@ -262,7 +262,7 @@ func (pd *gcePersistentDisk) TearDown() error {
// Unmounts the bind mount, and detaches the disk only if the PD // Unmounts the bind mount, and detaches the disk only if the PD
// resource was the last reference to that disk on the kubelet. // resource was the last reference to that disk on the kubelet.
func (pd *gcePersistentDisk) TearDownAt(dir string) error { func (pd *gcePersistentDisk) TearDownAt(dir string) error {
mountpoint, err := mount.IsMountPoint(dir) mountpoint, err := pd.mounter.IsMountPoint(dir)
if err != nil { if err != nil {
return err return err
} }
@ -287,7 +287,7 @@ func (pd *gcePersistentDisk) TearDownAt(dir string) error {
return err return err
} }
} }
mountpoint, mntErr := mount.IsMountPoint(dir) mountpoint, mntErr := pd.mounter.IsMountPoint(dir)
if mntErr != nil { if mntErr != nil {
glog.Errorf("isMountpoint check failed: %v", mntErr) glog.Errorf("isMountpoint check failed: %v", mntErr)
return err return err

View File

@ -68,7 +68,7 @@ func (util *GCEDiskUtil) AttachAndMountDisk(pd *gcePersistentDisk, globalPDPath
} }
// Only mount the PD globally once. // Only mount the PD globally once.
mountpoint, err := mount.IsMountPoint(globalPDPath) mountpoint, err := pd.mounter.IsMountPoint(globalPDPath)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
if err := os.MkdirAll(globalPDPath, 0750); err != nil { if err := os.MkdirAll(globalPDPath, 0750); err != nil {

View File

@ -67,5 +67,6 @@ func (mounter *nfsMounter) List() ([]mount.MountPoint, error) {
} }
func (mounter *nfsMounter) IsMountPoint(dir string) (bool, error) { func (mounter *nfsMounter) IsMountPoint(dir string) (bool, error) {
return mount.IsMountPoint(dir) isMounter := mount.New()
return isMounter.IsMountPoint(dir)
} }