Retool mount detection for tests

pull/6/head
Tim Hockin 2015-03-16 16:17:47 -07:00
parent 1725c23eb2
commit b42652cd3d
4 changed files with 75 additions and 63 deletions

View File

@ -82,10 +82,10 @@ 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, &realMediumer{}) return plugin.newBuilderInternal(spec, podRef, plugin.mounter, &realMountDetector{})
} }
func (plugin *emptyDirPlugin) newBuilderInternal(spec *api.Volume, podRef *api.ObjectReference, mounter mount.Interface, mediumer mediumer) (volume.Builder, error) { func (plugin *emptyDirPlugin) newBuilderInternal(spec *api.Volume, podRef *api.ObjectReference, mounter mount.Interface, mountDetector mountDetector) (volume.Builder, error) {
if plugin.legacyMode { if plugin.legacyMode {
// Legacy mode instances can be cleaned up but not created anew. // Legacy mode instances can be cleaned up but not created anew.
return nil, fmt.Errorf("legacy mode: can not create new instances") return nil, fmt.Errorf("legacy mode: can not create new instances")
@ -99,7 +99,7 @@ func (plugin *emptyDirPlugin) newBuilderInternal(spec *api.Volume, podRef *api.O
volName: spec.Name, volName: spec.Name,
medium: medium, medium: medium,
mounter: mounter, mounter: mounter,
mediumer: mediumer, mountDetector: mountDetector,
plugin: plugin, plugin: plugin,
legacyMode: false, legacyMode: false,
}, nil }, nil
@ -107,10 +107,10 @@ 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, &realMediumer{}) return plugin.newCleanerInternal(volName, podUID, plugin.mounter, &realMountDetector{})
} }
func (plugin *emptyDirPlugin) newCleanerInternal(volName string, podUID types.UID, mounter mount.Interface, mediumer mediumer) (volume.Cleaner, error) { func (plugin *emptyDirPlugin) newCleanerInternal(volName string, podUID types.UID, mounter mount.Interface, mountDetector mountDetector) (volume.Cleaner, error) {
legacy := false legacy := false
if plugin.legacyMode { if plugin.legacyMode {
legacy = true legacy = true
@ -120,16 +120,21 @@ func (plugin *emptyDirPlugin) newCleanerInternal(volName string, podUID types.UI
volName: volName, volName: volName,
medium: api.StorageTypeDefault, // might be changed later medium: api.StorageTypeDefault, // might be changed later
mounter: mounter, mounter: mounter,
mediumer: mediumer, mountDetector: mountDetector,
plugin: plugin, plugin: plugin,
legacyMode: legacy, legacyMode: legacy,
} }
return ed, nil return ed, nil
} }
// mediumer abstracts how to find what storageMedium a path is backed by. // mountDetector abstracts how to find what kind of mount a path is backed by.
type mediumer interface { type mountDetector interface {
GetMedium(path string) (storageMedium, error) // GetMountMedium determines what type of medium a given path is backed
// by and whether that path is a mount point. For example, if this
// returns (mediumMemory, false, nil), the caller knows that the path is
// on a memory FS (tmpfs on Linux) but is not the root mountpoint of
// that tmpfs.
GetMountMedium(path string) (storageMedium, bool, error)
} }
type storageMedium int type storageMedium int
@ -146,7 +151,7 @@ type emptyDir struct {
volName string volName string
medium api.StorageType medium api.StorageType
mounter mount.Interface mounter mount.Interface
mediumer mediumer mountDetector mountDetector
plugin *emptyDirPlugin plugin *emptyDirPlugin
legacyMode bool legacyMode bool
} }
@ -183,9 +188,11 @@ func (ed *emptyDir) setupTmpfs(dir string) error {
return err return err
} }
// Make SetUp idempotent. // Make SetUp idempotent.
if medium, err := ed.mediumer.GetMedium(dir); err != nil { medium, isMnt, err := ed.mountDetector.GetMountMedium(dir)
if err != nil {
return err return err
} else if medium == mediumMemory { }
if isMnt && medium == mediumMemory {
return nil // current state is what we expect return nil // current state is what we expect
} }
return ed.mounter.Mount("tmpfs", dir, "tmpfs", 0, "") return ed.mounter.Mount("tmpfs", dir, "tmpfs", 0, "")
@ -207,19 +214,17 @@ func (ed *emptyDir) TearDown() error {
// TearDownAt simply discards everything in the directory. // TearDownAt simply discards everything in the directory.
func (ed *emptyDir) TearDownAt(dir string) error { func (ed *emptyDir) TearDownAt(dir string) error {
// Figure out the medium. // Figure out the medium.
if medium, err := ed.mediumer.GetMedium(dir); err != nil { medium, isMnt, err := ed.mountDetector.GetMountMedium(dir)
if err != nil {
return err return err
} else { }
switch medium { if isMnt && medium == mediumMemory {
case mediumMemory:
ed.medium = api.StorageTypeMemory ed.medium = api.StorageTypeMemory
return ed.teardownTmpfs(dir) return ed.teardownTmpfs(dir)
default: }
// assume StorageTypeDefault // assume StorageTypeDefault
return ed.teardownDefault(dir) return ed.teardownDefault(dir)
} }
}
}
func (ed *emptyDir) teardownDefault(dir string) error { func (ed *emptyDir) teardownDefault(dir string) error {
tmpDir, err := volume.RenameDirectory(dir, ed.volName+".deleting~") tmpDir, err := volume.RenameDirectory(dir, ed.volName+".deleting~")

View File

@ -19,21 +19,27 @@ package empty_dir
import ( import (
"fmt" "fmt"
"syscall" "syscall"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/mount"
) )
// Defined by Linux - the type number for tmpfs mounts. // Defined by Linux - the type number for tmpfs mounts.
const linuxTmpfsMagic = 0x01021994 const linuxTmpfsMagic = 0x01021994
// realMediumer implements mediumer in terms of syscalls. // realMountDetector implements mountDetector in terms of syscalls.
type realMediumer struct{} type realMountDetector struct{}
func (m *realMediumer) GetMedium(path string) (storageMedium, error) { func (m *realMountDetector) GetMountMedium(path string) (storageMedium, bool, error) {
isMnt, err := mount.IsMountPoint(path)
if err != nil {
return 0, false, fmt.Errorf("IsMountPoint(%q): %v", path, err)
}
buf := syscall.Statfs_t{} buf := syscall.Statfs_t{}
if err := syscall.Statfs(path, &buf); err != nil { if err := syscall.Statfs(path, &buf); err != nil {
return 0, fmt.Errorf("statfs(%q): %v", path, err) return 0, false, fmt.Errorf("statfs(%q): %v", path, err)
} }
if buf.Type == linuxTmpfsMagic { if buf.Type == linuxTmpfsMagic {
return mediumMemory, nil return mediumMemory, isMnt, nil
} }
return mediumUnknown, nil return mediumUnknown, isMnt, nil
} }

View File

@ -56,12 +56,13 @@ func TestCanSupport(t *testing.T) {
} }
} }
type fakeMediumer struct { type fakeMountDetector struct {
typeToReturn storageMedium medium storageMedium
isMount bool
} }
func (fake *fakeMediumer) GetMedium(path string) (storageMedium, error) { func (fake *fakeMountDetector) GetMountMedium(path string) (storageMedium, bool, error) {
return fake.typeToReturn, nil return fake.medium, fake.isMount, nil
} }
func TestPlugin(t *testing.T) { func TestPlugin(t *testing.T) {
@ -72,8 +73,8 @@ func TestPlugin(t *testing.T) {
VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{Medium: api.StorageTypeDefault}}, VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{Medium: api.StorageTypeDefault}},
} }
mounter := mount.FakeMounter{} mounter := mount.FakeMounter{}
mediumer := fakeMediumer{} mountDetector := fakeMountDetector{}
builder, err := plug.(*emptyDirPlugin).newBuilderInternal(spec, &api.ObjectReference{UID: types.UID("poduid")}, &mounter, &mediumer) builder, err := plug.(*emptyDirPlugin).newBuilderInternal(spec, &api.ObjectReference{UID: types.UID("poduid")}, &mounter, &mountDetector)
if err != nil { if err != nil {
t.Errorf("Failed to make a new Builder: %v", err) t.Errorf("Failed to make a new Builder: %v", err)
} }
@ -101,7 +102,7 @@ func TestPlugin(t *testing.T) {
} }
mounter.ResetLog() mounter.ResetLog()
cleaner, err := plug.(*emptyDirPlugin).newCleanerInternal("vol1", types.UID("poduid"), &mounter, &fakeMediumer{}) cleaner, err := plug.(*emptyDirPlugin).newCleanerInternal("vol1", types.UID("poduid"), &mounter, &fakeMountDetector{})
if err != nil { if err != nil {
t.Errorf("Failed to make a new Cleaner: %v", err) t.Errorf("Failed to make a new Cleaner: %v", err)
} }
@ -131,8 +132,8 @@ func TestPluginTmpfs(t *testing.T) {
VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{Medium: api.StorageTypeMemory}}, VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{Medium: api.StorageTypeMemory}},
} }
mounter := mount.FakeMounter{} mounter := mount.FakeMounter{}
mediumer := fakeMediumer{} mountDetector := fakeMountDetector{}
builder, err := plug.(*emptyDirPlugin).newBuilderInternal(spec, &api.ObjectReference{UID: types.UID("poduid")}, &mounter, &mediumer) builder, err := plug.(*emptyDirPlugin).newBuilderInternal(spec, &api.ObjectReference{UID: types.UID("poduid")}, &mounter, &mountDetector)
if err != nil { if err != nil {
t.Errorf("Failed to make a new Builder: %v", err) t.Errorf("Failed to make a new Builder: %v", err)
} }
@ -164,7 +165,7 @@ func TestPluginTmpfs(t *testing.T) {
} }
mounter.ResetLog() mounter.ResetLog()
cleaner, err := plug.(*emptyDirPlugin).newCleanerInternal("vol1", types.UID("poduid"), &mounter, &fakeMediumer{mediumMemory}) cleaner, err := plug.(*emptyDirPlugin).newCleanerInternal("vol1", types.UID("poduid"), &mounter, &fakeMountDetector{mediumMemory, true})
if err != nil { if err != nil {
t.Errorf("Failed to make a new Cleaner: %v", err) t.Errorf("Failed to make a new Cleaner: %v", err)
} }
@ -181,7 +182,7 @@ func TestPluginTmpfs(t *testing.T) {
t.Errorf("SetUp() failed: %v", err) t.Errorf("SetUp() failed: %v", err)
} }
if len(mounter.Log) != 1 { if len(mounter.Log) != 1 {
t.Errorf("Expected 1 mounter call, got %#v", mounter.Log) t.Errorf("Expected 1 mounter call, got %d (%v)", len(mounter.Log), mounter.Log)
} else { } else {
if mounter.Log[0].Action != mount.FakeActionUnmount { if mounter.Log[0].Action != mount.FakeActionUnmount {
t.Errorf("Unexpected mounter action: %#v", mounter.Log[0]) t.Errorf("Unexpected mounter action: %#v", mounter.Log[0])
@ -221,11 +222,11 @@ func TestPluginLegacy(t *testing.T) {
} }
spec := api.Volume{VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}} spec := api.Volume{VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}
if _, err := plug.(*emptyDirPlugin).newBuilderInternal(&spec, &api.ObjectReference{UID: types.UID("poduid")}, &mount.FakeMounter{}, &fakeMediumer{}); err == nil { if _, err := plug.(*emptyDirPlugin).newBuilderInternal(&spec, &api.ObjectReference{UID: types.UID("poduid")}, &mount.FakeMounter{}, &fakeMountDetector{}); err == nil {
t.Errorf("Expected failiure") t.Errorf("Expected failiure")
} }
cleaner, err := plug.(*emptyDirPlugin).newCleanerInternal("vol1", types.UID("poduid"), &mount.FakeMounter{}, &fakeMediumer{}) cleaner, err := plug.(*emptyDirPlugin).newCleanerInternal("vol1", types.UID("poduid"), &mount.FakeMounter{}, &fakeMountDetector{})
if err != nil { if err != nil {
t.Errorf("Failed to make a new Cleaner: %v", err) t.Errorf("Failed to make a new Cleaner: %v", err)
} }

View File

@ -18,9 +18,9 @@ limitations under the License.
package empty_dir package empty_dir
// realMediumer pretends to implement mediumer. // realMountDetector pretends to implement mediumer.
type realMediumer struct{} type realMountDetector struct{}
func (m *realMediumer) GetMedium(path string) (storageMedium, error) { func (m *realMountDetector) GetMountMedium(path string) (storageMedium, bool, error) {
return mediumUnknown, nil return mediumUnknown, false, nil
} }