Skip to content

Commit a5f7b2c

Browse files
authored
fix(driver): get last partition of specific device (#32)
- get last partition of specific device - add more tests
1 parent e4c3ac6 commit a5f7b2c

File tree

6 files changed

+211
-90
lines changed

6 files changed

+211
-90
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
cmd/upcloud-csi-plugin/upcloud-csi-plugin
22
/go.work
33
/go.work.sum
4-
.vscode
4+
.vscode
5+
temp/

driver/healthcheck.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ func NewHealthChecker(checks ...HealthCheck) *HealthChecker {
3333
func (c *HealthChecker) Check(ctx context.Context) error {
3434
var eg errgroup.Group
3535

36-
for _, check := range c.checks {
36+
for _, c := range c.checks {
37+
check := c
3738
eg.Go(func() error {
3839
return check.Check(ctx)
3940
})

driver/mounter.go

Lines changed: 52 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
package driver
22

33
import (
4-
"bytes"
54
"encoding/json"
65
"errors"
76
"fmt"
87
"io"
9-
"k8s.io/mount-utils"
108
"os"
119
"os/exec"
1210
"strings"
1311
"syscall"
1412

13+
"k8s.io/mount-utils"
14+
1515
"github.com/sirupsen/logrus"
1616
"golang.org/x/sys/unix"
1717
)
@@ -91,16 +91,17 @@ func (m *mounter) Format(source, fsType string, mkfsArgs []string) error {
9191
return errors.New("source is not specified for formatting the volume")
9292
}
9393

94-
m.log.Infof("source: %s", source)
95-
96-
m.log.Info("create partition called")
97-
err := createPartition(source)
94+
m.log.WithFields(logrus.Fields{"source": source}).Info("create partition called")
95+
err := m.createPartition(source)
9896
if err != nil {
9997
return err
10098
}
10199

102-
m.log.Info("get last partition called")
103-
lastPartition := getLastPartition()
100+
m.log.WithFields(logrus.Fields{"source": source}).Info("get last partition called")
101+
lastPartition, err := getLastPartition(source)
102+
if err != nil {
103+
return err
104+
}
104105

105106
if fsType == "ext4" || fsType == "ext3" {
106107
mkfsArgs = append(mkfsArgs, "-F", lastPartition)
@@ -117,8 +118,9 @@ func (m *mounter) Format(source, fsType string, mkfsArgs []string) error {
117118
}
118119

119120
m.log.WithFields(logrus.Fields{
120-
"cmd": mkfsCmd,
121-
"args": mkfsArgs,
121+
"cmd": mkfsCmd,
122+
"args": mkfsArgs,
123+
"partition": lastPartition,
122124
}).Info("executing format command")
123125

124126
out, err := exec.Command(mkfsCmd, mkfsArgs...).CombinedOutput()
@@ -176,11 +178,16 @@ func (m *mounter) Mount(source, target, fsType string, opts ...string) error {
176178
}
177179

178180
func (m *mounter) Unmount(target string) error {
179-
umountCmd := "umount"
180181
if target == "" {
181182
return errors.New("target is not specified for unmounting the volume")
182183
}
183184

185+
if _, err := os.Stat(target); os.IsNotExist(err) {
186+
m.log.WithFields(logrus.Fields{"target": target}).Info("unmount target does not exist")
187+
return nil
188+
}
189+
190+
umountCmd := "umount"
184191
umountArgs := []string{target}
185192

186193
m.log.WithFields(logrus.Fields{
@@ -377,75 +384,49 @@ func (m *mounter) GetDeviceName(mounter mount.Interface, mountPath string) (stri
377384
return devicePath, err
378385
}
379386

380-
func getLastPartition() string {
381-
sfdisk := exec.Command("sfdisk", "-q", "--list")
382-
awk := exec.Command("awk", "NR>1{print $1}")
387+
func (m *mounter) createPartition(device string) error {
383388

384-
r, w := io.Pipe()
385-
sfdisk.Stdout = w
386-
awk.Stdin = r
387-
388-
var buf bytes.Buffer
389-
awk.Stdout = &buf
390-
391-
sfdisk.Start()
392-
awk.Start()
393-
sfdisk.Wait()
394-
w.Close()
395-
awk.Wait()
396-
397-
out := buf.String()
398-
partitions := strings.Split(out, "\n")
399-
400-
var lastPartition string
401-
for i := len(partitions) - 1; i > 0; i-- {
402-
if !strings.HasPrefix(partitions[i], "/dev") {
403-
continue
404-
} else {
405-
lastPartition = partitions[i]
406-
break
407-
}
389+
partedMklabel := exec.Command("parted", device, "mklabel", "gpt")
390+
partedMklabelOut, err := partedMklabel.CombinedOutput()
391+
if err != nil {
392+
return err
408393
}
394+
m.log.WithFields(logrus.Fields{
395+
"cmd": partedMklabel.String(),
396+
"output": string(partedMklabelOut)},
397+
).Info("created new disklabel")
409398

410-
return lastPartition
411-
}
412-
413-
func getLastDevice() string {
414-
lsblk := exec.Command("lsblk", "-dp")
415-
awk := exec.Command("awk", "NR>1{print $1}")
416-
417-
r, w := io.Pipe()
418-
lsblk.Stdout = w
419-
awk.Stdin = r
420-
421-
var buf bytes.Buffer
422-
awk.Stdout = &buf
423-
424-
lsblk.Start()
425-
awk.Start()
426-
lsblk.Wait()
427-
w.Close()
428-
awk.Wait()
399+
partedCreatePartition := exec.Command("parted", "-a", "opt", device, "mkpart", "primary", "2048s", "100%")
400+
partedCreatePartitionOut, err := partedCreatePartition.CombinedOutput()
401+
if err != nil {
402+
return err
403+
}
429404

430-
out := buf.String()
431-
disks := strings.Split(out, "\n")
432-
lastDevice := disks[len(disks)-1]
405+
m.log.WithFields(logrus.Fields{
406+
"cmd": partedCreatePartition.String(),
407+
"output": string(partedCreatePartitionOut)},
408+
).Info("created new primary partition")
433409

434-
return lastDevice
410+
return nil
435411
}
436412

437-
func createPartition(device string) error {
438-
partedMklabelOut, err := exec.Command("parted", device, "mklabel", "gpt").CombinedOutput()
413+
func getLastPartition(source string) (string, error) {
414+
sfdisk, err := exec.Command("sfdisk", "-q", "--list", "-o", "device", source).CombinedOutput()
439415
if err != nil {
440-
return err
416+
return "", err
441417
}
442-
fmt.Printf("parted mklabel output: %s\n", partedMklabelOut)
418+
return sfdiskOutputGetLastPartition(source, string(sfdisk))
419+
}
443420

444-
partedCreatePartitionOut, err := exec.Command("parted", "-a", "opt", device, "mkpart", "primary", "2048s", "100%").CombinedOutput()
445-
if err != nil {
446-
return err
421+
func sfdiskOutputGetLastPartition(source, sfdiskOutput string) (string, error) {
422+
outLines := strings.Split(sfdiskOutput, "\n")
423+
var lastPartition string
424+
for i := len(outLines) - 1; i >= 0; i-- {
425+
partition := strings.TrimSpace(outLines[i])
426+
if strings.HasPrefix(partition, source) {
427+
lastPartition = partition
428+
break
429+
}
447430
}
448-
fmt.Printf("mkpart output: %s\n", partedCreatePartitionOut)
449-
450-
return nil
431+
return lastPartition, nil
451432
}

driver/mounter_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package driver
2+
3+
import "testing"
4+
5+
func TestSfdiskOutputGetLastPartition(t *testing.T) {
6+
outputMultiple := `
7+
Device
8+
/dev/vda1
9+
/dev/vda2
10+
/dev/vda3
11+
`
12+
outputSingle := `
13+
Device
14+
/dev/vda1
15+
`
16+
outputNone := `
17+
Device
18+
`
19+
want := "/dev/vda3"
20+
got, _ := sfdiskOutputGetLastPartition("/dev/vda", outputMultiple)
21+
if want != got {
22+
t.Errorf("sfdiskOutputGetLastPartition failed want %s got %s", want, got)
23+
}
24+
25+
want = "/dev/vda1"
26+
got, _ = sfdiskOutputGetLastPartition("/dev/vda", outputSingle)
27+
if want != got {
28+
t.Errorf("sfdiskOutputGetLastPartition failed want %s got %s", want, got)
29+
}
30+
31+
want = ""
32+
got, _ = sfdiskOutputGetLastPartition("/dev/vda", outputNone)
33+
if want != got {
34+
t.Errorf("sfdiskOutputGetLastPartition failed want %s got %s", want, got)
35+
}
36+
}

driver/node.go

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,19 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
133133
}
134134

135135
if !mounted {
136-
source = getLastPartition()
136+
partition, err := getLastPartition(source)
137+
if err != nil {
138+
return nil, status.Error(codes.Internal, err.Error())
139+
}
137140
stageMountedLog := d.log.WithFields(logrus.Fields{
138-
"source": source,
139-
"target": target,
140-
"fsType": fsType,
141-
"options": options,
141+
"source": source,
142+
"partition": partition,
143+
"target": target,
144+
"fsType": fsType,
145+
"options": options,
142146
})
143147
stageMountedLog.Info("mount options")
144-
if err := d.mounter.Mount(source, target, fsType, options...); err != nil {
148+
if err := d.mounter.Mount(partition, target, fsType, options...); err != nil {
145149
return nil, status.Error(codes.Internal, err.Error())
146150
}
147151
} else {
@@ -289,21 +293,21 @@ func (d *Driver) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpublish
289293
// NodeGetCapabilities returns the supported capabilities of the node server
290294
func (d *Driver) NodeGetCapabilities(ctx context.Context, req *csi.NodeGetCapabilitiesRequest) (*csi.NodeGetCapabilitiesResponse, error) {
291295
nscaps := []*csi.NodeServiceCapability{
292-
&csi.NodeServiceCapability{
296+
{
293297
Type: &csi.NodeServiceCapability_Rpc{
294298
Rpc: &csi.NodeServiceCapability_RPC{
295299
Type: csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME,
296300
},
297301
},
298302
},
299-
&csi.NodeServiceCapability{
303+
{
300304
Type: &csi.NodeServiceCapability_Rpc{
301305
Rpc: &csi.NodeServiceCapability_RPC{
302306
Type: csi.NodeServiceCapability_RPC_EXPAND_VOLUME,
303307
},
304308
},
305309
},
306-
&csi.NodeServiceCapability{
310+
{
307311
Type: &csi.NodeServiceCapability_Rpc{
308312
Rpc: &csi.NodeServiceCapability_RPC{
309313
Type: csi.NodeServiceCapability_RPC_GET_VOLUME_STATS,
@@ -377,13 +381,13 @@ func (d *Driver) NodeGetVolumeStats(ctx context.Context, req *csi.NodeGetVolumeS
377381

378382
return &csi.NodeGetVolumeStatsResponse{
379383
Usage: []*csi.VolumeUsage{
380-
&csi.VolumeUsage{
384+
{
381385
Available: stats.availableBytes,
382386
Total: stats.totalBytes,
383387
Used: stats.usedBytes,
384388
Unit: csi.VolumeUsage_BYTES,
385389
},
386-
&csi.VolumeUsage{
390+
{
387391
Available: stats.availableInodes,
388392
Total: stats.totalInodes,
389393
Used: stats.usedInodes,
@@ -399,6 +403,7 @@ func (d *Driver) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandVolume
399403
return nil, status.Error(codes.InvalidArgument, "NodeExpandVolume volume ID not provided")
400404
}
401405

406+
source := d.getDiskSource(req.VolumeId)
402407
volumePath := req.GetVolumePath()
403408
if len(volumePath) == 0 {
404409
return nil, status.Error(codes.InvalidArgument, "NodeExpandVolume volume path not provided")
@@ -413,6 +418,7 @@ func (d *Driver) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandVolume
413418
"volume_id": req.VolumeId,
414419
"volume_path": req.VolumePath,
415420
"method": "node_expand_volume",
421+
"source": source,
416422
})
417423
log.Info("node expand volume called")
418424

@@ -427,7 +433,12 @@ func (d *Driver) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandVolume
427433
return nil, err
428434
}
429435

430-
if err = d.mounter.Mount(getLastPartition(), stagingPath, "ext4"); err != nil {
436+
lastPartition, err := getLastPartition(source)
437+
if err != nil {
438+
return nil, err
439+
}
440+
441+
if err = d.mounter.Mount(lastPartition, stagingPath, "ext4"); err != nil {
431442
return nil, err
432443
}
433444

@@ -442,17 +453,33 @@ func (d *Driver) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandVolume
442453

443454
// getDiskSource returns the absolute path of the attached volume for the given volumeID
444455
func (d *Driver) getDiskSource(volumeID string) string {
445-
fullId := strings.Join(strings.Split(volumeID, "-"), "")
446-
if len(fullId) <= 20 {
456+
diskID := volumeIDToDiskID(volumeID)
457+
if diskID == "" {
447458
return ""
448459
}
460+
return getDiskByID(diskID, "")
461+
}
449462

450-
link, err := os.Readlink(filepath.Join(diskIDPath, diskPrefix+fullId[:20]))
463+
func getDiskByID(diskID, basePath string) string {
464+
if basePath == "" {
465+
basePath = diskIDPath
466+
}
467+
link, err := os.Readlink(filepath.Join(basePath, diskPrefix+diskID))
451468
if err != nil {
452469
fmt.Println(fmt.Errorf("failed to get the link to source"))
453470
return ""
454471
}
455-
source := "/dev" + strings.TrimPrefix(link, "../..")
472+
if filepath.IsAbs(link) {
473+
return link
474+
}
475+
476+
return filepath.Join(basePath, link)
477+
}
456478

457-
return source
479+
func volumeIDToDiskID(volumeID string) string {
480+
fullId := strings.Join(strings.Split(volumeID, "-"), "")
481+
if len(fullId) <= 20 {
482+
return ""
483+
}
484+
return fullId[:20]
458485
}

0 commit comments

Comments
 (0)