Skip to content

Commit 23a2a74

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

File tree

9 files changed

+210
-26
lines changed

9 files changed

+210
-26
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

+6-1
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,11 @@ func (ns *GCENodeServer) NodeExpandVolume(ctx context.Context, req *csi.NodeExpa
377377
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("ControllerExpandVolume capacity range is invalid: %v", err))
378378
}
379379

380+
volumePath := req.GetVolumePath()
381+
if len(volumePath) == 0 {
382+
return nil, status.Error(codes.InvalidArgument, "ControllerExpandVolume volume path must be provided")
383+
}
384+
380385
volKey, err := common.VolumeIDToKey(volumeID)
381386
if err != nil {
382387
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("ControllerExpandVolume volume ID is invalid: %v", err))
@@ -388,7 +393,7 @@ func (ns *GCENodeServer) NodeExpandVolume(ctx context.Context, req *csi.NodeExpa
388393
}
389394

390395
resizer := resizefs.NewResizeFs(ns.Mounter)
391-
resized, err := resizer.Resize(devicePath, "")
396+
resized, err := resizer.Resize(devicePath, volumePath)
392397
if err != nil {
393398
return nil, status.Error(codes.Internal, fmt.Sprintf("Error when resizing volume %s: %v", volKey.String(), err))
394399

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()

test/e2e/utils/utils.go

+16-3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"k8s.io/klog"
3030
boskosclient "k8s.io/test-infra/boskos/client"
3131
"k8s.io/test-infra/boskos/common"
32+
utilcommon "sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/pkg/common"
3233
remote "sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/test/remote"
3334
)
3435

@@ -169,10 +170,10 @@ func ReadFile(instance *remote.InstanceInfo, filePath string) (string, error) {
169170
return output, nil
170171
}
171172

