func (oe *operationExecutor) generateUnmountDeviceFunc( deviceToDetach AttachedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, mounter mount.Interface) (func() error, error) { // Get attacher plugin attachableVolumePlugin, err := oe.volumePluginMgr.FindAttachablePluginBySpec(deviceToDetach.VolumeSpec) if err != nil || attachableVolumePlugin == nil { return nil, fmt.Errorf( "UnmountDevice.FindAttachablePluginBySpec failed for volume %q (spec.Name: %q) with: %v", deviceToDetach.VolumeName, deviceToDetach.VolumeSpec.Name(), err) } volumeDetacher, err := attachableVolumePlugin.NewDetacher() if err != nil { return nil, fmt.Errorf( "UnmountDevice.NewDetacher failed for volume %q (spec.Name: %q) with: %v", deviceToDetach.VolumeName, deviceToDetach.VolumeSpec.Name(), err) } volumeAttacher, err := attachableVolumePlugin.NewAttacher() if err != nil { return nil, fmt.Errorf( "UnmountDevice.NewAttacher failed for volume %q (spec.Name: %q) with: %v", deviceToDetach.VolumeName, deviceToDetach.VolumeSpec.Name(), err) } return func() error { deviceMountPath, err := volumeAttacher.GetDeviceMountPath(deviceToDetach.VolumeSpec) if err != nil { // On failure, return error. Caller will log and retry. return fmt.Errorf( "GetDeviceMountPath failed for volume %q (spec.Name: %q) with: %v", deviceToDetach.VolumeName, deviceToDetach.VolumeSpec.Name(), err) } refs, err := attachableVolumePlugin.GetDeviceMountRefs(deviceMountPath) if err != nil || len(refs) > 0 { if err == nil { err = fmt.Errorf("The device mount path %q is still mounted by other references %v", deviceMountPath, refs) } return fmt.Errorf( "GetDeviceMountRefs check failed for volume %q (spec.Name: %q) with: %v", deviceToDetach.VolumeName, deviceToDetach.VolumeSpec.Name(), err) } // Execute unmount unmountDeviceErr := volumeDetacher.UnmountDevice(deviceMountPath) if unmountDeviceErr != nil { // On failure, return error. Caller will log and retry. return fmt.Errorf( "UnmountDevice failed for volume %q (spec.Name: %q) with: %v", deviceToDetach.VolumeName, deviceToDetach.VolumeSpec.Name(), unmountDeviceErr) } // Before logging that UnmountDevice succeeded and moving on, // use mounter.PathIsDevice to check if the path is a device, // if so use mounter.DeviceOpened to check if the device is in use anywhere // else on the system. Retry if it returns true. isDevicePath, devicePathErr := mounter.PathIsDevice(deviceToDetach.DevicePath) var deviceOpened bool var deviceOpenedErr error if !isDevicePath && devicePathErr == nil { // not a device path or path doesn't exist //TODO: refer to #36092 glog.V(3).Infof("Not checking device path %s", deviceToDetach.DevicePath) deviceOpened = false } else { 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( "UnmountDevice succeeded for volume %q (spec.Name: %q).", deviceToDetach.VolumeName, deviceToDetach.VolumeSpec.Name()) // Update actual state of world markDeviceUnmountedErr := actualStateOfWorld.MarkDeviceAsUnmounted( deviceToDetach.VolumeName) if markDeviceUnmountedErr != nil { // On failure, return error. Caller will log and retry. return fmt.Errorf( "MarkDeviceAsUnmounted failed for device %q (spec.Name: %q) with: %v", deviceToDetach.VolumeName, deviceToDetach.VolumeSpec.Name(), markDeviceUnmountedErr) } return nil }, nil }
func (oe *operationExecutor) generateUnmountDeviceFunc( deviceToDetach AttachedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, mounter mount.Interface) (func() error, error) { // Get attacher plugin attachableVolumePlugin, err := oe.volumePluginMgr.FindAttachablePluginBySpec(deviceToDetach.VolumeSpec) if err != nil || attachableVolumePlugin == nil { return nil, fmt.Errorf( "UnmountDevice.FindAttachablePluginBySpec failed for volume %q (spec.Name: %q) with: %v", deviceToDetach.VolumeName, deviceToDetach.VolumeSpec.Name(), err) } volumeDetacher, err := attachableVolumePlugin.NewDetacher() if err != nil { return nil, fmt.Errorf( "UnmountDevice.NewDetacher failed for volume %q (spec.Name: %q) with: %v", deviceToDetach.VolumeName, deviceToDetach.VolumeSpec.Name(), err) } volumeAttacher, err := attachableVolumePlugin.NewAttacher() if err != nil { return nil, fmt.Errorf( "UnmountDevice.NewAttacher failed for volume %q (spec.Name: %q) with: %v", deviceToDetach.VolumeName, deviceToDetach.VolumeSpec.Name(), err) } return func() error { deviceMountPath, err := volumeAttacher.GetDeviceMountPath(deviceToDetach.VolumeSpec) if err != nil { // On failure, return error. Caller will log and retry. return fmt.Errorf( "GetDeviceMountPath failed for volume %q (spec.Name: %q) with: %v", deviceToDetach.VolumeName, deviceToDetach.VolumeSpec.Name(), err) } // Execute unmount unmountDeviceErr := volumeDetacher.UnmountDevice(deviceMountPath) if unmountDeviceErr != nil { // On failure, return error. Caller will log and retry. return fmt.Errorf( "UnmountDevice failed for volume %q (spec.Name: %q) with: %v", deviceToDetach.VolumeName, deviceToDetach.VolumeSpec.Name(), 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( "UnmountDevice succeeded for volume %q (spec.Name: %q).", deviceToDetach.VolumeName, deviceToDetach.VolumeSpec.Name()) // Update actual state of world markDeviceUnmountedErr := actualStateOfWorld.MarkDeviceAsUnmounted( deviceToDetach.VolumeName) if markDeviceUnmountedErr != nil { // On failure, return error. Caller will log and retry. return fmt.Errorf( "MarkDeviceAsUnmounted failed for device %q (spec.Name: %q) with: %v", deviceToDetach.VolumeName, deviceToDetach.VolumeSpec.Name(), markDeviceUnmountedErr) } return nil }, nil }