@@ -17,20 +17,48 @@ package gceGCEDriver
17
17
import (
18
18
"fmt"
19
19
"os"
20
+ "path"
21
+ "path/filepath"
22
+ "strings"
20
23
"sync"
21
24
22
25
csi "github.com/container-storage-interface/spec/lib/go/csi/v0"
23
26
"github.com/golang/glog"
24
27
"golang.org/x/net/context"
25
28
"google.golang.org/grpc/codes"
26
29
"google.golang.org/grpc/status"
27
- mountmanager "sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/pkg/mount-manager"
30
+ "k8s.io/apimachinery/pkg/util/sets"
31
+ "k8s.io/kubernetes/pkg/util/mount"
32
+ "k8s.io/utils/exec"
28
33
utils "sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/pkg/utils"
29
34
)
30
35
36
+ const (
37
+ diskByHostIdPath = "/host/dev/disk/by-id/"
38
+ diskByIdPath = "/dev/disk/by-id/"
39
+ diskGooglePrefix = "google-"
40
+ diskScsiGooglePrefix = "scsi-0Google_PersistentDisk_"
41
+ diskPartitionSuffix = "-part"
42
+ diskSDPath = "/host/dev/sd"
43
+ diskSDPattern = "/host/dev/sd*"
44
+ // How many times to retry for a consistent read of /proc/mounts.
45
+ maxListTries = 3
46
+ // Number of fields per line in /proc/mounts as per the fstab man page.
47
+ expectedNumFieldsPerLine = 6
48
+ // Location of the mount file to use
49
+ procMountsPath = "/proc/mounts"
50
+ // Location of the mountinfo file
51
+ procMountInfoPath = "/proc/self/mountinfo"
52
+ // 'fsck' found errors and corrected them
53
+ fsckErrorsCorrected = 1
54
+ // 'fsck' found errors but exited without correcting them
55
+ fsckErrorsUncorrected = 4
56
+ defaultMountCommand = "mount"
57
+ )
58
+
31
59
type GCENodeServer struct {
32
60
Driver * GCEDriver
33
- Mounter mountmanager. Mounter
61
+ Mounter mount. SafeFormatAndMount
34
62
// TODO: Only lock mutually exclusive calls and make locking more fine grained
35
63
mux sync.Mutex
36
64
}
@@ -71,7 +99,7 @@ func (ns *GCENodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePub
71
99
return nil , nil
72
100
}
73
101
74
- if err := ns .Mounter .MkdirAll (targetPath , 0750 ); err != nil {
102
+ if err := ns .Mounter .MakeDir (targetPath ); err != nil {
75
103
glog .Errorf ("mkdir failed on disk %s (%v)" , targetPath , err )
76
104
return nil , err
77
105
}
@@ -82,15 +110,15 @@ func (ns *GCENodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePub
82
110
options = append (options , "ro" )
83
111
}
84
112
85
- err = ns .Mounter .DoMount (stagingTargetPath , targetPath , "ext4" , options )
113
+ err = ns .Mounter .Mount (stagingTargetPath , targetPath , "ext4" , options )
86
114
if err != nil {
87
115
notMnt , mntErr := ns .Mounter .IsLikelyNotMountPoint (targetPath )
88
116
if mntErr != nil {
89
117
glog .Errorf ("IsLikelyNotMountPoint check failed: %v" , mntErr )
90
118
return nil , status .Error (codes .Internal , fmt .Sprintf ("TODO: %v" , err ))
91
119
}
92
120
if ! notMnt {
93
- if _ , mntErr = ns .Mounter .UnmountVolume (targetPath ); mntErr != nil {
121
+ if mntErr = ns .Mounter .Unmount (targetPath ); mntErr != nil {
94
122
glog .Errorf ("Failed to unmount: %v" , mntErr )
95
123
return nil , status .Error (codes .Internal , fmt .Sprintf ("TODO: %v" , err ))
96
124
}
@@ -105,7 +133,7 @@ func (ns *GCENodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePub
105
133
return nil , status .Error (codes .Internal , fmt .Sprintf ("TODO: %v" , err ))
106
134
}
107
135
}
108
- ns . Mounter .Remove (targetPath )
136
+ os .Remove (targetPath )
109
137
glog .Errorf ("Mount of disk %s failed: %v" , targetPath , err )
110
138
return nil , status .Error (codes .Internal , fmt .Sprintf ("TODO: %v" , err ))
111
139
}
@@ -130,9 +158,9 @@ func (ns *GCENodeServer) NodeUnpublishVolume(ctx context.Context, req *csi.NodeU
130
158
131
159
// TODO: Check volume still exists
132
160
133
- output , err := ns .Mounter .UnmountVolume (targetPath )
161
+ err := ns .Mounter .Unmount (targetPath )
134
162
if err != nil {
135
- return nil , status .Error (codes .Internal , fmt .Sprintf ("Unmount failed: %v\n Unmounting arguments: %s\n Output: %s \ n " , err , targetPath , output ))
163
+ return nil , status .Error (codes .Internal , fmt .Sprintf ("Unmount failed: %v\n Unmounting arguments: %s\n " , err , targetPath ))
136
164
}
137
165
138
166
return & csi.NodeUnpublishVolumeResponse {}, nil
@@ -168,8 +196,8 @@ func (ns *GCENodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStage
168
196
// TODO: Get real partitions
169
197
partition := ""
170
198
171
- devicePaths := ns . Mounter . GetDiskByIdPaths (volumeName , partition )
172
- devicePath , err := ns . Mounter . VerifyDevicePath (devicePaths )
199
+ devicePaths := getDiskByIdPaths (volumeName , partition )
200
+ devicePath , err := verifyDevicePath (devicePaths )
173
201
174
202
if err != nil {
175
203
return nil , status .Error (codes .Internal , fmt .Sprintf ("Error verifying GCE PD (%q) is attached: %v" , volumeName , err ))
@@ -184,7 +212,7 @@ func (ns *GCENodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStage
184
212
notMnt , err := ns .Mounter .IsLikelyNotMountPoint (stagingTargetPath )
185
213
if err != nil {
186
214
if os .IsNotExist (err ) {
187
- if err := ns .Mounter .MkdirAll (stagingTargetPath , 0750 ); err != nil {
215
+ if err := ns .Mounter .MakeDir (stagingTargetPath ); err != nil {
188
216
return nil , status .Error (codes .Internal , fmt .Sprintf ("Failed to create directory (%q): %v" , stagingTargetPath , err ))
189
217
}
190
218
notMnt = true
@@ -242,7 +270,7 @@ func (ns *GCENodeServer) NodeUnstageVolume(ctx context.Context, req *csi.NodeUns
242
270
return nil , status .Error (codes .InvalidArgument , "NodeUnstageVolume Staging Target Path must be provided" )
243
271
}
244
272
245
- _ , err := ns .Mounter .UnmountVolume (stagingTargetPath )
273
+ err := ns .Mounter .Unmount (stagingTargetPath )
246
274
if err != nil {
247
275
return nil , status .Error (codes .Internal , fmt .Sprintf ("NodeUnstageVolume failed to unmount at path %s: %v" , stagingTargetPath , err ))
248
276
}
@@ -277,3 +305,107 @@ func (ns *GCENodeServer) NodeGetInfo(ctx context.Context, req *csi.NodeGetInfoRe
277
305
}
278
306
return resp , nil
279
307
}
308
+
309
+ // Returns list of all /dev/disk/by-id/* paths for given PD.
310
+ func getDiskByIdPaths (pdName string , partition string ) []string {
311
+ devicePaths := []string {
312
+ path .Join (diskByIdPath , diskGooglePrefix + pdName ),
313
+ path .Join (diskByIdPath , diskScsiGooglePrefix + pdName ),
314
+ path .Join (diskByHostIdPath , diskScsiGooglePrefix + pdName ),
315
+ path .Join (diskByHostIdPath , diskScsiGooglePrefix + pdName ),
316
+ }
317
+
318
+ if partition != "" {
319
+ for i , path := range devicePaths {
320
+ devicePaths [i ] = path + diskPartitionSuffix + partition
321
+ }
322
+ }
323
+
324
+ return devicePaths
325
+ }
326
+
327
+ // Returns the first path that exists, or empty string if none exist.
328
+ func verifyDevicePath (devicePaths []string ) (string , error ) {
329
+ sdBefore , err := filepath .Glob (diskSDPattern )
330
+ if err != nil {
331
+ // Seeing this error means that the diskSDPattern is malformed.
332
+ glog .Errorf ("Error filepath.Glob(\" %s\" ): %v\r \n " , diskSDPattern , err )
333
+ }
334
+ sdBeforeSet := sets .NewString (sdBefore ... )
335
+ // TODO: Remove this udevadm stuff. Not applicable because can't access /dev/sd* from container
336
+ if err := udevadmChangeToNewDrives (sdBeforeSet ); err != nil {
337
+ // udevadm errors should not block disk detachment, log and continue
338
+ glog .Errorf ("udevadmChangeToNewDrives failed with: %v" , err )
339
+ }
340
+
341
+ for _ , path := range devicePaths {
342
+ if pathExists , err := pathExists (path ); err != nil {
343
+ return "" , fmt .Errorf ("Error checking if path exists: %v" , err )
344
+ } else if pathExists {
345
+ return path , nil
346
+ }
347
+ }
348
+
349
+ return "" , nil
350
+ }
351
+
352
+ // Triggers the application of udev rules by calling "udevadm trigger
353
+ // --action=change" for newly created "/dev/sd*" drives (exist only in
354
+ // after set). This is workaround for Issue #7972. Once the underlying
355
+ // issue has been resolved, this may be removed.
356
+ func udevadmChangeToNewDrives (sdBeforeSet sets.String ) error {
357
+ sdAfter , err := filepath .Glob (diskSDPattern )
358
+ if err != nil {
359
+ return fmt .Errorf ("Error filepath.Glob(\" %s\" ): %v\r \n " , diskSDPattern , err )
360
+ }
361
+
362
+ for _ , sd := range sdAfter {
363
+ if ! sdBeforeSet .Has (sd ) {
364
+ return udevadmChangeToDrive (sd )
365
+ }
366
+ }
367
+
368
+ return nil
369
+ }
370
+
371
+ // Calls "udevadm trigger --action=change" on the specified drive.
372
+ // drivePath must be the block device path to trigger on, in the format "/dev/sd*", or a symlink to it.
373
+ // This is workaround for Issue #7972. Once the underlying issue has been resolved, this may be removed.
374
+ func udevadmChangeToDrive (drivePath string ) error {
375
+ glog .V (5 ).Infof ("udevadmChangeToDrive: drive=%q" , drivePath )
376
+
377
+ // Evaluate symlink, if any
378
+ drive , err := filepath .EvalSymlinks (drivePath )
379
+ if err != nil {
380
+ return fmt .Errorf ("udevadmChangeToDrive: filepath.EvalSymlinks(%q) failed with %v." , drivePath , err )
381
+ }
382
+ glog .V (5 ).Infof ("udevadmChangeToDrive: symlink path is %q" , drive )
383
+
384
+ // Check to make sure input is "/dev/sd*"
385
+ if ! strings .Contains (drive , diskSDPath ) {
386
+ return fmt .Errorf ("udevadmChangeToDrive: expected input in the form \" %s\" but drive is %q." , diskSDPattern , drive )
387
+ }
388
+
389
+ // Call "udevadm trigger --action=change --property-match=DEVNAME=/dev/sd..."
390
+ _ , err = exec .New ().Command (
391
+ "udevadm" ,
392
+ "trigger" ,
393
+ "--action=change" ,
394
+ fmt .Sprintf ("--property-match=DEVNAME=%s" , drive )).CombinedOutput ()
395
+ if err != nil {
396
+ return fmt .Errorf ("udevadmChangeToDrive: udevadm trigger failed for drive %q with %v." , drive , err )
397
+ }
398
+ return nil
399
+ }
400
+
401
+ // PathExists returns true if the specified path exists.
402
+ func pathExists (path string ) (bool , error ) {
403
+ _ , err := os .Stat (path )
404
+ if err == nil {
405
+ return true , nil
406
+ } else if os .IsNotExist (err ) {
407
+ return false , nil
408
+ } else {
409
+ return false , err
410
+ }
411
+ }
0 commit comments