operation_executor.go 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. // Package operationexecutor implements interfaces that enable execution of
  14. // attach, detach, mount, and unmount operations with a
  15. // nestedpendingoperations so that more than one operation is never triggered
  16. // on the same volume for the same pod.
  17. package operationexecutor
  18. import (
  19. "fmt"
  20. "time"
  21. "k8s.io/klog"
  22. "k8s.io/utils/mount"
  23. v1 "k8s.io/api/core/v1"
  24. "k8s.io/apimachinery/pkg/api/resource"
  25. "k8s.io/apimachinery/pkg/types"
  26. "k8s.io/kubernetes/pkg/volume"
  27. "k8s.io/kubernetes/pkg/volume/util"
  28. "k8s.io/kubernetes/pkg/volume/util/hostutil"
  29. "k8s.io/kubernetes/pkg/volume/util/nestedpendingoperations"
  30. volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
  31. "k8s.io/kubernetes/pkg/volume/util/volumepathhandler"
  32. )
  33. // OperationExecutor defines a set of operations for attaching, detaching,
  34. // mounting, or unmounting a volume that are executed with a NewNestedPendingOperations which
  35. // prevents more than one operation from being triggered on the same volume.
  36. //
  37. // These operations should be idempotent (for example, AttachVolume should
  38. // still succeed if the volume is already attached to the node, etc.). However,
  39. // they depend on the volume plugins to implement this behavior.
  40. //
  41. // Once an operation completes successfully, the actualStateOfWorld is updated
  42. // to indicate the volume is attached/detached/mounted/unmounted.
  43. //
  44. // If the OperationExecutor fails to start the operation because, for example,
  45. // an operation with the same UniqueVolumeName is already pending, a non-nil
  46. // error is returned.
  47. //
  48. // Once the operation is started, since it is executed asynchronously,
  49. // errors are simply logged and the goroutine is terminated without updating
  50. // actualStateOfWorld (callers are responsible for retrying as needed).
  51. //
  52. // Some of these operations may result in calls to the API server; callers are
  53. // responsible for rate limiting on errors.
  54. type OperationExecutor interface {
  55. // AttachVolume attaches the volume to the node specified in volumeToAttach.
  56. // It then updates the actual state of the world to reflect that.
  57. AttachVolume(volumeToAttach VolumeToAttach, actualStateOfWorld ActualStateOfWorldAttacherUpdater) error
  58. // VerifyVolumesAreAttachedPerNode verifies the given list of volumes to see whether they are still attached to the node.
  59. // If any volume is not attached right now, it will update the actual state of the world to reflect that.
  60. // Note that this operation could be operated concurrently with other attach/detach operations.
  61. // In theory (but very unlikely in practise), race condition among these operations might mark volume as detached
  62. // even if it is attached. But reconciler can correct this in a short period of time.
  63. VerifyVolumesAreAttachedPerNode(AttachedVolumes []AttachedVolume, nodeName types.NodeName, actualStateOfWorld ActualStateOfWorldAttacherUpdater) error
  64. // VerifyVolumesAreAttached verifies volumes being used in entire cluster and if they are still attached to the node
  65. // If any volume is not attached right now, it will update actual state of world to reflect that.
  66. VerifyVolumesAreAttached(volumesToVerify map[types.NodeName][]AttachedVolume, actualStateOfWorld ActualStateOfWorldAttacherUpdater)
  67. // DetachVolume detaches the volume from the node specified in
  68. // volumeToDetach, and updates the actual state of the world to reflect
  69. // that. If verifySafeToDetach is set, a call is made to the fetch the node
  70. // object and it is used to verify that the volume does not exist in Node's
  71. // Status.VolumesInUse list (operation fails with error if it is).
  72. DetachVolume(volumeToDetach AttachedVolume, verifySafeToDetach bool, actualStateOfWorld ActualStateOfWorldAttacherUpdater) error
  73. // If a volume has 'Filesystem' volumeMode, MountVolume mounts the
  74. // volume to the pod specified in volumeToMount.
  75. // Specifically it will:
  76. // * Wait for the device to finish attaching (for attachable volumes only).
  77. // * Mount device to global mount path (for attachable volumes only).
  78. // * Update actual state of world to reflect volume is globally mounted (for
  79. // attachable volumes only).
  80. // * Mount the volume to the pod specific path.
  81. // * Update actual state of world to reflect volume is mounted to the pod
  82. // path.
  83. // The parameter "isRemount" is informational and used to adjust logging
  84. // verbosity. An initial mount is more log-worthy than a remount, for
  85. // example.
  86. //
  87. // For 'Block' volumeMode, this method creates a symbolic link to
  88. // the volume from both the pod specified in volumeToMount and global map path.
  89. // Specifically it will:
  90. // * Wait for the device to finish attaching (for attachable volumes only).
  91. // * Update actual state of world to reflect volume is globally mounted/mapped.
  92. // * Map volume to global map path using symbolic link.
  93. // * Map the volume to the pod device map path using symbolic link.
  94. // * Update actual state of world to reflect volume is mounted/mapped to the pod path.
  95. MountVolume(waitForAttachTimeout time.Duration, volumeToMount VolumeToMount, actualStateOfWorld ActualStateOfWorldMounterUpdater, isRemount bool) error
  96. // If a volume has 'Filesystem' volumeMode, UnmountVolume unmounts the
  97. // volume from the pod specified in volumeToUnmount and updates the actual
  98. // state of the world to reflect that.
  99. //
  100. // For 'Block' volumeMode, this method unmaps symbolic link to the volume
  101. // from both the pod device map path in volumeToUnmount and global map path.
  102. // And then, updates the actual state of the world to reflect that.
  103. UnmountVolume(volumeToUnmount MountedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, podsDir string) error
  104. // If a volume has 'Filesystem' volumeMode, UnmountDevice unmounts the
  105. // volumes global mount path from the device (for attachable volumes only,
  106. // freeing it for detach. It then updates the actual state of the world to
  107. // reflect that.
  108. //
  109. // For 'Block' volumeMode, this method checks number of symbolic links under
  110. // global map path. If number of reference is zero, remove global map path
  111. // directory and free a volume for detach.
  112. // It then updates the actual state of the world to reflect that.
  113. UnmountDevice(deviceToDetach AttachedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, hostutil hostutil.HostUtils) error
  114. // VerifyControllerAttachedVolume checks if the specified volume is present
  115. // in the specified nodes AttachedVolumes Status field. It uses kubeClient
  116. // to fetch the node object.
  117. // If the volume is found, the actual state of the world is updated to mark
  118. // the volume as attached.
  119. // If the volume does not implement the attacher interface, it is assumed to
  120. // be attached and the actual state of the world is updated accordingly.
  121. // If the volume is not found or there is an error (fetching the node
  122. // object, for example) then an error is returned which triggers exponential
  123. // back off on retries.
  124. VerifyControllerAttachedVolume(volumeToMount VolumeToMount, nodeName types.NodeName, actualStateOfWorld ActualStateOfWorldAttacherUpdater) error
  125. // IsOperationPending returns true if an operation for the given volumeName and podName is pending,
  126. // otherwise it returns false
  127. IsOperationPending(volumeName v1.UniqueVolumeName, podName volumetypes.UniquePodName) bool
  128. // ExpandInUseVolume will resize volume's file system to expected size without unmounting the volume.
  129. ExpandInUseVolume(volumeToMount VolumeToMount, actualStateOfWorld ActualStateOfWorldMounterUpdater) error
  130. // ReconstructVolumeOperation construct a new volumeSpec and returns it created by plugin
  131. ReconstructVolumeOperation(volumeMode v1.PersistentVolumeMode, plugin volume.VolumePlugin, mapperPlugin volume.BlockVolumePlugin, uid types.UID, podName volumetypes.UniquePodName, volumeSpecName string, volumePath string, pluginName string) (*volume.Spec, error)
  132. // CheckVolumeExistenceOperation checks volume existence
  133. CheckVolumeExistenceOperation(volumeSpec *volume.Spec, mountPath, volumeName string, mounter mount.Interface, uniqueVolumeName v1.UniqueVolumeName, podName volumetypes.UniquePodName, podUID types.UID, attachable volume.AttachableVolumePlugin) (bool, error)
  134. }
  135. // NewOperationExecutor returns a new instance of OperationExecutor.
  136. func NewOperationExecutor(
  137. operationGenerator OperationGenerator) OperationExecutor {
  138. return &operationExecutor{
  139. pendingOperations: nestedpendingoperations.NewNestedPendingOperations(
  140. true /* exponentialBackOffOnError */),
  141. operationGenerator: operationGenerator,
  142. }
  143. }
  144. // MarkVolumeOpts is an struct to pass arguments to MountVolume functions
  145. type MarkVolumeOpts struct {
  146. PodName volumetypes.UniquePodName
  147. PodUID types.UID
  148. VolumeName v1.UniqueVolumeName
  149. Mounter volume.Mounter
  150. BlockVolumeMapper volume.BlockVolumeMapper
  151. OuterVolumeSpecName string
  152. VolumeGidVolume string
  153. VolumeSpec *volume.Spec
  154. VolumeMountState VolumeMountState
  155. }
  156. // ActualStateOfWorldMounterUpdater defines a set of operations updating the actual
  157. // state of the world cache after successful mount/unmount.
  158. type ActualStateOfWorldMounterUpdater interface {
  159. // Marks the specified volume as mounted to the specified pod
  160. MarkVolumeAsMounted(markVolumeOpts MarkVolumeOpts) error
  161. // Marks the specified volume as unmounted from the specified pod
  162. MarkVolumeAsUnmounted(podName volumetypes.UniquePodName, volumeName v1.UniqueVolumeName) error
  163. // MarkVolumeMountAsUncertain marks state of volume mount for the pod uncertain
  164. MarkVolumeMountAsUncertain(markVolumeOpts MarkVolumeOpts) error
  165. // Marks the specified volume as having been globally mounted.
  166. MarkDeviceAsMounted(volumeName v1.UniqueVolumeName, devicePath, deviceMountPath string) error
  167. // MarkDeviceAsUncertain marks device state in global mount path as uncertain
  168. MarkDeviceAsUncertain(volumeName v1.UniqueVolumeName, devicePath, deviceMountPath string) error
  169. // Marks the specified volume as having its global mount unmounted.
  170. MarkDeviceAsUnmounted(volumeName v1.UniqueVolumeName) error
  171. // Marks the specified volume's file system resize request is finished.
  172. MarkVolumeAsResized(podName volumetypes.UniquePodName, volumeName v1.UniqueVolumeName) error
  173. // GetDeviceMountState returns mount state of the device in global path
  174. GetDeviceMountState(volumeName v1.UniqueVolumeName) DeviceMountState
  175. // GetVolumeMountState returns mount state of the volume for the Pod
  176. GetVolumeMountState(volumName v1.UniqueVolumeName, podName volumetypes.UniquePodName) VolumeMountState
  177. }
  178. // ActualStateOfWorldAttacherUpdater defines a set of operations updating the
  179. // actual state of the world cache after successful attach/detach/mount/unmount.
  180. type ActualStateOfWorldAttacherUpdater interface {
  181. // Marks the specified volume as attached to the specified node. If the
  182. // volume name is supplied, that volume name will be used. If not, the
  183. // volume name is computed using the result from querying the plugin.
  184. //
  185. // TODO: in the future, we should be able to remove the volumeName
  186. // argument to this method -- since it is used only for attachable
  187. // volumes. See issue 29695.
  188. MarkVolumeAsAttached(volumeName v1.UniqueVolumeName, volumeSpec *volume.Spec, nodeName types.NodeName, devicePath string) error
  189. // Marks the specified volume as *possibly* attached to the specified node.
  190. // If an attach operation fails, the attach/detach controller does not know for certain if the volume is attached or not.
  191. // If the volume name is supplied, that volume name will be used. If not, the
  192. // volume name is computed using the result from querying the plugin.
  193. MarkVolumeAsUncertain(volumeName v1.UniqueVolumeName, volumeSpec *volume.Spec, nodeName types.NodeName) error
  194. // Marks the specified volume as detached from the specified node
  195. MarkVolumeAsDetached(volumeName v1.UniqueVolumeName, nodeName types.NodeName)
  196. // Marks desire to detach the specified volume (remove the volume from the node's
  197. // volumesToReportAsAttached list)
  198. RemoveVolumeFromReportAsAttached(volumeName v1.UniqueVolumeName, nodeName types.NodeName) error
  199. // Unmarks the desire to detach for the specified volume (add the volume back to
  200. // the node's volumesToReportAsAttached list)
  201. AddVolumeToReportAsAttached(volumeName v1.UniqueVolumeName, nodeName types.NodeName)
  202. }
  203. // VolumeLogger defines a set of operations for generating volume-related logging and error msgs
  204. type VolumeLogger interface {
  205. // Creates a detailed msg that can be used in logs
  206. // The msg format follows the pattern "<prefixMsg> <volume details> <suffixMsg>",
  207. // where each implementation provides the volume details
  208. GenerateMsgDetailed(prefixMsg, suffixMsg string) (detailedMsg string)
  209. // Creates a detailed error that can be used in logs.
  210. // The msg format follows the pattern "<prefixMsg> <volume details>: <err> ",
  211. GenerateErrorDetailed(prefixMsg string, err error) (detailedErr error)
  212. // Creates a simple msg that is user friendly and a detailed msg that can be used in logs
  213. // The msg format follows the pattern "<prefixMsg> <volume details> <suffixMsg>",
  214. // where each implementation provides the volume details
  215. GenerateMsg(prefixMsg, suffixMsg string) (simpleMsg, detailedMsg string)
  216. // Creates a simple error that is user friendly and a detailed error that can be used in logs.
  217. // The msg format follows the pattern "<prefixMsg> <volume details>: <err> ",
  218. GenerateError(prefixMsg string, err error) (simpleErr, detailedErr error)
  219. }
  220. // Generates an error string with the format ": <err>" if err exists
  221. func errSuffix(err error) string {
  222. errStr := ""
  223. if err != nil {
  224. errStr = fmt.Sprintf(": %v", err)
  225. }
  226. return errStr
  227. }
  228. // Generate a detailed error msg for logs
  229. func generateVolumeMsgDetailed(prefixMsg, suffixMsg, volumeName, details string) (detailedMsg string) {
  230. return fmt.Sprintf("%v for volume %q %v %v", prefixMsg, volumeName, details, suffixMsg)
  231. }
  232. // Generate a simplified error msg for events and a detailed error msg for logs
  233. func generateVolumeMsg(prefixMsg, suffixMsg, volumeName, details string) (simpleMsg, detailedMsg string) {
  234. simpleMsg = fmt.Sprintf("%v for volume %q %v", prefixMsg, volumeName, suffixMsg)
  235. return simpleMsg, generateVolumeMsgDetailed(prefixMsg, suffixMsg, volumeName, details)
  236. }
  237. // VolumeToAttach represents a volume that should be attached to a node.
  238. type VolumeToAttach struct {
  239. // MultiAttachErrorReported indicates whether the multi-attach error has been reported for the given volume.
  240. // It is used to prevent reporting the error from being reported more than once for a given volume.
  241. MultiAttachErrorReported bool
  242. // VolumeName is the unique identifier for the volume that should be
  243. // attached.
  244. VolumeName v1.UniqueVolumeName
  245. // VolumeSpec is a volume spec containing the specification for the volume
  246. // that should be attached.
  247. VolumeSpec *volume.Spec
  248. // NodeName is the identifier for the node that the volume should be
  249. // attached to.
  250. NodeName types.NodeName
  251. // scheduledPods is a map containing the set of pods that reference this
  252. // volume and are scheduled to the underlying node. The key in the map is
  253. // the name of the pod and the value is a pod object containing more
  254. // information about the pod.
  255. ScheduledPods []*v1.Pod
  256. }
  257. // GenerateMsgDetailed returns detailed msgs for volumes to attach
  258. func (volume *VolumeToAttach) GenerateMsgDetailed(prefixMsg, suffixMsg string) (detailedMsg string) {
  259. detailedStr := fmt.Sprintf("(UniqueName: %q) from node %q", volume.VolumeName, volume.NodeName)
  260. volumeSpecName := "nil"
  261. if volume.VolumeSpec != nil {
  262. volumeSpecName = volume.VolumeSpec.Name()
  263. }
  264. return generateVolumeMsgDetailed(prefixMsg, suffixMsg, volumeSpecName, detailedStr)
  265. }
  266. // GenerateMsg returns simple and detailed msgs for volumes to attach
  267. func (volume *VolumeToAttach) GenerateMsg(prefixMsg, suffixMsg string) (simpleMsg, detailedMsg string) {
  268. detailedStr := fmt.Sprintf("(UniqueName: %q) from node %q", volume.VolumeName, volume.NodeName)
  269. volumeSpecName := "nil"
  270. if volume.VolumeSpec != nil {
  271. volumeSpecName = volume.VolumeSpec.Name()
  272. }
  273. return generateVolumeMsg(prefixMsg, suffixMsg, volumeSpecName, detailedStr)
  274. }
  275. // GenerateErrorDetailed returns detailed errors for volumes to attach
  276. func (volume *VolumeToAttach) GenerateErrorDetailed(prefixMsg string, err error) (detailedErr error) {
  277. return fmt.Errorf(volume.GenerateMsgDetailed(prefixMsg, errSuffix(err)))
  278. }
  279. // GenerateError returns simple and detailed errors for volumes to attach
  280. func (volume *VolumeToAttach) GenerateError(prefixMsg string, err error) (simpleErr, detailedErr error) {
  281. simpleMsg, detailedMsg := volume.GenerateMsg(prefixMsg, errSuffix(err))
  282. return fmt.Errorf(simpleMsg), fmt.Errorf(detailedMsg)
  283. }
  284. // VolumeToMount represents a volume that should be attached to this node and
  285. // mounted to the PodName.
  286. type VolumeToMount struct {
  287. // VolumeName is the unique identifier for the volume that should be
  288. // mounted.
  289. VolumeName v1.UniqueVolumeName
  290. // PodName is the unique identifier for the pod that the volume should be
  291. // mounted to after it is attached.
  292. PodName volumetypes.UniquePodName
  293. // VolumeSpec is a volume spec containing the specification for the volume
  294. // that should be mounted. Used to create NewMounter. Used to generate
  295. // InnerVolumeSpecName.
  296. VolumeSpec *volume.Spec
  297. // outerVolumeSpecName is the podSpec.Volume[x].Name of the volume. If the
  298. // volume was referenced through a persistent volume claim, this contains
  299. // the podSpec.Volume[x].Name of the persistent volume claim.
  300. OuterVolumeSpecName string
  301. // Pod to mount the volume to. Used to create NewMounter.
  302. Pod *v1.Pod
  303. // PluginIsAttachable indicates that the plugin for this volume implements
  304. // the volume.Attacher interface
  305. PluginIsAttachable bool
  306. // PluginIsDeviceMountable indicates that the plugin for this volume implements
  307. // the volume.DeviceMounter interface
  308. PluginIsDeviceMountable bool
  309. // VolumeGidValue contains the value of the GID annotation, if present.
  310. VolumeGidValue string
  311. // DevicePath contains the path on the node where the volume is attached.
  312. // For non-attachable volumes this is empty.
  313. DevicePath string
  314. // ReportedInUse indicates that the volume was successfully added to the
  315. // VolumesInUse field in the node's status.
  316. ReportedInUse bool
  317. // DesiredSizeLimit indicates the desired upper bound on the size of the volume
  318. // (if so implemented)
  319. DesiredSizeLimit *resource.Quantity
  320. }
  321. // DeviceMountState represents device mount state in a global path.
  322. type DeviceMountState string
  323. const (
  324. // DeviceGloballyMounted means device has been globally mounted successfully
  325. DeviceGloballyMounted DeviceMountState = "DeviceGloballyMounted"
  326. // DeviceMountUncertain means device may not be mounted but a mount operation may be
  327. // in-progress which can cause device mount to succeed.
  328. DeviceMountUncertain DeviceMountState = "DeviceMountUncertain"
  329. // DeviceNotMounted means device has not been mounted globally.
  330. DeviceNotMounted DeviceMountState = "DeviceNotMounted"
  331. )
  332. // VolumeMountState represents volume mount state in a path local to the pod.
  333. type VolumeMountState string
  334. const (
  335. // VolumeMounted means volume has been mounted in pod's local path
  336. VolumeMounted VolumeMountState = "VolumeMounted"
  337. // VolumeMountUncertain means volume may or may not be mounted in pods' local path
  338. VolumeMountUncertain VolumeMountState = "VolumeMountUncertain"
  339. // VolumeNotMounted means volume has not be mounted in pod's local path
  340. VolumeNotMounted VolumeMountState = "VolumeNotMounted"
  341. )
  342. // GenerateMsgDetailed returns detailed msgs for volumes to mount
  343. func (volume *VolumeToMount) GenerateMsgDetailed(prefixMsg, suffixMsg string) (detailedMsg string) {
  344. detailedStr := fmt.Sprintf("(UniqueName: %q) pod %q (UID: %q)", volume.VolumeName, volume.Pod.Name, volume.Pod.UID)
  345. volumeSpecName := "nil"
  346. if volume.VolumeSpec != nil {
  347. volumeSpecName = volume.VolumeSpec.Name()
  348. }
  349. return generateVolumeMsgDetailed(prefixMsg, suffixMsg, volumeSpecName, detailedStr)
  350. }
  351. // GenerateMsg returns simple and detailed msgs for volumes to mount
  352. func (volume *VolumeToMount) GenerateMsg(prefixMsg, suffixMsg string) (simpleMsg, detailedMsg string) {
  353. detailedStr := fmt.Sprintf("(UniqueName: %q) pod %q (UID: %q)", volume.VolumeName, volume.Pod.Name, volume.Pod.UID)
  354. volumeSpecName := "nil"
  355. if volume.VolumeSpec != nil {
  356. volumeSpecName = volume.VolumeSpec.Name()
  357. }
  358. return generateVolumeMsg(prefixMsg, suffixMsg, volumeSpecName, detailedStr)
  359. }
  360. // GenerateErrorDetailed returns detailed errors for volumes to mount
  361. func (volume *VolumeToMount) GenerateErrorDetailed(prefixMsg string, err error) (detailedErr error) {
  362. return fmt.Errorf(volume.GenerateMsgDetailed(prefixMsg, errSuffix(err)))
  363. }
  364. // GenerateError returns simple and detailed errors for volumes to mount
  365. func (volume *VolumeToMount) GenerateError(prefixMsg string, err error) (simpleErr, detailedErr error) {
  366. simpleMsg, detailedMsg := volume.GenerateMsg(prefixMsg, errSuffix(err))
  367. return fmt.Errorf(simpleMsg), fmt.Errorf(detailedMsg)
  368. }
  369. // AttachedVolume represents a volume that is attached to a node.
  370. type AttachedVolume struct {
  371. // VolumeName is the unique identifier for the volume that is attached.
  372. VolumeName v1.UniqueVolumeName
  373. // VolumeSpec is the volume spec containing the specification for the
  374. // volume that is attached.
  375. VolumeSpec *volume.Spec
  376. // NodeName is the identifier for the node that the volume is attached to.
  377. NodeName types.NodeName
  378. // PluginIsAttachable indicates that the plugin for this volume implements
  379. // the volume.Attacher interface
  380. PluginIsAttachable bool
  381. // DevicePath contains the path on the node where the volume is attached.
  382. // For non-attachable volumes this is empty.
  383. DevicePath string
  384. // DeviceMountPath contains the path on the node where the device should
  385. // be mounted after it is attached.
  386. DeviceMountPath string
  387. // PluginName is the Unescaped Qualified name of the volume plugin used to
  388. // attach and mount this volume.
  389. PluginName string
  390. }
  391. // GenerateMsgDetailed returns detailed msgs for attached volumes
  392. func (volume *AttachedVolume) GenerateMsgDetailed(prefixMsg, suffixMsg string) (detailedMsg string) {
  393. detailedStr := fmt.Sprintf("(UniqueName: %q) on node %q", volume.VolumeName, volume.NodeName)
  394. volumeSpecName := "nil"
  395. if volume.VolumeSpec != nil {
  396. volumeSpecName = volume.VolumeSpec.Name()
  397. }
  398. return generateVolumeMsgDetailed(prefixMsg, suffixMsg, volumeSpecName, detailedStr)
  399. }
  400. // GenerateMsg returns simple and detailed msgs for attached volumes
  401. func (volume *AttachedVolume) GenerateMsg(prefixMsg, suffixMsg string) (simpleMsg, detailedMsg string) {
  402. detailedStr := fmt.Sprintf("(UniqueName: %q) on node %q", volume.VolumeName, volume.NodeName)
  403. volumeSpecName := "nil"
  404. if volume.VolumeSpec != nil {
  405. volumeSpecName = volume.VolumeSpec.Name()
  406. }
  407. return generateVolumeMsg(prefixMsg, suffixMsg, volumeSpecName, detailedStr)
  408. }
  409. // GenerateErrorDetailed returns detailed errors for attached volumes
  410. func (volume *AttachedVolume) GenerateErrorDetailed(prefixMsg string, err error) (detailedErr error) {
  411. return fmt.Errorf(volume.GenerateMsgDetailed(prefixMsg, errSuffix(err)))
  412. }
  413. // GenerateError returns simple and detailed errors for attached volumes
  414. func (volume *AttachedVolume) GenerateError(prefixMsg string, err error) (simpleErr, detailedErr error) {
  415. simpleMsg, detailedMsg := volume.GenerateMsg(prefixMsg, errSuffix(err))
  416. return fmt.Errorf(simpleMsg), fmt.Errorf(detailedMsg)
  417. }
  418. // MountedVolume represents a volume that has successfully been mounted to a pod.
  419. type MountedVolume struct {
  420. // PodName is the unique identifier of the pod mounted to.
  421. PodName volumetypes.UniquePodName
  422. // VolumeName is the unique identifier of the volume mounted to the pod.
  423. VolumeName v1.UniqueVolumeName
  424. // InnerVolumeSpecName is the volume.Spec.Name() of the volume. If the
  425. // volume was referenced through a persistent volume claims, this contains
  426. // the name of the bound persistent volume object.
  427. // It is the name that plugins use in their pod mount path, i.e.
  428. // /var/lib/kubelet/pods/{podUID}/volumes/{escapeQualifiedPluginName}/{innerVolumeSpecName}/
  429. // PVC example,
  430. // apiVersion: v1
  431. // kind: PersistentVolume
  432. // metadata:
  433. // name: pv0003 <- InnerVolumeSpecName
  434. // spec:
  435. // capacity:
  436. // storage: 5Gi
  437. // accessModes:
  438. // - ReadWriteOnce
  439. // persistentVolumeReclaimPolicy: Recycle
  440. // nfs:
  441. // path: /tmp
  442. // server: 172.17.0.2
  443. // Non-PVC example:
  444. // apiVersion: v1
  445. // kind: Pod
  446. // metadata:
  447. // name: test-pd
  448. // spec:
  449. // containers:
  450. // - image: k8s.gcr.io/test-webserver
  451. // name: test-container
  452. // volumeMounts:
  453. // - mountPath: /test-pd
  454. // name: test-volume
  455. // volumes:
  456. // - name: test-volume <- InnerVolumeSpecName
  457. // gcePersistentDisk:
  458. // pdName: my-data-disk
  459. // fsType: ext4
  460. InnerVolumeSpecName string
  461. // outerVolumeSpecName is the podSpec.Volume[x].Name of the volume. If the
  462. // volume was referenced through a persistent volume claim, this contains
  463. // the podSpec.Volume[x].Name of the persistent volume claim.
  464. // PVC example:
  465. // kind: Pod
  466. // apiVersion: v1
  467. // metadata:
  468. // name: mypod
  469. // spec:
  470. // containers:
  471. // - name: myfrontend
  472. // image: dockerfile/nginx
  473. // volumeMounts:
  474. // - mountPath: "/var/www/html"
  475. // name: mypd
  476. // volumes:
  477. // - name: mypd <- OuterVolumeSpecName
  478. // persistentVolumeClaim:
  479. // claimName: myclaim
  480. // Non-PVC example:
  481. // apiVersion: v1
  482. // kind: Pod
  483. // metadata:
  484. // name: test-pd
  485. // spec:
  486. // containers:
  487. // - image: k8s.gcr.io/test-webserver
  488. // name: test-container
  489. // volumeMounts:
  490. // - mountPath: /test-pd
  491. // name: test-volume
  492. // volumes:
  493. // - name: test-volume <- OuterVolumeSpecName
  494. // gcePersistentDisk:
  495. // pdName: my-data-disk
  496. // fsType: ext4
  497. OuterVolumeSpecName string
  498. // PluginName is the "Unescaped Qualified" name of the volume plugin used to
  499. // mount and unmount this volume. It can be used to fetch the volume plugin
  500. // to unmount with, on demand. It is also the name that plugins use, though
  501. // escaped, in their pod mount path, i.e.
  502. // /var/lib/kubelet/pods/{podUID}/volumes/{escapeQualifiedPluginName}/{outerVolumeSpecName}/
  503. PluginName string
  504. // PodUID is the UID of the pod mounted to. It is also the string used by
  505. // plugins in their pod mount path, i.e.
  506. // /var/lib/kubelet/pods/{podUID}/volumes/{escapeQualifiedPluginName}/{outerVolumeSpecName}/
  507. PodUID types.UID
  508. // Mounter is the volume mounter used to mount this volume. It is required
  509. // by kubelet to create container.VolumeMap.
  510. // Mounter is only required for file system volumes and not required for block volumes.
  511. Mounter volume.Mounter
  512. // BlockVolumeMapper is the volume mapper used to map this volume. It is required
  513. // by kubelet to create container.VolumeMap.
  514. // BlockVolumeMapper is only required for block volumes and not required for file system volumes.
  515. BlockVolumeMapper volume.BlockVolumeMapper
  516. // VolumeGidValue contains the value of the GID annotation, if present.
  517. VolumeGidValue string
  518. // VolumeSpec is a volume spec containing the specification for the volume
  519. // that should be mounted.
  520. VolumeSpec *volume.Spec
  521. // DeviceMountPath contains the path on the node where the device should
  522. // be mounted after it is attached.
  523. DeviceMountPath string
  524. }
  525. // GenerateMsgDetailed returns detailed msgs for mounted volumes
  526. func (volume *MountedVolume) GenerateMsgDetailed(prefixMsg, suffixMsg string) (detailedMsg string) {
  527. detailedStr := fmt.Sprintf("(UniqueName: %q) pod %q (UID: %q)", volume.VolumeName, volume.PodName, volume.PodUID)
  528. return generateVolumeMsgDetailed(prefixMsg, suffixMsg, volume.OuterVolumeSpecName, detailedStr)
  529. }
  530. // GenerateMsg returns simple and detailed msgs for mounted volumes
  531. func (volume *MountedVolume) GenerateMsg(prefixMsg, suffixMsg string) (simpleMsg, detailedMsg string) {
  532. detailedStr := fmt.Sprintf("(UniqueName: %q) pod %q (UID: %q)", volume.VolumeName, volume.PodName, volume.PodUID)
  533. return generateVolumeMsg(prefixMsg, suffixMsg, volume.OuterVolumeSpecName, detailedStr)
  534. }
  535. // GenerateErrorDetailed returns simple and detailed errors for mounted volumes
  536. func (volume *MountedVolume) GenerateErrorDetailed(prefixMsg string, err error) (detailedErr error) {
  537. return fmt.Errorf(volume.GenerateMsgDetailed(prefixMsg, errSuffix(err)))
  538. }
  539. // GenerateError returns simple and detailed errors for mounted volumes
  540. func (volume *MountedVolume) GenerateError(prefixMsg string, err error) (simpleErr, detailedErr error) {
  541. simpleMsg, detailedMsg := volume.GenerateMsg(prefixMsg, errSuffix(err))
  542. return fmt.Errorf(simpleMsg), fmt.Errorf(detailedMsg)
  543. }
  544. type operationExecutor struct {
  545. // pendingOperations keeps track of pending attach and detach operations so
  546. // multiple operations are not started on the same volume
  547. pendingOperations nestedpendingoperations.NestedPendingOperations
  548. // operationGenerator is an interface that provides implementations for
  549. // generating volume function
  550. operationGenerator OperationGenerator
  551. }
  552. func (oe *operationExecutor) IsOperationPending(volumeName v1.UniqueVolumeName, podName volumetypes.UniquePodName) bool {
  553. return oe.pendingOperations.IsOperationPending(volumeName, podName)
  554. }
  555. func (oe *operationExecutor) AttachVolume(
  556. volumeToAttach VolumeToAttach,
  557. actualStateOfWorld ActualStateOfWorldAttacherUpdater) error {
  558. generatedOperations :=
  559. oe.operationGenerator.GenerateAttachVolumeFunc(volumeToAttach, actualStateOfWorld)
  560. return oe.pendingOperations.Run(
  561. volumeToAttach.VolumeName, "" /* podName */, generatedOperations)
  562. }
  563. func (oe *operationExecutor) DetachVolume(
  564. volumeToDetach AttachedVolume,
  565. verifySafeToDetach bool,
  566. actualStateOfWorld ActualStateOfWorldAttacherUpdater) error {
  567. generatedOperations, err :=
  568. oe.operationGenerator.GenerateDetachVolumeFunc(volumeToDetach, verifySafeToDetach, actualStateOfWorld)
  569. if err != nil {
  570. return err
  571. }
  572. return oe.pendingOperations.Run(
  573. volumeToDetach.VolumeName, "" /* podName */, generatedOperations)
  574. }
  575. func (oe *operationExecutor) VerifyVolumesAreAttached(
  576. attachedVolumes map[types.NodeName][]AttachedVolume,
  577. actualStateOfWorld ActualStateOfWorldAttacherUpdater) {
  578. // A map of plugin names and nodes on which they exist with volumes they manage
  579. bulkVerifyPluginsByNode := make(map[string]map[types.NodeName][]*volume.Spec)
  580. volumeSpecMapByPlugin := make(map[string]map[*volume.Spec]v1.UniqueVolumeName)
  581. for node, nodeAttachedVolumes := range attachedVolumes {
  582. for _, volumeAttached := range nodeAttachedVolumes {
  583. if volumeAttached.VolumeSpec == nil {
  584. klog.Errorf("VerifyVolumesAreAttached: nil spec for volume %s", volumeAttached.VolumeName)
  585. continue
  586. }
  587. volumePlugin, err :=
  588. oe.operationGenerator.GetVolumePluginMgr().FindPluginBySpec(volumeAttached.VolumeSpec)
  589. if err != nil {
  590. klog.Errorf(
  591. "VolumesAreAttached.FindPluginBySpec failed for volume %q (spec.Name: %q) on node %q with error: %v",
  592. volumeAttached.VolumeName,
  593. volumeAttached.VolumeSpec.Name(),
  594. volumeAttached.NodeName,
  595. err)
  596. continue
  597. }
  598. if volumePlugin == nil {
  599. // should never happen since FindPluginBySpec always returns error if volumePlugin = nil
  600. klog.Errorf(
  601. "Failed to find volume plugin for volume %q (spec.Name: %q) on node %q",
  602. volumeAttached.VolumeName,
  603. volumeAttached.VolumeSpec.Name(),
  604. volumeAttached.NodeName)
  605. continue
  606. }
  607. pluginName := volumePlugin.GetPluginName()
  608. if volumePlugin.SupportsBulkVolumeVerification() {
  609. pluginNodes, pluginNodesExist := bulkVerifyPluginsByNode[pluginName]
  610. if !pluginNodesExist {
  611. pluginNodes = make(map[types.NodeName][]*volume.Spec)
  612. }
  613. volumeSpecList, nodeExists := pluginNodes[node]
  614. if !nodeExists {
  615. volumeSpecList = []*volume.Spec{}
  616. }
  617. volumeSpecList = append(volumeSpecList, volumeAttached.VolumeSpec)
  618. pluginNodes[node] = volumeSpecList
  619. bulkVerifyPluginsByNode[pluginName] = pluginNodes
  620. volumeSpecMap, mapExists := volumeSpecMapByPlugin[pluginName]
  621. if !mapExists {
  622. volumeSpecMap = make(map[*volume.Spec]v1.UniqueVolumeName)
  623. }
  624. volumeSpecMap[volumeAttached.VolumeSpec] = volumeAttached.VolumeName
  625. volumeSpecMapByPlugin[pluginName] = volumeSpecMap
  626. continue
  627. }
  628. // If node doesn't support Bulk volume polling it is best to poll individually
  629. nodeError := oe.VerifyVolumesAreAttachedPerNode(nodeAttachedVolumes, node, actualStateOfWorld)
  630. if nodeError != nil {
  631. klog.Errorf("VerifyVolumesAreAttached failed for volumes %v, node %q with error %v", nodeAttachedVolumes, node, nodeError)
  632. }
  633. }
  634. }
  635. for pluginName, pluginNodeVolumes := range bulkVerifyPluginsByNode {
  636. generatedOperations, err := oe.operationGenerator.GenerateBulkVolumeVerifyFunc(
  637. pluginNodeVolumes,
  638. pluginName,
  639. volumeSpecMapByPlugin[pluginName],
  640. actualStateOfWorld)
  641. if err != nil {
  642. klog.Errorf("BulkVerifyVolumes.GenerateBulkVolumeVerifyFunc error bulk verifying volumes for plugin %q with %v", pluginName, err)
  643. }
  644. // Ugly hack to ensure - we don't do parallel bulk polling of same volume plugin
  645. uniquePluginName := v1.UniqueVolumeName(pluginName)
  646. err = oe.pendingOperations.Run(uniquePluginName, "" /* Pod Name */, generatedOperations)
  647. if err != nil {
  648. klog.Errorf("BulkVerifyVolumes.Run Error bulk volume verification for plugin %q with %v", pluginName, err)
  649. }
  650. }
  651. }
  652. func (oe *operationExecutor) VerifyVolumesAreAttachedPerNode(
  653. attachedVolumes []AttachedVolume,
  654. nodeName types.NodeName,
  655. actualStateOfWorld ActualStateOfWorldAttacherUpdater) error {
  656. generatedOperations, err :=
  657. oe.operationGenerator.GenerateVolumesAreAttachedFunc(attachedVolumes, nodeName, actualStateOfWorld)
  658. if err != nil {
  659. return err
  660. }
  661. // Give an empty UniqueVolumeName so that this operation could be executed concurrently.
  662. return oe.pendingOperations.Run("" /* volumeName */, "" /* podName */, generatedOperations)
  663. }
  664. func (oe *operationExecutor) MountVolume(
  665. waitForAttachTimeout time.Duration,
  666. volumeToMount VolumeToMount,
  667. actualStateOfWorld ActualStateOfWorldMounterUpdater,
  668. isRemount bool) error {
  669. fsVolume, err := util.CheckVolumeModeFilesystem(volumeToMount.VolumeSpec)
  670. if err != nil {
  671. return err
  672. }
  673. var generatedOperations volumetypes.GeneratedOperations
  674. if fsVolume {
  675. // Filesystem volume case
  676. // Mount/remount a volume when a volume is attached
  677. generatedOperations = oe.operationGenerator.GenerateMountVolumeFunc(
  678. waitForAttachTimeout, volumeToMount, actualStateOfWorld, isRemount)
  679. } else {
  680. // Block volume case
  681. // Creates a map to device if a volume is attached
  682. generatedOperations, err = oe.operationGenerator.GenerateMapVolumeFunc(
  683. waitForAttachTimeout, volumeToMount, actualStateOfWorld)
  684. }
  685. if err != nil {
  686. return err
  687. }
  688. // Avoid executing mount/map from multiple pods referencing the
  689. // same volume in parallel
  690. podName := nestedpendingoperations.EmptyUniquePodName
  691. // TODO: remove this -- not necessary
  692. if !volumeToMount.PluginIsAttachable && !volumeToMount.PluginIsDeviceMountable {
  693. // volume plugins which are Non-attachable and Non-deviceMountable can execute mount for multiple pods
  694. // referencing the same volume in parallel
  695. podName = util.GetUniquePodName(volumeToMount.Pod)
  696. }
  697. // TODO mount_device
  698. return oe.pendingOperations.Run(
  699. volumeToMount.VolumeName, podName, generatedOperations)
  700. }
  701. func (oe *operationExecutor) UnmountVolume(
  702. volumeToUnmount MountedVolume,
  703. actualStateOfWorld ActualStateOfWorldMounterUpdater,
  704. podsDir string) error {
  705. fsVolume, err := util.CheckVolumeModeFilesystem(volumeToUnmount.VolumeSpec)
  706. if err != nil {
  707. return err
  708. }
  709. var generatedOperations volumetypes.GeneratedOperations
  710. if fsVolume {
  711. // Filesystem volume case
  712. // Unmount a volume if a volume is mounted
  713. generatedOperations, err = oe.operationGenerator.GenerateUnmountVolumeFunc(
  714. volumeToUnmount, actualStateOfWorld, podsDir)
  715. } else {
  716. // Block volume case
  717. // Unmap a volume if a volume is mapped
  718. generatedOperations, err = oe.operationGenerator.GenerateUnmapVolumeFunc(
  719. volumeToUnmount, actualStateOfWorld)
  720. }
  721. if err != nil {
  722. return err
  723. }
  724. // All volume plugins can execute unmount/unmap for multiple pods referencing the
  725. // same volume in parallel
  726. podName := volumetypes.UniquePodName(volumeToUnmount.PodUID)
  727. return oe.pendingOperations.Run(
  728. volumeToUnmount.VolumeName, podName, generatedOperations)
  729. }
  730. func (oe *operationExecutor) UnmountDevice(
  731. deviceToDetach AttachedVolume,
  732. actualStateOfWorld ActualStateOfWorldMounterUpdater,
  733. hostutil hostutil.HostUtils) error {
  734. fsVolume, err := util.CheckVolumeModeFilesystem(deviceToDetach.VolumeSpec)
  735. if err != nil {
  736. return err
  737. }
  738. var generatedOperations volumetypes.GeneratedOperations
  739. if fsVolume {
  740. // Filesystem volume case
  741. // Unmount and detach a device if a volume isn't referenced
  742. generatedOperations, err = oe.operationGenerator.GenerateUnmountDeviceFunc(
  743. deviceToDetach, actualStateOfWorld, hostutil)
  744. } else {
  745. // Block volume case
  746. // Detach a device and remove loopback if a volume isn't referenced
  747. generatedOperations, err = oe.operationGenerator.GenerateUnmapDeviceFunc(
  748. deviceToDetach, actualStateOfWorld, hostutil)
  749. }
  750. if err != nil {
  751. return err
  752. }
  753. // Avoid executing unmount/unmap device from multiple pods referencing
  754. // the same volume in parallel
  755. podName := nestedpendingoperations.EmptyUniquePodName
  756. return oe.pendingOperations.Run(
  757. deviceToDetach.VolumeName, podName, generatedOperations)
  758. }
  759. func (oe *operationExecutor) ExpandInUseVolume(volumeToMount VolumeToMount, actualStateOfWorld ActualStateOfWorldMounterUpdater) error {
  760. generatedOperations, err := oe.operationGenerator.GenerateExpandInUseVolumeFunc(volumeToMount, actualStateOfWorld)
  761. if err != nil {
  762. return err
  763. }
  764. return oe.pendingOperations.Run(volumeToMount.VolumeName, "", generatedOperations)
  765. }
  766. func (oe *operationExecutor) VerifyControllerAttachedVolume(
  767. volumeToMount VolumeToMount,
  768. nodeName types.NodeName,
  769. actualStateOfWorld ActualStateOfWorldAttacherUpdater) error {
  770. generatedOperations, err :=
  771. oe.operationGenerator.GenerateVerifyControllerAttachedVolumeFunc(volumeToMount, nodeName, actualStateOfWorld)
  772. if err != nil {
  773. return err
  774. }
  775. return oe.pendingOperations.Run(
  776. volumeToMount.VolumeName, "" /* podName */, generatedOperations)
  777. }
  778. // ReconstructVolumeOperation return a func to create volumeSpec from mount path
  779. func (oe *operationExecutor) ReconstructVolumeOperation(
  780. volumeMode v1.PersistentVolumeMode,
  781. plugin volume.VolumePlugin,
  782. mapperPlugin volume.BlockVolumePlugin,
  783. uid types.UID,
  784. podName volumetypes.UniquePodName,
  785. volumeSpecName string,
  786. volumePath string,
  787. pluginName string) (*volume.Spec, error) {
  788. // Filesystem Volume case
  789. if volumeMode == v1.PersistentVolumeFilesystem {
  790. // Create volumeSpec from mount path
  791. klog.V(5).Infof("Starting operationExecutor.ReconstructVolumepodName")
  792. volumeSpec, err := plugin.ConstructVolumeSpec(volumeSpecName, volumePath)
  793. if err != nil {
  794. return nil, err
  795. }
  796. return volumeSpec, nil
  797. }
  798. // Block Volume case
  799. // Create volumeSpec from mount path
  800. klog.V(5).Infof("Starting operationExecutor.ReconstructVolume")
  801. // volumePath contains volumeName on the path. In the case of block volume, {volumeName} is symbolic link
  802. // corresponding to raw block device.
  803. // ex. volumePath: pods/{podUid}}/{DefaultKubeletVolumeDevicesDirName}/{escapeQualifiedPluginName}/{volumeName}
  804. volumeSpec, err := mapperPlugin.ConstructBlockVolumeSpec(uid, volumeSpecName, volumePath)
  805. if err != nil {
  806. return nil, err
  807. }
  808. return volumeSpec, nil
  809. }
  810. // CheckVolumeExistenceOperation checks mount path directory if volume still exists
  811. func (oe *operationExecutor) CheckVolumeExistenceOperation(
  812. volumeSpec *volume.Spec,
  813. mountPath, volumeName string,
  814. mounter mount.Interface,
  815. uniqueVolumeName v1.UniqueVolumeName,
  816. podName volumetypes.UniquePodName,
  817. podUID types.UID,
  818. attachable volume.AttachableVolumePlugin) (bool, error) {
  819. fsVolume, err := util.CheckVolumeModeFilesystem(volumeSpec)
  820. if err != nil {
  821. return false, err
  822. }
  823. // Filesystem Volume case
  824. // For attachable volume case, check mount path directory if volume is still existing and mounted.
  825. // Return true if volume is mounted.
  826. if fsVolume {
  827. if attachable != nil {
  828. var isNotMount bool
  829. var mountCheckErr error
  830. if mounter == nil {
  831. return false, fmt.Errorf("mounter was not set for a filesystem volume")
  832. }
  833. if isNotMount, mountCheckErr = mounter.IsLikelyNotMountPoint(mountPath); mountCheckErr != nil {
  834. return false, fmt.Errorf("Could not check whether the volume %q (spec.Name: %q) pod %q (UID: %q) is mounted with: %v",
  835. uniqueVolumeName,
  836. volumeName,
  837. podName,
  838. podUID,
  839. mountCheckErr)
  840. }
  841. return !isNotMount, nil
  842. }
  843. // For non-attachable volume case, skip check and return true without mount point check
  844. // since plugins may not have volume mount point.
  845. return true, nil
  846. }
  847. // Block Volume case
  848. // Check mount path directory if volume still exists, then return true if volume
  849. // is there. Either plugin is attachable or non-attachable, the plugin should
  850. // have symbolic link associated to raw block device under pod device map
  851. // if volume exists.
  852. blkutil := volumepathhandler.NewBlockVolumePathHandler()
  853. var islinkExist bool
  854. var checkErr error
  855. if islinkExist, checkErr = blkutil.IsSymlinkExist(mountPath); checkErr != nil {
  856. return false, fmt.Errorf("Could not check whether the block volume %q (spec.Name: %q) pod %q (UID: %q) is mapped to: %v",
  857. uniqueVolumeName,
  858. volumeName,
  859. podName,
  860. podUID,
  861. checkErr)
  862. }
  863. return islinkExist, nil
  864. }