diff --git a/pkg/kubelet/volume/empty_dir/empty_dir.go b/pkg/kubelet/volume/empty_dir/empty_dir.go index fcfe2aab045..67fb03db41f 100644 --- a/pkg/kubelet/volume/empty_dir/empty_dir.go +++ b/pkg/kubelet/volume/empty_dir/empty_dir.go @@ -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) { // 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 { // Legacy mode instances can be cleaned up but not created anew. return nil, fmt.Errorf("legacy mode: can not create new instances") @@ -95,41 +95,46 @@ func (plugin *emptyDirPlugin) newBuilderInternal(spec *api.Volume, podRef *api.O medium = spec.EmptyDir.Medium } return &emptyDir{ - podUID: podRef.UID, - volName: spec.Name, - medium: medium, - mounter: mounter, - mediumer: mediumer, - plugin: plugin, - legacyMode: false, + podUID: podRef.UID, + volName: spec.Name, + medium: medium, + mounter: mounter, + mountDetector: mountDetector, + plugin: plugin, + legacyMode: false, }, nil } func (plugin *emptyDirPlugin) NewCleaner(volName string, podUID types.UID) (volume.Cleaner, error) { // 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 if plugin.legacyMode { legacy = true } ed := &emptyDir{ - podUID: podUID, - volName: volName, - medium: api.StorageTypeDefault, // might be changed later - mounter: mounter, - mediumer: mediumer, - plugin: plugin, - legacyMode: legacy, + podUID: podUID, + volName: volName, + medium: api.StorageTypeDefault, // might be changed later + mounter: mounter, + mountDetector: mountDetector, + plugin: plugin, + legacyMode: legacy, } return ed, nil } -// mediumer abstracts how to find what storageMedium a path is backed by. -type mediumer interface { - GetMedium(path string) (storageMedium, error) +// mountDetector abstracts how to find what kind of mount a path is backed by. +type mountDetector interface { + // 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 @@ -142,13 +147,13 @@ const ( // EmptyDir volumes are temporary directories exposed to the pod. // These do not persist beyond the lifetime of a pod. type emptyDir struct { - podUID types.UID - volName string - medium api.StorageType - mounter mount.Interface - mediumer mediumer - plugin *emptyDirPlugin - legacyMode bool + podUID types.UID + volName string + medium api.StorageType + mounter mount.Interface + mountDetector mountDetector + plugin *emptyDirPlugin + legacyMode bool } // SetUp creates new directory. @@ -183,9 +188,11 @@ func (ed *emptyDir) setupTmpfs(dir string) error { return err } // Make SetUp idempotent. - if medium, err := ed.mediumer.GetMedium(dir); err != nil { + medium, isMnt, err := ed.mountDetector.GetMountMedium(dir) + if err != nil { return err - } else if medium == mediumMemory { + } + if isMnt && medium == mediumMemory { return nil // current state is what we expect } return ed.mounter.Mount("tmpfs", dir, "tmpfs", 0, "") @@ -207,18 +214,16 @@ func (ed *emptyDir) TearDown() error { // TearDownAt simply discards everything in the directory. func (ed *emptyDir) TearDownAt(dir string) error { // 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 - } else { - switch medium { - case mediumMemory: - ed.medium = api.StorageTypeMemory - return ed.teardownTmpfs(dir) - default: - // assume StorageTypeDefault - return ed.teardownDefault(dir) - } } + if isMnt && medium == mediumMemory { + ed.medium = api.StorageTypeMemory + return ed.teardownTmpfs(dir) + } + // assume StorageTypeDefault + return ed.teardownDefault(dir) } func (ed *emptyDir) teardownDefault(dir string) error { diff --git a/pkg/kubelet/volume/empty_dir/empty_dir_linux.go b/pkg/kubelet/volume/empty_dir/empty_dir_linux.go index 16141abd89b..56bae2aa4b7 100644 --- a/pkg/kubelet/volume/empty_dir/empty_dir_linux.go +++ b/pkg/kubelet/volume/empty_dir/empty_dir_linux.go @@ -19,21 +19,27 @@ package empty_dir import ( "fmt" "syscall" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/util/mount" ) // Defined by Linux - the type number for tmpfs mounts. const linuxTmpfsMagic = 0x01021994 -// realMediumer implements mediumer in terms of syscalls. -type realMediumer struct{} +// realMountDetector implements mountDetector in terms of syscalls. +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{} 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 { - return mediumMemory, nil + return mediumMemory, isMnt, nil } - return mediumUnknown, nil + return mediumUnknown, isMnt, nil } diff --git a/pkg/kubelet/volume/empty_dir/empty_dir_test.go b/pkg/kubelet/volume/empty_dir/empty_dir_test.go index 5c79d7592b9..8be26098d64 100644 --- a/pkg/kubelet/volume/empty_dir/empty_dir_test.go +++ b/pkg/kubelet/volume/empty_dir/empty_dir_test.go @@ -56,12 +56,13 @@ func TestCanSupport(t *testing.T) { } } -type fakeMediumer struct { - typeToReturn storageMedium +type fakeMountDetector struct { + medium storageMedium + isMount bool } -func (fake *fakeMediumer) GetMedium(path string) (storageMedium, error) { - return fake.typeToReturn, nil +func (fake *fakeMountDetector) GetMountMedium(path string) (storageMedium, bool, error) { + return fake.medium, fake.isMount, nil } func TestPlugin(t *testing.T) { @@ -72,8 +73,8 @@ func TestPlugin(t *testing.T) { VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{Medium: api.StorageTypeDefault}}, } mounter := mount.FakeMounter{} - mediumer := fakeMediumer{} - builder, err := plug.(*emptyDirPlugin).newBuilderInternal(spec, &api.ObjectReference{UID: types.UID("poduid")}, &mounter, &mediumer) + mountDetector := fakeMountDetector{} + builder, err := plug.(*emptyDirPlugin).newBuilderInternal(spec, &api.ObjectReference{UID: types.UID("poduid")}, &mounter, &mountDetector) if err != nil { t.Errorf("Failed to make a new Builder: %v", err) } @@ -101,7 +102,7 @@ func TestPlugin(t *testing.T) { } 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 { 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}}, } mounter := mount.FakeMounter{} - mediumer := fakeMediumer{} - builder, err := plug.(*emptyDirPlugin).newBuilderInternal(spec, &api.ObjectReference{UID: types.UID("poduid")}, &mounter, &mediumer) + mountDetector := fakeMountDetector{} + builder, err := plug.(*emptyDirPlugin).newBuilderInternal(spec, &api.ObjectReference{UID: types.UID("poduid")}, &mounter, &mountDetector) if err != nil { t.Errorf("Failed to make a new Builder: %v", err) } @@ -164,7 +165,7 @@ func TestPluginTmpfs(t *testing.T) { } 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 { 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) } 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 { if mounter.Log[0].Action != mount.FakeActionUnmount { 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{}}} - 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") } - 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 { t.Errorf("Failed to make a new Cleaner: %v", err) } diff --git a/pkg/kubelet/volume/empty_dir/empty_dir_unsupported.go b/pkg/kubelet/volume/empty_dir/empty_dir_unsupported.go index 2b905bd2a93..ff2adc262ff 100644 --- a/pkg/kubelet/volume/empty_dir/empty_dir_unsupported.go +++ b/pkg/kubelet/volume/empty_dir/empty_dir_unsupported.go @@ -18,9 +18,9 @@ limitations under the License. package empty_dir -// realMediumer pretends to implement mediumer. -type realMediumer struct{} +// realMountDetector pretends to implement mediumer. +type realMountDetector struct{} -func (m *realMediumer) GetMedium(path string) (storageMedium, error) { - return mediumUnknown, nil +func (m *realMountDetector) GetMountMedium(path string) (storageMedium, bool, error) { + return mediumUnknown, false, nil }