Skip to content

Commit 3f690ec

Browse files
committed
Some changes based on code review. Will squash before merge. Add e2e test for block volume resize. Add nodeExpand validation
1 parent 7713644 commit 3f690ec

File tree

8 files changed

+278
-27
lines changed

8 files changed

+278
-27
lines changed

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

+7
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,13 @@ func (gceIdentity *GCEIdentityServer) GetPluginCapabilities(ctx context.Context,
6666
},
6767
},
6868
},
69+
{
70+
Type: &csi.PluginCapability_VolumeExpansion_{
71+
VolumeExpansion: &csi.PluginCapability_VolumeExpansion{
72+
Type: csi.PluginCapability_VolumeExpansion_OFFLINE,
73+
},
74+
},
75+
},
6976
},
7077
}, nil
7178
}

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

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ func TestGetPluginCapabilities(t *testing.T) {
6262
if capability.GetVolumeExpansion() != nil {
6363
switch capability.GetVolumeExpansion().GetType() {
6464
case csi.PluginCapability_VolumeExpansion_ONLINE:
65+
case csi.PluginCapability_VolumeExpansion_OFFLINE:
6566
default:
6667
t.Fatalf("Unknown capability: %v", capability.GetVolumeExpansion().GetType())
6768
}

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

+68-3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package gceGCEDriver
1717
import (
1818
"fmt"
1919
"os"
20+
"strconv"
2021
"strings"
2122
"sync"
2223

@@ -377,23 +378,55 @@ func (ns *GCENodeServer) NodeExpandVolume(ctx context.Context, req *csi.NodeExpa
377378
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("ControllerExpandVolume capacity range is invalid: %v", err))
378379
}
379380

381+
volumePath := req.GetVolumePath()
382+
if len(volumePath) == 0 {
383+
return nil, status.Error(codes.InvalidArgument, "ControllerExpandVolume volume path must be provided")
384+
}
385+
380386
volKey, err := common.VolumeIDToKey(volumeID)
381387
if err != nil {
382388
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("ControllerExpandVolume volume ID is invalid: %v", err))
383389
}
384390

385391
devicePath, err := ns.getDevicePath(volumeID, "")
386392
if err != nil {
387-
return nil, status.Error(codes.Internal, fmt.Sprintf("Error when getting device path for %s: %v", volumeID, err))
393+
return nil, status.Error(codes.Internal, fmt.Sprintf("ControllerExpandVolume error when getting device path for %s: %v", volumeID, err))
388394
}
389395

390396
resizer := resizefs.NewResizeFs(ns.Mounter)
391-
resized, err := resizer.Resize(devicePath, "")
397+
resized, err := resizer.Resize(devicePath, volumePath)
392398
if err != nil {
393-
return nil, status.Error(codes.Internal, fmt.Sprintf("Error when resizing volume %s: %v", volKey.String(), err))
399+
return nil, status.Error(codes.Internal, fmt.Sprintf("ControllerExpandVolume error when resizing volume %s: %v", volKey.String(), err))
394400

395401
}
396402

403+
// Verify the resize actually happened.
404+
format, err := ns.Mounter.GetDiskFormat(devicePath)
405+
if err != nil {
406+
return nil, status.Error(codes.Internal, fmt.Sprintf("ControllerExpandVolume error checking format for device %s: %v", devicePath, err))
407+
}
408+
if format == "" {
409+
// Block volume
410+
gotSizeGb, err := ns.getBlockSizeGb(devicePath)
411+
if err != nil {
412+
return nil, status.Error(codes.Internal, fmt.Sprintf("ControllerExpandVolume error when getting size of block volume at path %s: %v", devicePath, err))
413+
}
414+
if gotSizeGb != common.BytesToGb(reqBytes) {
415+
return nil, status.Errorf(codes.Internal, "ControllerExpandVolume resize requested for %v but after resize volume was size %v", common.BytesToGb(reqBytes), gotSizeGb)
416+
}
417+
} else {
418+
// Some sort of formatted volume
419+
gotSizeGb, err := ns.getFSSizeGb(devicePath)
420+
421+
if err != nil {
422+
return nil, status.Errorf(codes.Internal, "ControllerExpandVolume resize could not get fs size of %s: %v", volumePath, err)
423+
}
424+
if gotSizeGb != common.BytesToGb(reqBytes) {
425+
return nil, status.Errorf(codes.Internal, "ControllerExpandVolume resize requested for size %v but after resize volume was size %v", common.BytesToGb(reqBytes), gotSizeGb)
426+
}
427+
}
428+
429+
// Respond
397430
resp := &csi.NodeExpandVolumeResponse{}
398431
if resized {
399432
resp.CapacityBytes = reqBytes
@@ -435,3 +468,35 @@ func (ns *GCENodeServer) getDevicePath(volumeID string, partition string) (strin
435468
}
436469
return devicePath, nil
437470
}
471+
472+
func (ns *GCENodeServer) getBlockSizeGb(devicePath string) (int64, error) {
473+
output, err := ns.Mounter.Exec.Run("blockdev", "--getsize64", devicePath)
474+
if err != nil {
475+
return -1, fmt.Errorf("error when getting size of block volume at path %s: output: %s, err: %v", devicePath, string(output), err)
476+
}
477+
strOut := strings.TrimSpace(string(output))
478+
gotSizeBytes, err := strconv.ParseInt(strOut, 10, 64)
479+
if err != nil {
480+
return -1, fmt.Errorf("failed to parse size %s into int a size", strOut)
481+
}
482+
return common.BytesToGb(gotSizeBytes), nil
483+
}
484+
485+
func (ns *GCENodeServer) getFSSizeGb(volumePath string) (int64, error) {
486+
o, err := ns.Mounter.Exec.Run("df", "--output=size", "-BG", volumePath)
487+
if err != nil {
488+
return -1, fmt.Errorf("failed to run command, output: %s, err: %v", string(o), err)
489+
}
490+
491+
tokens := strings.Split(strings.TrimSpace(string(o)), "\n")
492+
if len(tokens) != 2 {
493+
return -1, fmt.Errorf("got unexpected df output: %s", string(o))
494+
}
495+
496+
output := strings.TrimSuffix(strings.TrimSpace(tokens[1]), "G")
497+
n, err := strconv.ParseInt(output, 10, 64)
498+
if err != nil {
499+
return -1, fmt.Errorf("failed to parse %s into int", output)
500+
}
501+
return n, nil
502+
}

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

+40-11
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package gceGCEDriver
1717
import (
1818
"errors"
1919
"fmt"
20+
"strconv"
2021
"testing"
2122

2223
"context"
@@ -26,6 +27,7 @@ import (
2627
"google.golang.org/grpc/status"
2728
"k8s.io/kubernetes/pkg/util/mount"
2829
utilexec "k8s.io/utils/exec"
30+
"sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/pkg/common"
2931
metadataservice "sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/pkg/gce-cloud-provider/metadata"
3032
mountmanager "sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/pkg/mount-manager"
3133
)
@@ -348,7 +350,7 @@ func TestNodeExpandVolume(t *testing.T) {
348350
testCases := []struct {
349351
name string
350352
req *csi.NodeExpandVolumeRequest
351-
blockDevice bool
353+
fsOrBlock string
352354
expRespBytes int64
353355
expErrCode codes.Code
354356
}{
@@ -361,7 +363,7 @@ func TestNodeExpandVolume(t *testing.T) {
361363
RequiredBytes: resizedBytes,
362364
},
363365
},
364-
blockDevice: false,
366+
fsOrBlock: "ext4",
365367
expRespBytes: resizedBytes,
366368
},
367369
{
@@ -373,29 +375,56 @@ func TestNodeExpandVolume(t *testing.T) {
373375
RequiredBytes: resizedBytes,
374376
},
375377
},
376-
blockDevice: true,
378+
fsOrBlock: "block",
379+
},
380+
{
381+
name: "xfs fs expand",
382+
req: &csi.NodeExpandVolumeRequest{
383+
VolumeId: volumeID,
384+
VolumePath: "some-path",
385+
CapacityRange: &csi.CapacityRange{
386+
RequiredBytes: resizedBytes,
387+
},
388+
},
389+
fsOrBlock: "xfs",
390+
expRespBytes: resizedBytes,
377391
},
378392
}
379393
for _, tc := range testCases {
380394
t.Logf("Test case: %s", tc.name)
381395

382396
execCallback := func(cmd string, args ...string) ([]byte, error) {
383-
if cmd == "blkid" {
384-
if tc.blockDevice {
397+
switch cmd {
398+
case "blkid":
399+
if tc.fsOrBlock == "block" {
385400
// blkid returns exit code 2 when run on unformatted device
386401
return nil, utilexec.CodeExitError{
387402
Err: errors.New("this is an exit error"),
388403
Code: 2,
389404
}
390-
} else {
391-
return []byte("DEVNAME=/dev/sdb\nTYPE=ext4"), nil
392405
}
393-
} else if cmd == "resize2fs" {
394-
if tc.blockDevice {
395-
t.Fatalf("resize fs called on block device")
406+
return []byte(fmt.Sprintf("DEVNAME=/dev/sdb\nTYPE=%s", tc.fsOrBlock)), nil
407+
case "resize2fs":
408+
if tc.fsOrBlock == "ext4" {
409+
return nil, nil
410+
}
411+
t.Fatalf("resize fs called on device with %s", tc.fsOrBlock)
412+
case "xfs_growfs":
413+
if tc.fsOrBlock != "xfs" {
414+
t.Fatalf("xfs_growfs called on device with %s", tc.fsOrBlock)
396415
}
397-
return nil, nil
416+
for _, arg := range args {
417+
if arg == tc.req.VolumePath {
418+
return nil, nil
419+
}
420+
}
421+
t.Errorf("xfs_growfs args did not contain volume path %s", tc.req.VolumePath)
422+
case "df":
423+
return []byte(fmt.Sprintf("FOO\n%v", common.BytesToGb(resizedBytes))), nil
424+
case "blockdev":
425+
return []byte(strconv.Itoa(int(resizedBytes))), nil
398426
}
427+
399428
return nil, fmt.Errorf("fake exec got unknown call to %v %v", cmd, args)
400429
}
401430
mounter := mountmanager.NewFakeSafeMounterWithCustomExec(mount.NewFakeExec(execCallback))

test/e2e/tests/multi_zone_e2e_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,9 @@ func testAttachWriteReadDetach(volID string, volName string, instance *remote.In
165165

166166
// Stage Disk
167167
stageDir := filepath.Join("/tmp/", volName, "stage")
168-
err = client.NodeStageVolume(volID, stageDir)
168+
err = client.NodeStageExt4Volume(volID, stageDir)
169169
if err != nil {
170-
return fmt.Errorf("NodeStageVolume failed with error: %v", err)
170+
return fmt.Errorf("NodeStageExt4Volume failed with error: %v", err)
171171
}
172172

173173
defer func() {

test/e2e/tests/single_zone_e2e_test.go

+112-3
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ var _ = Describe("GCE PD CSI Driver", func() {
101101

102102
})
103103

104-
It("Should resize controller and node", func() {
104+
It("Should resize controller and node for an ext4 volume", func() {
105105
testContext := getRandomTestContext()
106106

107107
p, z, _ := testContext.Instance.GetIdentity()
@@ -153,7 +153,7 @@ var _ = Describe("GCE PD CSI Driver", func() {
153153

154154
// Stage Disk
155155
stageDir := filepath.Join("/tmp/", volName, "stage")
156-
err = client.NodeStageVolume(volID, stageDir)
156+
err = client.NodeStageExt4Volume(volID, stageDir)
157157
Expect(err).To(BeNil(), "Node Stage volume failed")
158158

159159
defer func() {
@@ -199,7 +199,7 @@ var _ = Describe("GCE PD CSI Driver", func() {
199199
Expect(cloudDisk.SizeGb).To(Equal(newSizeGb))
200200

201201
// Resize node
202-
err = client.NodeExpandVolume(volID, publishDir, newSizeGb)
202+
_, err = client.NodeExpandVolume(volID, publishDir, newSizeGb)
203203
Expect(err).To(BeNil(), "Node expand volume failed")
204204

205205
// Verify disk size
@@ -209,6 +209,115 @@ var _ = Describe("GCE PD CSI Driver", func() {
209209

210210
})
211211

212+
It("Should resize controller and node for an block volume", func() {
213+
testContext := getRandomTestContext()
214+
215+
p, z, _ := testContext.Instance.GetIdentity()
216+
client := testContext.Client
217+
instance := testContext.Instance
218+
219+
// Create Disk
220+
volName := testNamePrefix + string(uuid.NewUUID())
221+
volID, err := client.CreateVolume(volName, nil, defaultSizeGb,
222+
&csi.TopologyRequirement{
223+
Requisite: []*csi.Topology{
224+
{
225+
Segments: map[string]string{common.TopologyKeyZone: z},
226+
},
227+
},
228+
})
229+
Expect(err).To(BeNil(), "CreateVolume failed with error: %v", err)
230+
231+
// Validate Disk Created
232+
cloudDisk, err := computeService.Disks.Get(p, z, volName).Do()
233+
Expect(err).To(BeNil(), "Could not get disk from cloud directly")
234+
Expect(cloudDisk.Type).To(ContainSubstring(standardDiskType))
235+
Expect(cloudDisk.Status).To(Equal(readyState))
236+
Expect(cloudDisk.SizeGb).To(Equal(defaultSizeGb))
237+
Expect(cloudDisk.Name).To(Equal(volName))
238+
239+
defer func() {
240+
// Delete Disk
241+
client.DeleteVolume(volID)
242+
Expect(err).To(BeNil(), "DeleteVolume failed")
243+
244+
// Validate Disk Deleted
245+
_, err = computeService.Disks.Get(p, z, volName).Do()
246+
Expect(gce.IsGCEError(err, "notFound")).To(BeTrue(), "Expected disk to not be found")
247+
}()
248+
249+
// Attach Disk
250+
err = client.ControllerPublishVolume(volID, instance.GetNodeID())
251+
Expect(err).To(BeNil(), "Controller publish volume failed")
252+
253+
defer func() {
254+
// Detach Disk
255+
err = client.ControllerUnpublishVolume(volID, instance.GetNodeID())
256+
if err != nil {
257+
klog.Errorf("Failed to detach disk: %v", err)
258+
}
259+
260+
}()
261+
262+
// Stage Disk
263+
stageDir := filepath.Join("/tmp/", volName, "stage")
264+
err = client.NodeStageBlockVolume(volID, stageDir)
265+
Expect(err).To(BeNil(), "Node Stage volume failed")
266+
267+
defer func() {
268+
// Unstage Disk
269+
err = client.NodeUnstageVolume(volID, stageDir)
270+
if err != nil {
271+
klog.Errorf("Failed to unstage volume: %v", err)
272+
}
273+
fp := filepath.Join("/tmp/", volName)
274+
err = testutils.RmAll(instance, fp)
275+
if err != nil {
276+
klog.Errorf("Failed to rm file path %s: %v", fp, err)
277+
}
278+
}()
279+
280+
// Mount Disk
281+
publishDir := filepath.Join("/tmp/", volName, "mount")
282+
err = client.NodePublishBlockVolume(volID, stageDir, publishDir)
283+
Expect(err).To(BeNil(), "Node publish volume failed")
284+
285+
defer func() {
286+
// Unmount Disk
287+
err = client.NodeUnpublishVolume(volID, publishDir)
288+
if err != nil {
289+
klog.Errorf("NodeUnpublishVolume failed with error: %v", err)
290+
}
291+
}()
292+
293+
// Verify pre-resize fs size
294+
sizeGb, err := testutils.GetBlockSizeInGb(instance, publishDir)
295+
Expect(err).To(BeNil(), "Failed to get block device size in GB")
296+
Expect(sizeGb).To(Equal(defaultSizeGb), "Old size should be equal")
297+
298+
// Resize controller
299+
var newSizeGb int64 = 10
300+
err = client.ControllerExpandVolume(volID, newSizeGb)
301+
302+
Expect(err).To(BeNil(), "Controller expand volume failed")
303+
304+
// Verify cloud size
305+
cloudDisk, err = computeService.Disks.Get(p, z, volName).Do()
306+
Expect(err).To(BeNil(), "Get cloud disk failed")
307+
Expect(cloudDisk.SizeGb).To(Equal(newSizeGb))
308+
309+
// Resize node
310+
resp, err := client.NodeExpandVolume(volID, publishDir, newSizeGb)
311+
Expect(err).To(BeNil(), "Node expand volume failed")
312+
Expect(resp.CapacityBytes).To(Equal(int64(0)), "Node expand should not do anything")
313+
314+
// Verify disk size
315+
sizeGb, err = testutils.GetBlockSizeInGb(instance, publishDir)
316+
Expect(err).To(BeNil(), "Failed to get block device size in GB")
317+
Expect(sizeGb).To(Equal(newSizeGb), "New size should be equal")
318+
319+
})
320+
212321
It("Should create disks in correct zones when topology is specified", func() {
213322
Expect(testContexts).ToNot(BeEmpty())
214323
testContext := getRandomTestContext()

0 commit comments

Comments
 (0)