172-
func GetFSSizeInGb(instance *remote.InstanceInfo, path string) (int64, error) {
173-
output, err := instance.SSHNoSudo("df", "--output=size", "-BG", path, "|", "awk", "'NR==2'")
173+
func GetFSSizeInGb(instance *remote.InstanceInfo, mountPath string) (int64, error) {
174+
output, err := instance.SSHNoSudo("df", "--output=size", "-BG", mountPath, "|", "awk", "'NR==2'")
174175
if err != nil {
175-
return -1, fmt.Errorf("failed to get size of path %s. Output: %v, error: %v", path, output, err)
176+
return -1, fmt.Errorf("failed to get size of path %s. Output: %v, error: %v", mountPath, output, err)
176177
}
177178
output = strings.TrimSuffix(strings.TrimSpace(output), "G")
178179
n, err := strconv.ParseInt(output, 10, 64)
@@ -182,6 +183,18 @@ func GetFSSizeInGb(instance *remote.InstanceInfo, path string) (int64, error) {
182183
return n, nil
183184
}
184185

186+
func GetBlockSizeInGb(instance *remote.InstanceInfo, devicePath string) (int64, error) {
187+
output, err := instance.SSH("blockdev", "--getsize64", devicePath)
188+
if err != nil {
189+
return -1, fmt.Errorf("failed to get size of path %s. Output: %v, error: %v", devicePath, output, err)
190+
}
191+
n, err := strconv.ParseInt(strings.TrimSpace(output), 10, 64)
192+
if err != nil {
193+
return -1, fmt.Errorf("failed to parse size %s into int", output)
194+
}
195+
return utilcommon.BytesToGb(n), nil
196+
}
197+
185198
func RmAll(instance *remote.InstanceInfo, filePath string) error {
186199
output, err := instance.SSH("rm", "-rf", filePath)
187200
if err != nil {

test/remote/client-wrappers.go

+32-5
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ var (
3636
Mode: csipb.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
3737
},
3838
}
39+
blockVolCap = &csipb.VolumeCapability{
40+
AccessType: &csipb.VolumeCapability_Block{
41+
Block: &csipb.VolumeCapability_BlockVolume{},
42+
},
43+
AccessMode: &csipb.VolumeCapability_AccessMode{
44+
Mode: csipb.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
45+
},
46+
}
3947
stdVolCaps = []*csipb.VolumeCapability{
4048
stdVolCap,
4149
}
@@ -139,11 +147,19 @@ func (c *CsiClient) ControllerUnpublishVolume(volId, nodeId string) error {
139147
return err
140148
}
141149

142-
func (c *CsiClient) NodeStageVolume(volId, stageDir string) error {
150+
func (c *CsiClient) NodeStageExt4Volume(volId, stageDir string) error {
151+
return c.NodeStageVolume(volId, stageDir, stdVolCap)
152+
}
153+
154+
func (c *CsiClient) NodeStageBlockVolume(volId, stageDir string) error {
155+
return c.NodeStageVolume(volId, stageDir, blockVolCap)
156+
}
157+
158+
func (c *CsiClient) NodeStageVolume(volId, stageDir string, volumeCap *csipb.VolumeCapability) error {
143159
nodeStageReq := &csipb.NodeStageVolumeRequest{
144160
VolumeId: volId,
145161
StagingTargetPath: stageDir,
146-
VolumeCapability: stdVolCap,
162+
VolumeCapability: volumeCap,
147163
}
148164
_, err := c.nodeClient.NodeStageVolume(context.Background(), nodeStageReq)
149165
return err
@@ -179,6 +195,18 @@ func (c *CsiClient) NodePublishVolume(volumeId, stageDir, publishDir string) err
179195
return err
180196
}
181197

198+
func (c *CsiClient) NodePublishBlockVolume(volumeId, stageDir, publishDir string) error {
199+
nodePublishReq := &csipb.NodePublishVolumeRequest{
200+
VolumeId: volumeId,
201+
StagingTargetPath: stageDir,
202+
TargetPath: publishDir,
203+
VolumeCapability: blockVolCap,
204+
Readonly: false,
205+
}
206+
_, err := c.nodeClient.NodePublishVolume(context.Background(), nodePublishReq)
207+
return err
208+
}
209+
182210
func (c *CsiClient) ControllerExpandVolume(volumeId string, sizeGb int64) error {
183211
controllerExpandReq := &csipb.ControllerExpandVolumeRequest{
184212
VolumeId: volumeId,
@@ -190,16 +218,15 @@ func (c *CsiClient) ControllerExpandVolume(volumeId string, sizeGb int64) error
190218
return err
191219
}
192220

193-
func (c *CsiClient) NodeExpandVolume(volumeId, volumePath string, sizeGb int64) error {
221+
func (c *CsiClient) NodeExpandVolume(volumeId, volumePath string, sizeGb int64) (*csipb.NodeExpandVolumeResponse, error) {
194222
nodeExpandReq := &csipb.NodeExpandVolumeRequest{
195223
VolumeId: volumeId,
196224
VolumePath: volumePath,
197225
CapacityRange: &csipb.CapacityRange{
198226
RequiredBytes: common.GbToBytes(sizeGb),
199227
},
200228
}
201-
_, err := c.nodeClient.NodeExpandVolume(context.Background(), nodeExpandReq)
202-
return err
229+
return c.nodeClient.NodeExpandVolume(context.Background(), nodeExpandReq)
203230
}
204231

205232
func (c *CsiClient) NodeGetInfo() (*csipb.NodeGetInfoResponse, error) {

test/run-e2e-local.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ set -o errexit
55

66
readonly PKGDIR=sigs.k8s.io/gcp-compute-persistent-disk-csi-driver
77

8-
ginkgo --v "test/e2e/tests" -- --project ${PROJECT} --service-account ${IAM_NAME} --v=4 --logtostderr
8+
ginkgo --focus="resize\scontroller\sand\snode\sfor\san\sblock\svolume" --v "test/e2e/tests" -- --project ${PROJECT} --service-account ${IAM_NAME} --v=4 --logtostderr

0 commit comments

Comments
 (0)