Merge pull request #28714 from ciwang/robust-unmount
Automatic merge from submit-queue Add OpenFile check if device is in use before unmount Fixes #28252
This commit is contained in:
		@@ -51,6 +51,19 @@ func (mi *fakeMountInterface) IsLikelyNotMountPoint(file string) (bool, error) {
 | 
				
			|||||||
	return false, fmt.Errorf("unsupported")
 | 
						return false, fmt.Errorf("unsupported")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (mi *fakeMountInterface) DeviceOpened(pathname string) (bool, error) {
 | 
				
			||||||
 | 
						for _, mp := range mi.mountPoints {
 | 
				
			||||||
 | 
							if mp.Device == pathname {
 | 
				
			||||||
 | 
								return true, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (mi *fakeMountInterface) PathIsDevice(pathname string) (bool, error) {
 | 
				
			||||||
 | 
						return true, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func fakeContainerMgrMountInt() mount.Interface {
 | 
					func fakeContainerMgrMountInt() mount.Interface {
 | 
				
			||||||
	return &fakeMountInterface{
 | 
						return &fakeMountInterface{
 | 
				
			||||||
		[]mount.MountPoint{
 | 
							[]mount.MountPoint{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,6 +44,19 @@ func (mi *fakeMountInterface) IsLikelyNotMountPoint(file string) (bool, error) {
 | 
				
			|||||||
	return false, fmt.Errorf("unsupported")
 | 
						return false, fmt.Errorf("unsupported")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (mi *fakeMountInterface) DeviceOpened(pathname string) (bool, error) {
 | 
				
			||||||
 | 
						for _, mp := range mi.mountPoints {
 | 
				
			||||||
 | 
							if mp.Device == pathname {
 | 
				
			||||||
 | 
								return true, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (mi *fakeMountInterface) PathIsDevice(pathname string) (bool, error) {
 | 
				
			||||||
 | 
						return true, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func fakeContainerMgrMountInt() mount.Interface {
 | 
					func fakeContainerMgrMountInt() mount.Interface {
 | 
				
			||||||
	return &fakeMountInterface{
 | 
						return &fakeMountInterface{
 | 
				
			||||||
		[]mount.MountPoint{
 | 
							[]mount.MountPoint{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -405,6 +405,21 @@ func NewMainKubelet(
 | 
				
			|||||||
	klet.podCache = kubecontainer.NewCache()
 | 
						klet.podCache = kubecontainer.NewCache()
 | 
				
			||||||
	klet.podManager = kubepod.NewBasicPodManager(kubepod.NewBasicMirrorClient(klet.kubeClient))
 | 
						klet.podManager = kubepod.NewBasicPodManager(kubepod.NewBasicMirrorClient(klet.kubeClient))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						klet.volumePluginMgr, err =
 | 
				
			||||||
 | 
							NewInitializedVolumePluginMgr(klet, volumePlugins)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						klet.volumeManager, err = volumemanager.NewVolumeManager(
 | 
				
			||||||
 | 
							enableControllerAttachDetach,
 | 
				
			||||||
 | 
							hostname,
 | 
				
			||||||
 | 
							klet.podManager,
 | 
				
			||||||
 | 
							klet.kubeClient,
 | 
				
			||||||
 | 
							klet.volumePluginMgr,
 | 
				
			||||||
 | 
							klet.containerRuntime,
 | 
				
			||||||
 | 
							mounter)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Initialize the runtime.
 | 
						// Initialize the runtime.
 | 
				
			||||||
	switch containerRuntime {
 | 
						switch containerRuntime {
 | 
				
			||||||
	case "docker":
 | 
						case "docker":
 | 
				
			||||||
@@ -510,7 +525,8 @@ func NewMainKubelet(
 | 
				
			|||||||
		klet.podManager,
 | 
							klet.podManager,
 | 
				
			||||||
		klet.kubeClient,
 | 
							klet.kubeClient,
 | 
				
			||||||
		klet.volumePluginMgr,
 | 
							klet.volumePluginMgr,
 | 
				
			||||||
		klet.containerRuntime)
 | 
							klet.containerRuntime,
 | 
				
			||||||
 | 
							mounter)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	runtimeCache, err := kubecontainer.NewRuntimeCache(klet.containerRuntime)
 | 
						runtimeCache, err := kubecontainer.NewRuntimeCache(klet.containerRuntime)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -284,13 +284,15 @@ func newTestKubeletWithImageList(
 | 
				
			|||||||
		t.Fatalf("failed to initialize VolumePluginMgr: %v", err)
 | 
							t.Fatalf("failed to initialize VolumePluginMgr: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						kubelet.mounter = &mount.FakeMounter{}
 | 
				
			||||||
	kubelet.volumeManager, err = kubeletvolume.NewVolumeManager(
 | 
						kubelet.volumeManager, err = kubeletvolume.NewVolumeManager(
 | 
				
			||||||
		controllerAttachDetachEnabled,
 | 
							controllerAttachDetachEnabled,
 | 
				
			||||||
		kubelet.hostname,
 | 
							kubelet.hostname,
 | 
				
			||||||
		kubelet.podManager,
 | 
							kubelet.podManager,
 | 
				
			||||||
		fakeKubeClient,
 | 
							fakeKubeClient,
 | 
				
			||||||
		kubelet.volumePluginMgr,
 | 
							kubelet.volumePluginMgr,
 | 
				
			||||||
		fakeRuntime)
 | 
							fakeRuntime,
 | 
				
			||||||
 | 
							kubelet.mounter)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("failed to initialize volume manager: %v", err)
 | 
							t.Fatalf("failed to initialize volume manager: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -432,7 +434,6 @@ func TestSyncPodsDeletesWhenSourcesAreReady(t *testing.T) {
 | 
				
			|||||||
func TestVolumeAttachAndMountControllerDisabled(t *testing.T) {
 | 
					func TestVolumeAttachAndMountControllerDisabled(t *testing.T) {
 | 
				
			||||||
	testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
 | 
						testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
 | 
				
			||||||
	kubelet := testKubelet.kubelet
 | 
						kubelet := testKubelet.kubelet
 | 
				
			||||||
	kubelet.mounter = &mount.FakeMounter{}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pod := podWithUidNameNsSpec("12345678", "foo", "test", api.PodSpec{
 | 
						pod := podWithUidNameNsSpec("12345678", "foo", "test", api.PodSpec{
 | 
				
			||||||
		Volumes: []api.Volume{
 | 
							Volumes: []api.Volume{
 | 
				
			||||||
@@ -503,7 +504,6 @@ func TestVolumeAttachAndMountControllerDisabled(t *testing.T) {
 | 
				
			|||||||
func TestVolumeUnmountAndDetachControllerDisabled(t *testing.T) {
 | 
					func TestVolumeUnmountAndDetachControllerDisabled(t *testing.T) {
 | 
				
			||||||
	testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
 | 
						testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
 | 
				
			||||||
	kubelet := testKubelet.kubelet
 | 
						kubelet := testKubelet.kubelet
 | 
				
			||||||
	kubelet.mounter = &mount.FakeMounter{}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pod := podWithUidNameNsSpec("12345678", "foo", "test", api.PodSpec{
 | 
						pod := podWithUidNameNsSpec("12345678", "foo", "test", api.PodSpec{
 | 
				
			||||||
		Volumes: []api.Volume{
 | 
							Volumes: []api.Volume{
 | 
				
			||||||
@@ -615,7 +615,6 @@ func TestVolumeUnmountAndDetachControllerDisabled(t *testing.T) {
 | 
				
			|||||||
func TestVolumeAttachAndMountControllerEnabled(t *testing.T) {
 | 
					func TestVolumeAttachAndMountControllerEnabled(t *testing.T) {
 | 
				
			||||||
	testKubelet := newTestKubelet(t, true /* controllerAttachDetachEnabled */)
 | 
						testKubelet := newTestKubelet(t, true /* controllerAttachDetachEnabled */)
 | 
				
			||||||
	kubelet := testKubelet.kubelet
 | 
						kubelet := testKubelet.kubelet
 | 
				
			||||||
	kubelet.mounter = &mount.FakeMounter{}
 | 
					 | 
				
			||||||
	kubeClient := testKubelet.fakeKubeClient
 | 
						kubeClient := testKubelet.fakeKubeClient
 | 
				
			||||||
	kubeClient.AddReactor("get", "nodes",
 | 
						kubeClient.AddReactor("get", "nodes",
 | 
				
			||||||
		func(action core.Action) (bool, runtime.Object, error) {
 | 
							func(action core.Action) (bool, runtime.Object, error) {
 | 
				
			||||||
@@ -710,7 +709,6 @@ func TestVolumeAttachAndMountControllerEnabled(t *testing.T) {
 | 
				
			|||||||
func TestVolumeUnmountAndDetachControllerEnabled(t *testing.T) {
 | 
					func TestVolumeUnmountAndDetachControllerEnabled(t *testing.T) {
 | 
				
			||||||
	testKubelet := newTestKubelet(t, true /* controllerAttachDetachEnabled */)
 | 
						testKubelet := newTestKubelet(t, true /* controllerAttachDetachEnabled */)
 | 
				
			||||||
	kubelet := testKubelet.kubelet
 | 
						kubelet := testKubelet.kubelet
 | 
				
			||||||
	kubelet.mounter = &mount.FakeMounter{}
 | 
					 | 
				
			||||||
	kubeClient := testKubelet.fakeKubeClient
 | 
						kubeClient := testKubelet.fakeKubeClient
 | 
				
			||||||
	kubeClient.AddReactor("get", "nodes",
 | 
						kubeClient.AddReactor("get", "nodes",
 | 
				
			||||||
		func(action core.Action) (bool, runtime.Object, error) {
 | 
							func(action core.Action) (bool, runtime.Object, error) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -98,7 +98,8 @@ func TestRunOnce(t *testing.T) {
 | 
				
			|||||||
		kb.podManager,
 | 
							kb.podManager,
 | 
				
			||||||
		kb.kubeClient,
 | 
							kb.kubeClient,
 | 
				
			||||||
		kb.volumePluginMgr,
 | 
							kb.volumePluginMgr,
 | 
				
			||||||
		fakeRuntime)
 | 
							fakeRuntime,
 | 
				
			||||||
 | 
							kb.mounter)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kb.networkPlugin, _ = network.InitNetworkPlugin([]network.NetworkPlugin{}, "", nettest.NewFakeHost(nil), componentconfig.HairpinNone, kb.nonMasqueradeCIDR)
 | 
						kb.networkPlugin, _ = network.InitNetworkPlugin([]network.NetworkPlugin{}, "", nettest.NewFakeHost(nil), componentconfig.HairpinNone, kb.nonMasqueradeCIDR)
 | 
				
			||||||
	// TODO: Factor out "StatsProvider" from Kubelet so we don't have a cyclic dependency
 | 
						// TODO: Factor out "StatsProvider" from Kubelet so we don't have a cyclic dependency
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -582,7 +582,8 @@ func (asw *actualStateOfWorld) newAttachedVolume(
 | 
				
			|||||||
			VolumeName:         attachedVolume.volumeName,
 | 
								VolumeName:         attachedVolume.volumeName,
 | 
				
			||||||
			VolumeSpec:         attachedVolume.spec,
 | 
								VolumeSpec:         attachedVolume.spec,
 | 
				
			||||||
			NodeName:           asw.nodeName,
 | 
								NodeName:           asw.nodeName,
 | 
				
			||||||
			PluginIsAttachable: attachedVolume.pluginIsAttachable},
 | 
								PluginIsAttachable: attachedVolume.pluginIsAttachable,
 | 
				
			||||||
 | 
								DevicePath:         attachedVolume.devicePath},
 | 
				
			||||||
		GloballyMounted: attachedVolume.globallyMounted}
 | 
							GloballyMounted: attachedVolume.globallyMounted}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,6 +26,7 @@ import (
 | 
				
			|||||||
	"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
 | 
						"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/kubelet/volumemanager/cache"
 | 
						"k8s.io/kubernetes/pkg/kubelet/volumemanager/cache"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/util/goroutinemap"
 | 
						"k8s.io/kubernetes/pkg/util/goroutinemap"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/util/mount"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/util/wait"
 | 
						"k8s.io/kubernetes/pkg/util/wait"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/volume/util/operationexecutor"
 | 
						"k8s.io/kubernetes/pkg/volume/util/operationexecutor"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -62,6 +63,7 @@ type Reconciler interface {
 | 
				
			|||||||
// operationExecutor - used to trigger attach/detach/mount/unmount operations
 | 
					// operationExecutor - used to trigger attach/detach/mount/unmount operations
 | 
				
			||||||
//   safely (prevents more than one operation from being triggered on the same
 | 
					//   safely (prevents more than one operation from being triggered on the same
 | 
				
			||||||
//   volume)
 | 
					//   volume)
 | 
				
			||||||
 | 
					// mounter - mounter passed in from kubelet, passed down unmount path
 | 
				
			||||||
func NewReconciler(
 | 
					func NewReconciler(
 | 
				
			||||||
	kubeClient internalclientset.Interface,
 | 
						kubeClient internalclientset.Interface,
 | 
				
			||||||
	controllerAttachDetachEnabled bool,
 | 
						controllerAttachDetachEnabled bool,
 | 
				
			||||||
@@ -70,7 +72,8 @@ func NewReconciler(
 | 
				
			|||||||
	hostName string,
 | 
						hostName string,
 | 
				
			||||||
	desiredStateOfWorld cache.DesiredStateOfWorld,
 | 
						desiredStateOfWorld cache.DesiredStateOfWorld,
 | 
				
			||||||
	actualStateOfWorld cache.ActualStateOfWorld,
 | 
						actualStateOfWorld cache.ActualStateOfWorld,
 | 
				
			||||||
	operationExecutor operationexecutor.OperationExecutor) Reconciler {
 | 
						operationExecutor operationexecutor.OperationExecutor,
 | 
				
			||||||
 | 
						mounter mount.Interface) Reconciler {
 | 
				
			||||||
	return &reconciler{
 | 
						return &reconciler{
 | 
				
			||||||
		kubeClient:                    kubeClient,
 | 
							kubeClient:                    kubeClient,
 | 
				
			||||||
		controllerAttachDetachEnabled: controllerAttachDetachEnabled,
 | 
							controllerAttachDetachEnabled: controllerAttachDetachEnabled,
 | 
				
			||||||
@@ -80,6 +83,7 @@ func NewReconciler(
 | 
				
			|||||||
		desiredStateOfWorld:           desiredStateOfWorld,
 | 
							desiredStateOfWorld:           desiredStateOfWorld,
 | 
				
			||||||
		actualStateOfWorld:            actualStateOfWorld,
 | 
							actualStateOfWorld:            actualStateOfWorld,
 | 
				
			||||||
		operationExecutor:             operationExecutor,
 | 
							operationExecutor:             operationExecutor,
 | 
				
			||||||
 | 
							mounter:                       mounter,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -92,6 +96,7 @@ type reconciler struct {
 | 
				
			|||||||
	desiredStateOfWorld           cache.DesiredStateOfWorld
 | 
						desiredStateOfWorld           cache.DesiredStateOfWorld
 | 
				
			||||||
	actualStateOfWorld            cache.ActualStateOfWorld
 | 
						actualStateOfWorld            cache.ActualStateOfWorld
 | 
				
			||||||
	operationExecutor             operationexecutor.OperationExecutor
 | 
						operationExecutor             operationexecutor.OperationExecutor
 | 
				
			||||||
 | 
						mounter                       mount.Interface
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (rc *reconciler) Run(stopCh <-chan struct{}) {
 | 
					func (rc *reconciler) Run(stopCh <-chan struct{}) {
 | 
				
			||||||
@@ -264,7 +269,7 @@ func (rc *reconciler) reconciliationLoopFunc() func() {
 | 
				
			|||||||
						attachedVolume.VolumeName,
 | 
											attachedVolume.VolumeName,
 | 
				
			||||||
						attachedVolume.VolumeSpec.Name())
 | 
											attachedVolume.VolumeSpec.Name())
 | 
				
			||||||
					err := rc.operationExecutor.UnmountDevice(
 | 
										err := rc.operationExecutor.UnmountDevice(
 | 
				
			||||||
						attachedVolume.AttachedVolume, rc.actualStateOfWorld)
 | 
											attachedVolume.AttachedVolume, rc.actualStateOfWorld, rc.mounter)
 | 
				
			||||||
					if err != nil &&
 | 
										if err != nil &&
 | 
				
			||||||
						!goroutinemap.IsAlreadyExists(err) &&
 | 
											!goroutinemap.IsAlreadyExists(err) &&
 | 
				
			||||||
						!goroutinemap.IsExponentialBackoff(err) {
 | 
											!goroutinemap.IsExponentialBackoff(err) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,7 @@ import (
 | 
				
			|||||||
	"k8s.io/kubernetes/pkg/client/testing/core"
 | 
						"k8s.io/kubernetes/pkg/client/testing/core"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/kubelet/volumemanager/cache"
 | 
						"k8s.io/kubernetes/pkg/kubelet/volumemanager/cache"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/runtime"
 | 
						"k8s.io/kubernetes/pkg/runtime"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/util/mount"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/util/wait"
 | 
						"k8s.io/kubernetes/pkg/util/wait"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/volume"
 | 
						"k8s.io/kubernetes/pkg/volume"
 | 
				
			||||||
	volumetesting "k8s.io/kubernetes/pkg/volume/testing"
 | 
						volumetesting "k8s.io/kubernetes/pkg/volume/testing"
 | 
				
			||||||
@@ -62,7 +63,8 @@ func Test_Run_Positive_DoNothing(t *testing.T) {
 | 
				
			|||||||
		nodeName,
 | 
							nodeName,
 | 
				
			||||||
		dsw,
 | 
							dsw,
 | 
				
			||||||
		asw,
 | 
							asw,
 | 
				
			||||||
		oex)
 | 
							oex,
 | 
				
			||||||
 | 
							&mount.FakeMounter{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Act
 | 
						// Act
 | 
				
			||||||
	go reconciler.Run(wait.NeverStop)
 | 
						go reconciler.Run(wait.NeverStop)
 | 
				
			||||||
@@ -94,7 +96,8 @@ func Test_Run_Positive_VolumeAttachAndMount(t *testing.T) {
 | 
				
			|||||||
		nodeName,
 | 
							nodeName,
 | 
				
			||||||
		dsw,
 | 
							dsw,
 | 
				
			||||||
		asw,
 | 
							asw,
 | 
				
			||||||
		oex)
 | 
							oex,
 | 
				
			||||||
 | 
							&mount.FakeMounter{})
 | 
				
			||||||
	pod := &api.Pod{
 | 
						pod := &api.Pod{
 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{
 | 
							ObjectMeta: api.ObjectMeta{
 | 
				
			||||||
			Name: "pod1",
 | 
								Name: "pod1",
 | 
				
			||||||
@@ -161,7 +164,8 @@ func Test_Run_Positive_VolumeMountControllerAttachEnabled(t *testing.T) {
 | 
				
			|||||||
		nodeName,
 | 
							nodeName,
 | 
				
			||||||
		dsw,
 | 
							dsw,
 | 
				
			||||||
		asw,
 | 
							asw,
 | 
				
			||||||
		oex)
 | 
							oex,
 | 
				
			||||||
 | 
							&mount.FakeMounter{})
 | 
				
			||||||
	pod := &api.Pod{
 | 
						pod := &api.Pod{
 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{
 | 
							ObjectMeta: api.ObjectMeta{
 | 
				
			||||||
			Name: "pod1",
 | 
								Name: "pod1",
 | 
				
			||||||
@@ -228,7 +232,8 @@ func Test_Run_Positive_VolumeAttachMountUnmountDetach(t *testing.T) {
 | 
				
			|||||||
		nodeName,
 | 
							nodeName,
 | 
				
			||||||
		dsw,
 | 
							dsw,
 | 
				
			||||||
		asw,
 | 
							asw,
 | 
				
			||||||
		oex)
 | 
							oex,
 | 
				
			||||||
 | 
							&mount.FakeMounter{})
 | 
				
			||||||
	pod := &api.Pod{
 | 
						pod := &api.Pod{
 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{
 | 
							ObjectMeta: api.ObjectMeta{
 | 
				
			||||||
			Name: "pod1",
 | 
								Name: "pod1",
 | 
				
			||||||
@@ -307,7 +312,8 @@ func Test_Run_Positive_VolumeUnmountControllerAttachEnabled(t *testing.T) {
 | 
				
			|||||||
		nodeName,
 | 
							nodeName,
 | 
				
			||||||
		dsw,
 | 
							dsw,
 | 
				
			||||||
		asw,
 | 
							asw,
 | 
				
			||||||
		oex)
 | 
							oex,
 | 
				
			||||||
 | 
							&mount.FakeMounter{})
 | 
				
			||||||
	pod := &api.Pod{
 | 
						pod := &api.Pod{
 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{
 | 
							ObjectMeta: api.ObjectMeta{
 | 
				
			||||||
			Name: "pod1",
 | 
								Name: "pod1",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,6 +31,7 @@ import (
 | 
				
			|||||||
	"k8s.io/kubernetes/pkg/kubelet/volumemanager/cache"
 | 
						"k8s.io/kubernetes/pkg/kubelet/volumemanager/cache"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/kubelet/volumemanager/populator"
 | 
						"k8s.io/kubernetes/pkg/kubelet/volumemanager/populator"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/kubelet/volumemanager/reconciler"
 | 
						"k8s.io/kubernetes/pkg/kubelet/volumemanager/reconciler"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/util/mount"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/util/runtime"
 | 
						"k8s.io/kubernetes/pkg/util/runtime"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/util/sets"
 | 
						"k8s.io/kubernetes/pkg/util/sets"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/util/wait"
 | 
						"k8s.io/kubernetes/pkg/util/wait"
 | 
				
			||||||
@@ -142,7 +143,8 @@ func NewVolumeManager(
 | 
				
			|||||||
	podManager pod.Manager,
 | 
						podManager pod.Manager,
 | 
				
			||||||
	kubeClient internalclientset.Interface,
 | 
						kubeClient internalclientset.Interface,
 | 
				
			||||||
	volumePluginMgr *volume.VolumePluginMgr,
 | 
						volumePluginMgr *volume.VolumePluginMgr,
 | 
				
			||||||
	kubeContainerRuntime kubecontainer.Runtime) (VolumeManager, error) {
 | 
						kubeContainerRuntime kubecontainer.Runtime,
 | 
				
			||||||
 | 
						mounter mount.Interface) (VolumeManager, error) {
 | 
				
			||||||
	vm := &volumeManager{
 | 
						vm := &volumeManager{
 | 
				
			||||||
		kubeClient:          kubeClient,
 | 
							kubeClient:          kubeClient,
 | 
				
			||||||
		volumePluginMgr:     volumePluginMgr,
 | 
							volumePluginMgr:     volumePluginMgr,
 | 
				
			||||||
@@ -161,7 +163,8 @@ func NewVolumeManager(
 | 
				
			|||||||
		hostName,
 | 
							hostName,
 | 
				
			||||||
		vm.desiredStateOfWorld,
 | 
							vm.desiredStateOfWorld,
 | 
				
			||||||
		vm.actualStateOfWorld,
 | 
							vm.actualStateOfWorld,
 | 
				
			||||||
		vm.operationExecutor)
 | 
							vm.operationExecutor,
 | 
				
			||||||
 | 
							mounter)
 | 
				
			||||||
	vm.desiredStateOfWorldPopulator = populator.NewDesiredStateOfWorldPopulator(
 | 
						vm.desiredStateOfWorldPopulator = populator.NewDesiredStateOfWorldPopulator(
 | 
				
			||||||
		kubeClient,
 | 
							kubeClient,
 | 
				
			||||||
		desiredStateOfWorldPopulatorLoopSleepPeriod,
 | 
							desiredStateOfWorldPopulatorLoopSleepPeriod,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -81,7 +81,7 @@ func (f *FakeMounter) Mount(source string, target string, fstype string, options
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	f.MountPoints = append(f.MountPoints, MountPoint{Device: source, Path: target, Type: fstype})
 | 
						f.MountPoints = append(f.MountPoints, MountPoint{Device: source, Path: target, Type: fstype})
 | 
				
			||||||
	glog.V(5).Infof("Fake mounter: mouted %s to %s", source, target)
 | 
						glog.V(5).Infof("Fake mounter: mounted %s to %s", source, target)
 | 
				
			||||||
	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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -93,7 +93,7 @@ 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 {
 | 
				
			||||||
			glog.V(5).Infof("Fake mounter: unmouted %s from %s", mp.Device, target)
 | 
								glog.V(5).Infof("Fake mounter: unmounted %s from %s", mp.Device, target)
 | 
				
			||||||
			// Don't copy it to newMountpoints
 | 
								// Don't copy it to newMountpoints
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -117,10 +117,26 @@ func (f *FakeMounter) IsLikelyNotMountPoint(file string) (bool, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	for _, mp := range f.MountPoints {
 | 
						for _, mp := range f.MountPoints {
 | 
				
			||||||
		if mp.Path == file {
 | 
							if mp.Path == file {
 | 
				
			||||||
			glog.V(5).Infof("isLikelyMountPoint for %s: monted %s, false", file, mp.Path)
 | 
								glog.V(5).Infof("isLikelyMountPoint for %s: mounted %s, false", file, mp.Path)
 | 
				
			||||||
			return false, nil
 | 
								return false, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	glog.V(5).Infof("isLikelyMountPoint for %s: true", file)
 | 
						glog.V(5).Infof("isLikelyMountPoint for %s: true", file)
 | 
				
			||||||
	return true, nil
 | 
						return true, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *FakeMounter) DeviceOpened(pathname string) (bool, error) {
 | 
				
			||||||
 | 
						f.mutex.Lock()
 | 
				
			||||||
 | 
						defer f.mutex.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, mp := range f.MountPoints {
 | 
				
			||||||
 | 
							if mp.Device == pathname {
 | 
				
			||||||
 | 
								return true, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *FakeMounter) PathIsDevice(pathname string) (bool, error) {
 | 
				
			||||||
 | 
						return true, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,9 +19,10 @@ limitations under the License.
 | 
				
			|||||||
package mount
 | 
					package mount
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/golang/glog"
 | 
						"github.com/golang/glog"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/util/exec"
 | 
						"k8s.io/kubernetes/pkg/util/exec"
 | 
				
			||||||
	"path/filepath"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Interface interface {
 | 
					type Interface interface {
 | 
				
			||||||
@@ -37,6 +38,11 @@ type Interface interface {
 | 
				
			|||||||
	// IsLikelyNotMountPoint determines if a directory is a mountpoint.
 | 
						// IsLikelyNotMountPoint determines if a directory is a mountpoint.
 | 
				
			||||||
	// It should return ErrNotExist when the directory does not exist.
 | 
						// It should return ErrNotExist when the directory does not exist.
 | 
				
			||||||
	IsLikelyNotMountPoint(file string) (bool, error)
 | 
						IsLikelyNotMountPoint(file string) (bool, error)
 | 
				
			||||||
 | 
						// DeviceOpened determines if the device is in use elsewhere
 | 
				
			||||||
 | 
						// on the system, i.e. still mounted.
 | 
				
			||||||
 | 
						DeviceOpened(pathname string) (bool, error)
 | 
				
			||||||
 | 
						// PathIsDevice determines if a path is a device.
 | 
				
			||||||
 | 
						PathIsDevice(pathname string) (bool, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// This represents a single line in /proc/mounts or /etc/fstab.
 | 
					// This represents a single line in /proc/mounts or /etc/fstab.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -171,6 +171,56 @@ func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
 | 
				
			|||||||
	return true, nil
 | 
						return true, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeviceOpened checks if block device in use by calling Open with O_EXCL flag.
 | 
				
			||||||
 | 
					// Returns true if open returns errno EBUSY, and false if errno is nil.
 | 
				
			||||||
 | 
					// Returns an error if errno is any error other than EBUSY.
 | 
				
			||||||
 | 
					// Returns with error if pathname is not a device.
 | 
				
			||||||
 | 
					func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) {
 | 
				
			||||||
 | 
						return exclusiveOpenFailsOnDevice(pathname)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PathIsDevice uses FileInfo returned from os.Stat to check if path refers
 | 
				
			||||||
 | 
					// to a device.
 | 
				
			||||||
 | 
					func (mounter *Mounter) PathIsDevice(pathname string) (bool, error) {
 | 
				
			||||||
 | 
						return pathIsDevice(pathname)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func exclusiveOpenFailsOnDevice(pathname string) (bool, error) {
 | 
				
			||||||
 | 
						if isDevice, err := pathIsDevice(pathname); !isDevice {
 | 
				
			||||||
 | 
							return false, fmt.Errorf(
 | 
				
			||||||
 | 
								"PathIsDevice failed for path %q: %v",
 | 
				
			||||||
 | 
								pathname,
 | 
				
			||||||
 | 
								err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						fd, errno := syscall.Open(pathname, syscall.O_RDONLY|syscall.O_EXCL, 0)
 | 
				
			||||||
 | 
						// If the device is in use, open will return an invalid fd.
 | 
				
			||||||
 | 
						// When this happens, it is expected that Close will fail and throw an error.
 | 
				
			||||||
 | 
						defer syscall.Close(fd)
 | 
				
			||||||
 | 
						if errno == nil {
 | 
				
			||||||
 | 
							// device not in use
 | 
				
			||||||
 | 
							return false, nil
 | 
				
			||||||
 | 
						} else if errno == syscall.EBUSY {
 | 
				
			||||||
 | 
							// device is in use
 | 
				
			||||||
 | 
							return true, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// error during call to Open
 | 
				
			||||||
 | 
						return false, errno
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func pathIsDevice(pathname string) (bool, error) {
 | 
				
			||||||
 | 
						finfo, err := os.Stat(pathname)
 | 
				
			||||||
 | 
						// err in call to os.Stat
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// path refers to a device
 | 
				
			||||||
 | 
						if finfo.Mode()&os.ModeDevice != 0 {
 | 
				
			||||||
 | 
							return true, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// path does not refer to device
 | 
				
			||||||
 | 
						return false, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func listProcMounts(mountFilePath string) ([]MountPoint, error) {
 | 
					func listProcMounts(mountFilePath string) ([]MountPoint, error) {
 | 
				
			||||||
	hash1, err := readProcMounts(mountFilePath, nil)
 | 
						hash1, err := readProcMounts(mountFilePath, nil)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,6 +36,14 @@ func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
 | 
				
			|||||||
	return true, nil
 | 
						return true, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) {
 | 
				
			||||||
 | 
						return false, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (mounter *Mounter) PathIsDevice(pathname string) (bool, error) {
 | 
				
			||||||
 | 
						return true, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
 | 
					func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -203,6 +203,20 @@ func (n *NsenterMounter) IsLikelyNotMountPoint(file string) (bool, error) {
 | 
				
			|||||||
	return true, nil
 | 
						return true, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeviceOpened checks if block device in use by calling Open with O_EXCL flag.
 | 
				
			||||||
 | 
					// Returns true if open returns errno EBUSY, and false if errno is nil.
 | 
				
			||||||
 | 
					// Returns an error if errno is any error other than EBUSY.
 | 
				
			||||||
 | 
					// Returns with error if pathname is not a device.
 | 
				
			||||||
 | 
					func (n *NsenterMounter) DeviceOpened(pathname string) (bool, error) {
 | 
				
			||||||
 | 
						return exclusiveOpenFailsOnDevice(pathname)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PathIsDevice uses FileInfo returned from os.Stat to check if path refers
 | 
				
			||||||
 | 
					// to a device.
 | 
				
			||||||
 | 
					func (n *NsenterMounter) PathIsDevice(pathname string) (bool, error) {
 | 
				
			||||||
 | 
						return pathIsDevice(pathname)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (n *NsenterMounter) absHostPath(command string) string {
 | 
					func (n *NsenterMounter) absHostPath(command string) string {
 | 
				
			||||||
	path, ok := n.paths[command]
 | 
						path, ok := n.paths[command]
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,3 +41,11 @@ func (*NsenterMounter) List() ([]MountPoint, error) {
 | 
				
			|||||||
func (*NsenterMounter) IsLikelyNotMountPoint(file string) (bool, error) {
 | 
					func (*NsenterMounter) IsLikelyNotMountPoint(file string) (bool, error) {
 | 
				
			||||||
	return true, nil
 | 
						return true, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (*NsenterMounter) DeviceOpened(pathname string) (bool, error) {
 | 
				
			||||||
 | 
						return false, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (*NsenterMounter) PathIsDevice(pathname string) (bool, error) {
 | 
				
			||||||
 | 
						return true, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -116,6 +116,12 @@ func verifyDevicePath(devicePaths []string) (string, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Unmount the global mount path, which should be the only one, and delete it.
 | 
					// Unmount the global mount path, which should be the only one, and delete it.
 | 
				
			||||||
func unmountPDAndRemoveGlobalPath(globalMountPath string, mounter mount.Interface) error {
 | 
					func unmountPDAndRemoveGlobalPath(globalMountPath string, mounter mount.Interface) error {
 | 
				
			||||||
 | 
						if pathExists, pathErr := pathExists(globalMountPath); pathErr != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("Error checking if path exists: %v", pathErr)
 | 
				
			||||||
 | 
						} else if !pathExists {
 | 
				
			||||||
 | 
							glog.V(5).Infof("Warning: Unmount skipped because path does not exist: %v", globalMountPath)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	err := mounter.Unmount(globalMountPath)
 | 
						err := mounter.Unmount(globalMountPath)
 | 
				
			||||||
	os.Remove(globalMountPath)
 | 
						os.Remove(globalMountPath)
 | 
				
			||||||
	return err
 | 
						return err
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -279,6 +279,12 @@ func pathExists(path string) (bool, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Unmount the global mount path, which should be the only one, and delete it.
 | 
					// Unmount the global mount path, which should be the only one, and delete it.
 | 
				
			||||||
func unmountPDAndRemoveGlobalPath(globalMountPath string, mounter mount.Interface) error {
 | 
					func unmountPDAndRemoveGlobalPath(globalMountPath string, mounter mount.Interface) error {
 | 
				
			||||||
 | 
						if pathExists, pathErr := pathExists(globalMountPath); pathErr != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("Error checking if path exists: %v", pathErr)
 | 
				
			||||||
 | 
						} else if !pathExists {
 | 
				
			||||||
 | 
							glog.V(5).Infof("Warning: Unmount skipped because path does not exist: %v", globalMountPath)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	err := mounter.Unmount(globalMountPath)
 | 
						err := mounter.Unmount(globalMountPath)
 | 
				
			||||||
	os.Remove(globalMountPath)
 | 
						os.Remove(globalMountPath)
 | 
				
			||||||
	return err
 | 
						return err
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -125,7 +125,14 @@ func verifyDevicePath(devicePaths []string, sdBeforeSet sets.String) (string, er
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Unmount the global PD mount, which should be the only one, and delete it.
 | 
					// Unmount the global PD mount, which should be the only one, and delete it.
 | 
				
			||||||
 | 
					// Does nothing if globalMountPath does not exist.
 | 
				
			||||||
func unmountPDAndRemoveGlobalPath(globalMountPath string, mounter mount.Interface) error {
 | 
					func unmountPDAndRemoveGlobalPath(globalMountPath string, mounter mount.Interface) error {
 | 
				
			||||||
 | 
						if pathExists, pathErr := pathExists(globalMountPath); pathErr != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("Error checking if path exists: %v", pathErr)
 | 
				
			||||||
 | 
						} else if !pathExists {
 | 
				
			||||||
 | 
							glog.V(5).Infof("Warning: Unmount skipped because path does not exist: %v", globalMountPath)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	err := mounter.Unmount(globalMountPath)
 | 
						err := mounter.Unmount(globalMountPath)
 | 
				
			||||||
	os.Remove(globalMountPath)
 | 
						os.Remove(globalMountPath)
 | 
				
			||||||
	return err
 | 
						return err
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,6 +28,7 @@ import (
 | 
				
			|||||||
	"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
 | 
						"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/types"
 | 
						"k8s.io/kubernetes/pkg/types"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/util/goroutinemap"
 | 
						"k8s.io/kubernetes/pkg/util/goroutinemap"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/util/mount"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/volume"
 | 
						"k8s.io/kubernetes/pkg/volume"
 | 
				
			||||||
	volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
 | 
						volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -83,7 +84,7 @@ type OperationExecutor interface {
 | 
				
			|||||||
	// UnmountDevice unmounts the volumes global mount path from the device (for
 | 
						// UnmountDevice unmounts the volumes global mount path from the device (for
 | 
				
			||||||
	// attachable volumes only, freeing it for detach. It then updates the
 | 
						// attachable volumes only, freeing it for detach. It then updates the
 | 
				
			||||||
	// actual state of the world to reflect that.
 | 
						// actual state of the world to reflect that.
 | 
				
			||||||
	UnmountDevice(deviceToDetach AttachedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater) error
 | 
						UnmountDevice(deviceToDetach AttachedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, mounter mount.Interface) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// VerifyControllerAttachedVolume checks if the specified volume is present
 | 
						// VerifyControllerAttachedVolume checks if the specified volume is present
 | 
				
			||||||
	// in the specified nodes AttachedVolumes Status field. It uses kubeClient
 | 
						// in the specified nodes AttachedVolumes Status field. It uses kubeClient
 | 
				
			||||||
@@ -206,6 +207,10 @@ type AttachedVolume struct {
 | 
				
			|||||||
	// PluginIsAttachable indicates that the plugin for this volume implements
 | 
						// PluginIsAttachable indicates that the plugin for this volume implements
 | 
				
			||||||
	// the volume.Attacher interface
 | 
						// the volume.Attacher interface
 | 
				
			||||||
	PluginIsAttachable bool
 | 
						PluginIsAttachable bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// DevicePath contains the path on the node where the volume is attached.
 | 
				
			||||||
 | 
						// For non-attachable volumes this is empty.
 | 
				
			||||||
 | 
						DevicePath string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MountedVolume represents a volume that has successfully been mounted to a pod.
 | 
					// MountedVolume represents a volume that has successfully been mounted to a pod.
 | 
				
			||||||
@@ -382,9 +387,10 @@ func (oe *operationExecutor) UnmountVolume(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (oe *operationExecutor) UnmountDevice(
 | 
					func (oe *operationExecutor) UnmountDevice(
 | 
				
			||||||
	deviceToDetach AttachedVolume,
 | 
						deviceToDetach AttachedVolume,
 | 
				
			||||||
	actualStateOfWorld ActualStateOfWorldMounterUpdater) error {
 | 
						actualStateOfWorld ActualStateOfWorldMounterUpdater,
 | 
				
			||||||
 | 
						mounter mount.Interface) error {
 | 
				
			||||||
	unmountDeviceFunc, err :=
 | 
						unmountDeviceFunc, err :=
 | 
				
			||||||
		oe.generateUnmountDeviceFunc(deviceToDetach, actualStateOfWorld)
 | 
							oe.generateUnmountDeviceFunc(deviceToDetach, actualStateOfWorld, mounter)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -811,7 +817,8 @@ func (oe *operationExecutor) generateUnmountVolumeFunc(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (oe *operationExecutor) generateUnmountDeviceFunc(
 | 
					func (oe *operationExecutor) generateUnmountDeviceFunc(
 | 
				
			||||||
	deviceToDetach AttachedVolume,
 | 
						deviceToDetach AttachedVolume,
 | 
				
			||||||
	actualStateOfWorld ActualStateOfWorldMounterUpdater) (func() error, error) {
 | 
						actualStateOfWorld ActualStateOfWorldMounterUpdater,
 | 
				
			||||||
 | 
						mounter mount.Interface) (func() error, error) {
 | 
				
			||||||
	// Get attacher plugin
 | 
						// Get attacher plugin
 | 
				
			||||||
	attachableVolumePlugin, err :=
 | 
						attachableVolumePlugin, err :=
 | 
				
			||||||
		oe.volumePluginMgr.FindAttachablePluginBySpec(deviceToDetach.VolumeSpec)
 | 
							oe.volumePluginMgr.FindAttachablePluginBySpec(deviceToDetach.VolumeSpec)
 | 
				
			||||||
@@ -863,6 +870,24 @@ func (oe *operationExecutor) generateUnmountDeviceFunc(
 | 
				
			|||||||
				deviceToDetach.VolumeSpec.Name(),
 | 
									deviceToDetach.VolumeSpec.Name(),
 | 
				
			||||||
				unmountDeviceErr)
 | 
									unmountDeviceErr)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							// Before logging that UnmountDevice succeeded and moving on,
 | 
				
			||||||
 | 
							// use mounter.DeviceOpened to check if the device is in use anywhere
 | 
				
			||||||
 | 
							// else on the system. Retry if it returns true.
 | 
				
			||||||
 | 
							deviceOpened, deviceOpenedErr := mounter.DeviceOpened(deviceToDetach.DevicePath)
 | 
				
			||||||
 | 
							if deviceOpenedErr != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf(
 | 
				
			||||||
 | 
									"UnmountDevice.DeviceOpened failed for volume %q (spec.Name: %q) with: %v",
 | 
				
			||||||
 | 
									deviceToDetach.VolumeName,
 | 
				
			||||||
 | 
									deviceToDetach.VolumeSpec.Name(),
 | 
				
			||||||
 | 
									deviceOpenedErr)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// The device is still in use elsewhere. Caller will log and retry.
 | 
				
			||||||
 | 
							if deviceOpened {
 | 
				
			||||||
 | 
								return fmt.Errorf(
 | 
				
			||||||
 | 
									"UnmountDevice failed for volume %q (spec.Name: %q) because the device is in use when it was no longer expected to be in use",
 | 
				
			||||||
 | 
									deviceToDetach.VolumeName,
 | 
				
			||||||
 | 
									deviceToDetach.VolumeSpec.Name())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		glog.Infof(
 | 
							glog.Infof(
 | 
				
			||||||
			"UnmountDevice succeeded for volume %q (spec.Name: %q).",
 | 
								"UnmountDevice succeeded for volume %q (spec.Name: %q).",
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user