@@ -14,6 +14,55 @@ See the License for the specific language governing permissions and
14
14
limitations under the License.
15
15
*/
16
16
17
+ /*
18
+ This file defines block volume related methods for CSI driver.
19
+ CSI driver is responsible for staging/publishing volumes to their staging/publish paths.
20
+ Mapping and unmapping of a device in a publish path to its global map path and its
21
+ pod device map path are done by operation_executor through MapBlockVolume/UnmapBlockVolume
22
+ (MapBlockVolume and UnmapBlockVolume take care for lock, symlink, and bind mount).
23
+
24
+ Summary of block volume related CSI driver's methods are as follows:
25
+ - GetGlobalMapPath returns a global map path,
26
+ - GetPodDeviceMapPath returns a pod device map path and filename,
27
+ - SetUpDevice calls CSI's NodeStageVolume and stage a volume to its staging path,
28
+ - MapPodDevice calls CSI's NodePublishVolume and publish a volume to its publish path,
29
+ - UnmapPodDevice calls CSI's NodeUnpublishVolume and unpublish a volume from its publish path,
30
+ - TearDownDevice calls CSI's NodeUnstageVolume and unstage a volume from its staging path.
31
+
32
+ These methods are called by below sequences:
33
+ - operation_executor.MountVolume
34
+ - csi.GetGlobalMapPath
35
+ - csi.SetupDevice
36
+ - NodeStageVolume
37
+ - ASW.MarkDeviceAsMounted
38
+ - csi.GetPodDeviceMapPath
39
+ - csi.MapPodDevice
40
+ - NodePublishVolume
41
+ - util.MapBlockVolume
42
+ - ASW.MarkVolumeAsMounted
43
+
44
+ - operation_executor.UnmountVolume
45
+ - csi.GetPodDeviceMapPath
46
+ - util.UnmapBlockVolume
47
+ - csi.UnmapPodDevice
48
+ - NodeUnpublishVolume
49
+ - ASW.MarkVolumeAsUnmounted
50
+
51
+ - operation_executor.UnmountDevice
52
+ - csi.TearDownDevice
53
+ - NodeUnstageVolume
54
+ - ASW.MarkDeviceAsUnmounted
55
+
56
+ After successful MountVolume for block volume, directory structure will be like below:
57
+ /dev/loopX ... Descriptor lock(Loopback device to mapFile under global map path)
58
+ /var/lib/kubelet/plugins/kubernetes.io/csi/volumeDevices/{specName}/dev/ ... Global map path
59
+ /var/lib/kubelet/plugins/kubernetes.io/csi/volumeDevices/{specName}/dev/{podUID} ... MapFile(Bind mount to publish Path)
60
+ /var/lib/kubelet/plugins/kubernetes.io/csi/volumeDevices/staging/{specName} ... Staging path
61
+ /var/lib/kubelet/plugins/kubernetes.io/csi/volumeDevices/publish/{specName}/{podUID} ... Publish path
62
+ /var/lib/kubelet/pods/{podUID}/volumeDevices/kubernetes.io~csi/ ... Pod device map path
63
+ /var/lib/kubelet/pods/{podUID}/volumeDevices/kubernetes.io~csi/{specName} ... MapFile(Symlink to publish path)
64
+ */
65
+
17
66
package csi
18
67
19
68
import (
@@ -51,34 +100,31 @@ var _ volume.BlockVolumeMapper = &csiBlockMapper{}
51
100
var _ volume.CustomBlockVolumeMapper = & csiBlockMapper {}
52
101
53
102
// GetGlobalMapPath returns a global map path (on the node) to a device file which will be symlinked to
54
- // Example: plugins/kubernetes.io/csi/volumeDevices/{pvname }/dev
103
+ // Example: plugins/kubernetes.io/csi/volumeDevices/{specName }/dev
55
104
func (m * csiBlockMapper ) GetGlobalMapPath (spec * volume.Spec ) (string , error ) {
56
- dir := getVolumeDevicePluginDir (spec . Name () , m .plugin .host )
105
+ dir := getVolumeDevicePluginDir (m . specName , m .plugin .host )
57
106
klog .V (4 ).Infof (log ("blockMapper.GetGlobalMapPath = %s" , dir ))
58
107
return dir , nil
59
108
}
60
109
61
110
// getStagingPath returns a staging path for a directory (on the node) that should be used on NodeStageVolume/NodeUnstageVolume
62
- // Example: plugins/kubernetes.io/csi/volumeDevices/staging/{pvname }
111
+ // Example: plugins/kubernetes.io/csi/volumeDevices/staging/{specName }
63
112
func (m * csiBlockMapper ) getStagingPath () string {
64
- sanitizedSpecVolID := utilstrings .EscapeQualifiedName (m .specName )
65
- return filepath .Join (m .plugin .host .GetVolumeDevicePluginDir (CSIPluginName ), "staging" , sanitizedSpecVolID )
113
+ return filepath .Join (m .plugin .host .GetVolumeDevicePluginDir (CSIPluginName ), "staging" , m .specName )
66
114
}
67
115
68
116
// getPublishPath returns a publish path for a file (on the node) that should be used on NodePublishVolume/NodeUnpublishVolume
69
- // Example: plugins/kubernetes.io/csi/volumeDevices/publish/{pvname }
117
+ // Example: plugins/kubernetes.io/csi/volumeDevices/publish/{specName}/{podUID }
70
118
func (m * csiBlockMapper ) getPublishPath () string {
71
- sanitizedSpecVolID := utilstrings .EscapeQualifiedName (m .specName )
72
- return filepath .Join (m .plugin .host .GetVolumeDevicePluginDir (CSIPluginName ), "publish" , sanitizedSpecVolID )
119
+ return filepath .Join (m .plugin .host .GetVolumeDevicePluginDir (CSIPluginName ), "publish" , m .specName , string (m .podUID ))
73
120
}
74
121
75
122
// GetPodDeviceMapPath returns pod's device file which will be mapped to a volume
76
- // returns: pods/{podUid }/volumeDevices/kubernetes.io~csi, {pvname }
123
+ // returns: pods/{podUID }/volumeDevices/kubernetes.io~csi, {specName }
77
124
func (m * csiBlockMapper ) GetPodDeviceMapPath () (string , string ) {
78
125
path := m .plugin .host .GetPodVolumeDeviceDir (m .podUID , utilstrings .EscapeQualifiedName (CSIPluginName ))
79
- specName := m .specName
80
- klog .V (4 ).Infof (log ("blockMapper.GetPodDeviceMapPath [path=%s; name=%s]" , path , specName ))
81
- return path , specName
126
+ klog .V (4 ).Infof (log ("blockMapper.GetPodDeviceMapPath [path=%s; name=%s]" , path , m .specName ))
127
+ return path , m .specName
82
128
}
83
129
84
130
// stageVolumeForBlock stages a block volume to stagingPath
@@ -150,7 +196,6 @@ func (m *csiBlockMapper) publishVolumeForBlock(
150
196
accessMode v1.PersistentVolumeAccessMode ,
151
197
csiSource * v1.CSIPersistentVolumeSource ,
152
198
attachment * storage.VolumeAttachment ,
153
- stagingPath string ,
154
199
) (string , error ) {
155
200
klog .V (4 ).Infof (log ("blockMapper.publishVolumeForBlock called" ))
156
201
@@ -186,7 +231,7 @@ func (m *csiBlockMapper) publishVolumeForBlock(
186
231
ctx ,
187
232
m .volumeID ,
188
233
m .readOnly ,
189
- stagingPath ,
234
+ m . getStagingPath () ,
190
235
publishPath ,
191
236
accessMode ,
192
237
publishVolumeInfo ,
@@ -252,13 +297,7 @@ func (m *csiBlockMapper) SetUpDevice() error {
252
297
}
253
298
254
299
// Call NodeStageVolume
255
- stagingPath , err := m .stageVolumeForBlock (ctx , csiClient , accessMode , csiSource , attachment )
256
- if err != nil {
257
- return err
258
- }
259
-
260
- // Call NodePublishVolume
261
- _ , err = m .publishVolumeForBlock (ctx , csiClient , accessMode , csiSource , attachment , stagingPath )
300
+ _ , err = m .stageVolumeForBlock (ctx , csiClient , accessMode , csiSource , attachment )
262
301
if err != nil {
263
302
return err
264
303
}
@@ -267,7 +306,59 @@ func (m *csiBlockMapper) SetUpDevice() error {
267
306
}
268
307
269
308
func (m * csiBlockMapper ) MapPodDevice () (string , error ) {
270
- return m .getPublishPath (), nil
309
+ if ! m .plugin .blockEnabled {
310
+ return "" , errors .New ("CSIBlockVolume feature not enabled" )
311
+ }
312
+ klog .V (4 ).Infof (log ("blockMapper.MapPodDevice called" ))
313
+
314
+ // Get csiSource from spec
315
+ if m .spec == nil {
316
+ return "" , errors .New (log ("blockMapper.MapPodDevice spec is nil" ))
317
+ }
318
+
319
+ csiSource , err := getCSISourceFromSpec (m .spec )
320
+ if err != nil {
321
+ return "" , errors .New (log ("blockMapper.MapPodDevice failed to get CSI persistent source: %v" , err ))
322
+ }
323
+
324
+ driverName := csiSource .Driver
325
+ skip , err := m .plugin .skipAttach (driverName )
326
+ if err != nil {
327
+ return "" , errors .New (log ("blockMapper.MapPodDevice failed to check CSIDriver for %s: %v" , driverName , err ))
328
+ }
329
+
330
+ var attachment * storage.VolumeAttachment
331
+ if ! skip {
332
+ // Search for attachment by VolumeAttachment.Spec.Source.PersistentVolumeName
333
+ nodeName := string (m .plugin .host .GetNodeName ())
334
+ attachID := getAttachmentName (csiSource .VolumeHandle , csiSource .Driver , nodeName )
335
+ attachment , err = m .k8s .StorageV1 ().VolumeAttachments ().Get (attachID , meta.GetOptions {})
336
+ if err != nil {
337
+ return "" , errors .New (log ("blockMapper.MapPodDevice failed to get volume attachment [id=%v]: %v" , attachID , err ))
338
+ }
339
+ }
340
+
341
+ //TODO (vladimirvivien) implement better AccessModes mapping between k8s and CSI
342
+ accessMode := v1 .ReadWriteOnce
343
+ if m .spec .PersistentVolume .Spec .AccessModes != nil {
344
+ accessMode = m .spec .PersistentVolume .Spec .AccessModes [0 ]
345
+ }
346
+
347
+ ctx , cancel := context .WithTimeout (context .Background (), csiTimeout )
348
+ defer cancel ()
349
+
350
+ csiClient , err := m .csiClientGetter .Get ()
351
+ if err != nil {
352
+ return "" , errors .New (log ("blockMapper.MapPodDevice failed to get CSI client: %v" , err ))
353
+ }
354
+
355
+ // Call NodePublishVolume
356
+ publishPath , err := m .publishVolumeForBlock (ctx , csiClient , accessMode , csiSource , attachment )
357
+ if err != nil {
358
+ return "" , err
359
+ }
360
+
361
+ return publishPath , nil
271
362
}
272
363
273
364
var _ volume.BlockVolumeUnmapper = & csiBlockMapper {}
@@ -322,8 +413,6 @@ func (m *csiBlockMapper) TearDownDevice(globalMapPath, devicePath string) error
322
413
return errors .New ("CSIBlockVolume feature not enabled" )
323
414
}
324
415
325
- klog .V (4 ).Infof (log ("unmapper.TearDownDevice(globalMapPath=%s; devicePath=%s)" , globalMapPath , devicePath ))
326
-
327
416
ctx , cancel := context .WithTimeout (context .Background (), csiTimeout )
328
417
defer cancel ()
329
418
@@ -332,21 +421,6 @@ func (m *csiBlockMapper) TearDownDevice(globalMapPath, devicePath string) error
332
421
return errors .New (log ("blockMapper.TearDownDevice failed to get CSI client: %v" , err ))
333
422
}
334
423
335
- // Call NodeUnpublishVolume
336
- publishPath := m .getPublishPath ()
337
- if _ , err := os .Stat (publishPath ); err != nil {
338
- if os .IsNotExist (err ) {
339
- klog .V (4 ).Infof (log ("blockMapper.TearDownDevice publishPath(%s) has already been deleted, skip calling NodeUnpublishVolume" , publishPath ))
340
- } else {
341
- return err
342
- }
343
- } else {
344
- err := m .unpublishVolumeForBlock (ctx , csiClient , publishPath )
345
- if err != nil {
346
- return err
347
- }
348
- }
349
-
350
424
// Call NodeUnstageVolume
351
425
stagingPath := m .getStagingPath ()
352
426
if _ , err := os .Stat (stagingPath ); err != nil {
@@ -367,5 +441,32 @@ func (m *csiBlockMapper) TearDownDevice(globalMapPath, devicePath string) error
367
441
368
442
// UnmapPodDevice unmaps the block device path.
369
443
func (m * csiBlockMapper ) UnmapPodDevice () error {
444
+ if ! m .plugin .blockEnabled {
445
+ return errors .New ("CSIBlockVolume feature not enabled" )
446
+ }
447
+ publishPath := m .getPublishPath ()
448
+
449
+ csiClient , err := m .csiClientGetter .Get ()
450
+ if err != nil {
451
+ return errors .New (log ("blockMapper.UnmapPodDevice failed to get CSI client: %v" , err ))
452
+ }
453
+
454
+ ctx , cancel := context .WithTimeout (context .Background (), csiTimeout )
455
+ defer cancel ()
456
+
457
+ // Call NodeUnpublishVolume
458
+ if _ , err := os .Stat (publishPath ); err != nil {
459
+ if os .IsNotExist (err ) {
460
+ klog .V (4 ).Infof (log ("blockMapper.UnmapPodDevice publishPath(%s) has already been deleted, skip calling NodeUnpublishVolume" , publishPath ))
461
+ } else {
462
+ return err
463
+ }
464
+ } else {
465
+ err := m .unpublishVolumeForBlock (ctx , csiClient , publishPath )
466
+ if err != nil {
467
+ return err
468
+ }
469
+ }
470
+
370
471
return nil
371
472
}
0 commit comments