Skip to content

Commit eced10c

Browse files
committed
Replaced mount-manager with well tested k8s mount utilities
1 parent c7b4035 commit eced10c

File tree

7 files changed

+188
-475
lines changed

7 files changed

+188
-475
lines changed

cmd/main.go

+1-5
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222

2323
gce "sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/pkg/gce-cloud-provider"
2424
driver "sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/pkg/gce-pd-csi-driver"
25-
mountmanager "sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/pkg/mount-manager"
2625
)
2726

2827
func init() {
@@ -56,10 +55,7 @@ func handle() {
5655
glog.Fatalf("Failed to get cloud provider: %v", err)
5756
}
5857

59-
mounter, err := mountmanager.CreateMounter()
60-
if err != nil {
61-
glog.Fatalf("Failed to get mounter: %v", err)
62-
}
58+
mounter := driver.NewSafeFormatAndMounter()
6359

6460
err = gceDriver.SetupGCEDriver(cloudProvider, mounter, *driverName, *nodeID, vendorVersion)
6561
if err != nil {

pkg/gce-pd-csi-driver/gce-pd-driver.go

+42-3
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ import (
2121
"github.com/golang/glog"
2222
"google.golang.org/grpc/codes"
2323
"google.golang.org/grpc/status"
24+
"k8s.io/kubernetes/pkg/util/mount"
2425
gce "sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/pkg/gce-cloud-provider"
25-
mountmanager "sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/pkg/mount-manager"
2626
)
2727

2828
type GCEDriver struct {
@@ -43,7 +43,7 @@ func GetGCEDriver() *GCEDriver {
4343
return &GCEDriver{}
4444
}
4545

46-
func (gceDriver *GCEDriver) SetupGCEDriver(cloudProvider gce.GCECompute, mounter mountmanager.Mounter, name, nodeID, vendorVersion string) error {
46+
func (gceDriver *GCEDriver) SetupGCEDriver(cloudProvider gce.GCECompute, mounter mount.SafeFormatAndMount, name, nodeID, vendorVersion string) error {
4747
if name == "" {
4848
return fmt.Errorf("Driver name missing")
4949
}
@@ -129,7 +129,7 @@ func NewIdentityServer(gceDriver *GCEDriver) *GCEIdentityServer {
129129
}
130130
}
131131

132-
func NewNodeServer(gceDriver *GCEDriver, mounter mountmanager.Mounter) *GCENodeServer {
132+
func NewNodeServer(gceDriver *GCEDriver, mounter mount.SafeFormatAndMount) *GCENodeServer {
133133
return &GCENodeServer{
134134
Driver: gceDriver,
135135
Mounter: mounter,
@@ -143,6 +143,45 @@ func NewControllerServer(gceDriver *GCEDriver, cloudProvider gce.GCECompute) *GC
143143
}
144144
}
145145

146+
func NewSafeFormatAndMounter() mount.SafeFormatAndMount {
147+
realMounter := mount.New("")
148+
realExec := mount.NewOsExec()
149+
return mount.SafeFormatAndMount{
150+
Interface: realMounter,
151+
Exec: realExec,
152+
}
153+
}
154+
155+
func NewFakeSafeFormatAndMounter(mountErrs []error) mount.SafeFormatAndMount {
156+
execCallback := func(cmd string, args ...string) ([]byte, error) {
157+
return nil, nil
158+
// TODO: Fill out exec callback for errors
159+
/*
160+
if len(test.execScripts) <= execCallCount {
161+
t.Errorf("Unexpected command: %s %v", cmd, args)
162+
return nil, nil
163+
}
164+
script := test.execScripts[execCallCount]
165+
execCallCount++
166+
if script.command != cmd {
167+
t.Errorf("Unexpected command %s. Expecting %s", cmd, script.command)
168+
}
169+
for j := range args {
170+
if args[j] != script.args[j] {
171+
t.Errorf("Unexpected args %v. Expecting %v", args, script.args)
172+
}
173+
}
174+
return []byte(script.output), script.err
175+
*/
176+
}
177+
fakeMounter := mount.ErrorMounter{&mount.FakeMounter{}, 0, mountErrs}
178+
fakeExec := mount.NewFakeExec(execCallback)
179+
return mount.SafeFormatAndMount{
180+
Interface: &fakeMounter,
181+
Exec: fakeExec,
182+
}
183+
}
184+
146185
func (gceDriver *GCEDriver) Run(endpoint string) {
147186
glog.Infof("Driver: %v", gceDriver.name)
148187

pkg/gce-pd-csi-driver/node.go

+144-12
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,48 @@ package gceGCEDriver
1717
import (
1818
"fmt"
1919
"os"
20+
"path"
21+
"path/filepath"
22+
"strings"
2023
"sync"
2124

2225
csi "github.com/container-storage-interface/spec/lib/go/csi/v0"
2326
"github.com/golang/glog"
2427
"golang.org/x/net/context"
2528
"google.golang.org/grpc/codes"
2629
"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"
2833
utils "sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/pkg/utils"
2934
)
3035

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+
3159
type GCENodeServer struct {
3260
Driver *GCEDriver
33-
Mounter mountmanager.Mounter
61+
Mounter mount.SafeFormatAndMount
3462
// TODO: Only lock mutually exclusive calls and make locking more fine grained
3563
mux sync.Mutex
3664
}
@@ -71,7 +99,7 @@ func (ns *GCENodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePub
7199
return nil, nil
72100
}
73101

74-
if err := ns.Mounter.MkdirAll(targetPath, 0750); err != nil {
102+
if err := ns.Mounter.MakeDir(targetPath); err != nil {
75103
glog.Errorf("mkdir failed on disk %s (%v)", targetPath, err)
76104
return nil, err
77105
}
@@ -82,15 +110,15 @@ func (ns *GCENodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePub
82110
options = append(options, "ro")
83111
}
84112

85-
err = ns.Mounter.DoMount(stagingTargetPath, targetPath, "ext4", options)
113+
err = ns.Mounter.Mount(stagingTargetPath, targetPath, "ext4", options)
86114
if err != nil {
87115
notMnt, mntErr := ns.Mounter.IsLikelyNotMountPoint(targetPath)
88116
if mntErr != nil {
89117
glog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
90118
return nil, status.Error(codes.Internal, fmt.Sprintf("TODO: %v", err))
91119
}
92120
if !notMnt {
93-
if _, mntErr = ns.Mounter.UnmountVolume(targetPath); mntErr != nil {
121+
if mntErr = ns.Mounter.Unmount(targetPath); mntErr != nil {
94122
glog.Errorf("Failed to unmount: %v", mntErr)
95123
return nil, status.Error(codes.Internal, fmt.Sprintf("TODO: %v", err))
96124
}
@@ -105,7 +133,7 @@ func (ns *GCENodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePub
105133
return nil, status.Error(codes.Internal, fmt.Sprintf("TODO: %v", err))
106134
}
107135
}
108-
ns.Mounter.Remove(targetPath)
136+
os.Remove(targetPath)
109137
glog.Errorf("Mount of disk %s failed: %v", targetPath, err)
110138
return nil, status.Error(codes.Internal, fmt.Sprintf("TODO: %v", err))
111139
}
@@ -130,9 +158,9 @@ func (ns *GCENodeServer) NodeUnpublishVolume(ctx context.Context, req *csi.NodeU
130158

131159
// TODO: Check volume still exists
132160

133-
output, err := ns.Mounter.UnmountVolume(targetPath)
161+
err := ns.Mounter.Unmount(targetPath)
134162
if err != nil {
135-
return nil, status.Error(codes.Internal, fmt.Sprintf("Unmount failed: %v\nUnmounting arguments: %s\nOutput: %s\n", err, targetPath, output))
163+
return nil, status.Error(codes.Internal, fmt.Sprintf("Unmount failed: %v\nUnmounting arguments: %s\n", err, targetPath))
136164
}
137165

138166
return &csi.NodeUnpublishVolumeResponse{}, nil
@@ -168,8 +196,8 @@ func (ns *GCENodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStage
168196
// TODO: Get real partitions
169197
partition := ""
170198

171-
devicePaths := ns.Mounter.GetDiskByIdPaths(volumeName, partition)
172-
devicePath, err := ns.Mounter.VerifyDevicePath(devicePaths)
199+
devicePaths := getDiskByIdPaths(volumeName, partition)
200+
devicePath, err := verifyDevicePath(devicePaths)
173201

174202
if err != nil {
175203
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
184212
notMnt, err := ns.Mounter.IsLikelyNotMountPoint(stagingTargetPath)
185213
if err != nil {
186214
if os.IsNotExist(err) {
187-
if err := ns.Mounter.MkdirAll(stagingTargetPath, 0750); err != nil {
215+
if err := ns.Mounter.MakeDir(stagingTargetPath); err != nil {
188216
return nil, status.Error(codes.Internal, fmt.Sprintf("Failed to create directory (%q): %v", stagingTargetPath, err))
189217
}
190218
notMnt = true
@@ -242,7 +270,7 @@ func (ns *GCENodeServer) NodeUnstageVolume(ctx context.Context, req *csi.NodeUns
242270
return nil, status.Error(codes.InvalidArgument, "NodeUnstageVolume Staging Target Path must be provided")
243271
}
244272

245-
_, err := ns.Mounter.UnmountVolume(stagingTargetPath)
273+
err := ns.Mounter.Unmount(stagingTargetPath)
246274
if err != nil {
247275
return nil, status.Error(codes.Internal, fmt.Sprintf("NodeUnstageVolume failed to unmount at path %s: %v", stagingTargetPath, err))
248276
}
@@ -277,3 +305,107 @@ func (ns *GCENodeServer) NodeGetInfo(ctx context.Context, req *csi.NodeGetInfoRe
277305
}
278306
return resp, nil
279307
}
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+
}

pkg/mount-manager/fake-mounter.go

-66
This file was deleted.

0 commit comments

Comments
 (0)