Skip to content

Commit dd9f565

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 dd9f565

File tree

9 files changed

+304
-28
lines changed

9 files changed

+304
-28
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

+100-3
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@ limitations under the License.
1515
package gceGCEDriver
1616

1717
import (
18+
"bytes"
1819
"fmt"
20+
"io"
1921
"os"
22+
"os/exec"
23+
"strconv"
2024
"strings"
2125
"sync"
2226

@@ -377,23 +381,54 @@ func (ns *GCENodeServer) NodeExpandVolume(ctx context.Context, req *csi.NodeExpa
377381
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("ControllerExpandVolume capacity range is invalid: %v", err))
378382
}
379383

384+
volumePath := req.GetVolumePath()
385+
if len(volumePath) == 0 {
386+
return nil, status.Error(codes.InvalidArgument, "ControllerExpandVolume volume path must be provided")
387+
}
388+
380389
volKey, err := common.VolumeIDToKey(volumeID)
381390
if err != nil {
382391
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("ControllerExpandVolume volume ID is invalid: %v", err))
383392
}
384393

385394
devicePath, err := ns.getDevicePath(volumeID, "")
386395
if err != nil {
387-
return nil, status.Error(codes.Internal, fmt.Sprintf("Error when getting device path for %s: %v", volumeID, err))
396+
return nil, status.Error(codes.Internal, fmt.Sprintf("ControllerExpandVolume error when getting device path for %s: %v", volumeID, err))
388397
}
389398

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

395404
}
396405

406+
// Verify the resize actually happened.
407+
format, err := ns.Mounter.GetDiskFormat(devicePath)
408+
if err != nil {
409+
return nil, status.Error(codes.Internal, fmt.Sprintf("ControllerExpandVolume error checking format for device %s: %v", devicePath, err))
410+
}
411+
if format == "" {
412+
// Block volume
413+
gotSizeGb, err := getBlockSizeGb(devicePath)
414+
if err != nil {
415+
return nil, status.Error(codes.Internal, fmt.Sprintf("ControllerExpandVolume error when getting size of block volume at path %s: %v", devicePath, err))
416+
}
417+
if gotSizeGb != common.BytesToGb(reqBytes) {
418+
return nil, status.Errorf(codes.Internal, "ControllerExpandVolume resize requested for %v but after resize volume was size %v", common.BytesToGb(reqBytes), gotSizeGb)
419+
}
420+
} else {
421+
// Some sort of formatted volume
422+
gotSizeGb, err := getFSSizeGb(volumePath)
423+
if err != nil {
424+
return nil, status.Errorf(codes.Internal, "ControllerExpandVolume resize could not get fs size of %s: %v", volumePath, err)
425+
}
426+
if gotSizeGb != common.BytesToGb(reqBytes) {
427+
return nil, status.Errorf(codes.Internal, "ControllerExpandVolume resize requested for size %v but after resize volume was size %v", common.BytesToGb(reqBytes), gotSizeGb)
428+
}
429+
}
430+
431+
// Respond
397432
resp := &csi.NodeExpandVolumeResponse{}
398433
if resized {
399434
resp.CapacityBytes = reqBytes
@@ -435,3 +470,65 @@ func (ns *GCENodeServer) getDevicePath(volumeID string, partition string) (strin
435470
}
436471
return devicePath, nil
437472
}
473+
474+
func getBlockSizeGb(devicePath string) (int64, error) {
475+
cmd := exec.Command("blockdev", "--getsize64", devicePath)
476+
output, err := cmd.CombinedOutput()
477+
if err != nil {
478+
return -1, fmt.Errorf("error when getting size of block volume at path %s: output: %s, err: %v", devicePath, string(output), err)
479+
}
480+
strOut := strings.TrimSpace(string(output))
481+
gotSizeBytes, err := strconv.ParseInt(strOut, 10, 64)
482+
if err != nil {
483+
return -1, fmt.Errorf("failed to parse size %s into int a size", strOut)
484+
}
485+
return common.BytesToGb(gotSizeBytes), nil
486+
}
487+
488+
func getFSSizeGb(volumePath string) (int64, error) {
489+
var buf bytes.Buffer
490+
var sedErr bytes.Buffer
491+
var dfErr bytes.Buffer
492+
493+
cmderr := func(d, a bytes.Buffer, e error) error {
494+
return fmt.Errorf("df err: %v, sed err: %v, err: %v", d.String(), a.String(), e)
495+
}
496+
497+
// Generate Commands
498+
df := exec.Command("df", "--output=size", "-BG", volumePath)
499+
sed := exec.Command("sed", "-n", "2p")
500+
501+
r, w := io.Pipe()
502+
503+
// Pipe df output to sed
504+
df.Stdout = w
505+
sed.Stdin = r
506+
507+
// Get sed output and errors
508+
sed.Stdout = &buf
509+
sed.Stderr = &sedErr
510+
df.Stderr = &dfErr
511+
512+
if err := df.Start(); err != nil {
513+
return -1, cmderr(dfErr, sedErr, err)
514+
}
515+
if err := sed.Start(); err != nil {
516+
return -1, cmderr(dfErr, sedErr, err)
517+
}
518+
if err := df.Wait(); err != nil {
519+
return -1, cmderr(dfErr, sedErr, err)
520+
}
521+
if err := w.Close(); err != nil {
522+
return -1, cmderr(dfErr, sedErr, err)
523+
}
524+
if err := sed.Wait(); err != nil {
525+
return -1, cmderr(dfErr, sedErr, err)
526+
}
527+
528+
output := strings.TrimSuffix(strings.TrimSpace(buf.String()), "G")
529+
n, err := strconv.ParseInt(output, 10, 64)
530+
if err != nil {
531+
return -1, fmt.Errorf("failed to parse size %s into int", output)
532+
}
533+
return n, nil
534+
}

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

+33-11
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ func TestNodeExpandVolume(t *testing.T) {
348348
testCases := []struct {
349349
name string
350350
req *csi.NodeExpandVolumeRequest
351-
blockDevice bool
351+
fsOrBlock string
352352
expRespBytes int64
353353
expErrCode codes.Code
354354
}{
@@ -361,7 +361,7 @@ func TestNodeExpandVolume(t *testing.T) {
361361
RequiredBytes: resizedBytes,
362362
},
363363
},
364-
blockDevice: false,
364+
fsOrBlock: "ext4",
365365
expRespBytes: resizedBytes,
366366
},
367367
{
@@ -373,28 +373,50 @@ func TestNodeExpandVolume(t *testing.T) {
373373
RequiredBytes: resizedBytes,
374374
},
375375
},
376-
blockDevice: true,
376+
fsOrBlock: "block",
377+
},
378+
{
379+
name: "xfs fs expand",
380+
req: &csi.NodeExpandVolumeRequest{
381+
VolumeId: volumeID,
382+
VolumePath: "some-path",
383+
CapacityRange: &csi.CapacityRange{
384+
RequiredBytes: resizedBytes,
385+
},
386+
},
387+
fsOrBlock: "xfs",
388+
expRespBytes: resizedBytes,
377389
},
378390
}
379391
for _, tc := range testCases {
380392
t.Logf("Test case: %s", tc.name)
381393

382394
execCallback := func(cmd string, args ...string) ([]byte, error) {
383-
if cmd == "blkid" {
384-
if tc.blockDevice {
395+
switch cmd {
396+
case "blkid":
397+
if tc.fsOrBlock == "block" {
385398
// blkid returns exit code 2 when run on unformatted device
386399
return nil, utilexec.CodeExitError{
387400
Err: errors.New("this is an exit error"),
388401
Code: 2,
389402
}
390-
} else {
391-
return []byte("DEVNAME=/dev/sdb\nTYPE=ext4"), nil
392403
}
393-
} else if cmd == "resize2fs" {
394-
if tc.blockDevice {
395-
t.Fatalf("resize fs called on block device")
404+
return []byte(fmt.Sprintf("DEVNAME=/dev/sdb\nTYPE=%s", tc.fsOrBlock)), nil
405+
case "resize2fs":
406+
if tc.fsOrBlock == "ext4" {
407+
return nil, nil
408+
}
409+
t.Fatalf("resize fs called on device with %s", tc.fsOrBlock)
410+
case "xfs_growfs":
411+
if tc.fsOrBlock != "xfs" {
412+
t.Fatalf("xfs_growfs called on device with %s", tc.fsOrBlock)
413+
}
414+
for _, arg := range args {
415+
if arg == tc.req.VolumePath {
416+
return nil, nil
417+
}
396418
}
397-
return nil, nil
419+
t.Errorf("xfs_growfs args did not contain volume path %s", tc.req.VolumePath)
398420
}
399421
return nil, fmt.Errorf("fake exec got unknown call to %v %v", cmd, args)
400422
}

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)