From b3db3b1939e3e2c1fc02ecc5f7f491e16d04a845 Mon Sep 17 00:00:00 2001 From: Xiang Li Date: Thu, 18 Feb 2021 15:07:06 -0800 Subject: [PATCH] restore snapshot to larger size --- pkg/driver/mocks/mock_mounter.go | 227 ++++++++++++++++--------------- pkg/driver/mount.go | 140 ++++++++++++++++++- pkg/driver/node.go | 24 +++- pkg/driver/node_test.go | 41 ++++++ pkg/driver/sanity_test.go | 4 + 5 files changed, 323 insertions(+), 113 deletions(-) diff --git a/pkg/driver/mocks/mock_mounter.go b/pkg/driver/mocks/mock_mounter.go index 1a8fd12608..d4bb692206 100644 --- a/pkg/driver/mocks/mock_mounter.go +++ b/pkg/driver/mocks/mock_mounter.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/driver (interfaces: Mounter) +// Source: ./pkg/driver/mount.go // Package mocks is a generated GoMock package. package mocks @@ -35,201 +35,216 @@ func (m *MockMounter) EXPECT() *MockMounterMockRecorder { return m.recorder } -// Command mocks base method -func (m *MockMounter) Command(arg0 string, arg1 ...string) exec.Cmd { +// Mount mocks base method +func (m *MockMounter) Mount(source, target, fstype string, options []string) error { m.ctrl.T.Helper() - varargs := []interface{}{arg0} - for _, a := range arg1 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "Command", varargs...) - ret0, _ := ret[0].(exec.Cmd) + ret := m.ctrl.Call(m, "Mount", source, target, fstype, options) + ret0, _ := ret[0].(error) return ret0 } -// Command indicates an expected call of Command -func (mr *MockMounterMockRecorder) Command(arg0 interface{}, arg1 ...interface{}) *gomock.Call { +// Mount indicates an expected call of Mount +func (mr *MockMounterMockRecorder) Mount(source, target, fstype, options interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0}, arg1...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Command", reflect.TypeOf((*MockMounter)(nil).Command), varargs...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Mount", reflect.TypeOf((*MockMounter)(nil).Mount), source, target, fstype, options) } -// CommandContext mocks base method -func (m *MockMounter) CommandContext(arg0 context.Context, arg1 string, arg2 ...string) exec.Cmd { +// Unmount mocks base method +func (m *MockMounter) Unmount(target string) error { m.ctrl.T.Helper() - varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "CommandContext", varargs...) - ret0, _ := ret[0].(exec.Cmd) + ret := m.ctrl.Call(m, "Unmount", target) + ret0, _ := ret[0].(error) return ret0 } -// CommandContext indicates an expected call of CommandContext -func (mr *MockMounterMockRecorder) CommandContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { +// Unmount indicates an expected call of Unmount +func (mr *MockMounterMockRecorder) Unmount(target interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1}, arg2...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommandContext", reflect.TypeOf((*MockMounter)(nil).CommandContext), varargs...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unmount", reflect.TypeOf((*MockMounter)(nil).Unmount), target) } -// ExistsPath mocks base method -func (m *MockMounter) ExistsPath(arg0 string) (bool, error) { +// List mocks base method +func (m *MockMounter) List() ([]mount.MountPoint, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ExistsPath", arg0) - ret0, _ := ret[0].(bool) + ret := m.ctrl.Call(m, "List") + ret0, _ := ret[0].([]mount.MountPoint) ret1, _ := ret[1].(error) return ret0, ret1 } -// ExistsPath indicates an expected call of ExistsPath -func (mr *MockMounterMockRecorder) ExistsPath(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExistsPath", reflect.TypeOf((*MockMounter)(nil).ExistsPath), arg0) -} - -// FormatAndMount mocks base method -func (m *MockMounter) FormatAndMount(arg0, arg1, arg2 string, arg3 []string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FormatAndMount", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(error) - return ret0 -} - -// FormatAndMount indicates an expected call of FormatAndMount -func (mr *MockMounterMockRecorder) FormatAndMount(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { +// List indicates an expected call of List +func (mr *MockMounterMockRecorder) List() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FormatAndMount", reflect.TypeOf((*MockMounter)(nil).FormatAndMount), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockMounter)(nil).List)) } -// GetDeviceName mocks base method -func (m *MockMounter) GetDeviceName(arg0 string) (string, int, error) { +// IsLikelyNotMountPoint mocks base method +func (m *MockMounter) IsLikelyNotMountPoint(file string) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetDeviceName", arg0) - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(int) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 + ret := m.ctrl.Call(m, "IsLikelyNotMountPoint", file) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 } -// GetDeviceName indicates an expected call of GetDeviceName -func (mr *MockMounterMockRecorder) GetDeviceName(arg0 interface{}) *gomock.Call { +// IsLikelyNotMountPoint indicates an expected call of IsLikelyNotMountPoint +func (mr *MockMounterMockRecorder) IsLikelyNotMountPoint(file interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeviceName", reflect.TypeOf((*MockMounter)(nil).GetDeviceName), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsLikelyNotMountPoint", reflect.TypeOf((*MockMounter)(nil).IsLikelyNotMountPoint), file) } // GetMountRefs mocks base method -func (m *MockMounter) GetMountRefs(arg0 string) ([]string, error) { +func (m *MockMounter) GetMountRefs(pathname string) ([]string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetMountRefs", arg0) + ret := m.ctrl.Call(m, "GetMountRefs", pathname) ret0, _ := ret[0].([]string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMountRefs indicates an expected call of GetMountRefs -func (mr *MockMounterMockRecorder) GetMountRefs(arg0 interface{}) *gomock.Call { +func (mr *MockMounterMockRecorder) GetMountRefs(pathname interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMountRefs", reflect.TypeOf((*MockMounter)(nil).GetMountRefs), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMountRefs", reflect.TypeOf((*MockMounter)(nil).GetMountRefs), pathname) } -// IsLikelyNotMountPoint mocks base method -func (m *MockMounter) IsLikelyNotMountPoint(arg0 string) (bool, error) { +// Command mocks base method +func (m *MockMounter) Command(cmd string, args ...string) exec.Cmd { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsLikelyNotMountPoint", arg0) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 + varargs := []interface{}{cmd} + for _, a := range args { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Command", varargs...) + ret0, _ := ret[0].(exec.Cmd) + return ret0 } -// IsLikelyNotMountPoint indicates an expected call of IsLikelyNotMountPoint -func (mr *MockMounterMockRecorder) IsLikelyNotMountPoint(arg0 interface{}) *gomock.Call { +// Command indicates an expected call of Command +func (mr *MockMounterMockRecorder) Command(cmd interface{}, args ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsLikelyNotMountPoint", reflect.TypeOf((*MockMounter)(nil).IsLikelyNotMountPoint), arg0) + varargs := append([]interface{}{cmd}, args...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Command", reflect.TypeOf((*MockMounter)(nil).Command), varargs...) } -// List mocks base method -func (m *MockMounter) List() ([]mount.MountPoint, error) { +// CommandContext mocks base method +func (m *MockMounter) CommandContext(ctx context.Context, cmd string, args ...string) exec.Cmd { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List") - ret0, _ := ret[0].([]mount.MountPoint) - ret1, _ := ret[1].(error) - return ret0, ret1 + varargs := []interface{}{ctx, cmd} + for _, a := range args { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CommandContext", varargs...) + ret0, _ := ret[0].(exec.Cmd) + return ret0 } -// List indicates an expected call of List -func (mr *MockMounterMockRecorder) List() *gomock.Call { +// CommandContext indicates an expected call of CommandContext +func (mr *MockMounterMockRecorder) CommandContext(ctx, cmd interface{}, args ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockMounter)(nil).List)) + varargs := append([]interface{}{ctx, cmd}, args...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommandContext", reflect.TypeOf((*MockMounter)(nil).CommandContext), varargs...) } // LookPath mocks base method -func (m *MockMounter) LookPath(arg0 string) (string, error) { +func (m *MockMounter) LookPath(file string) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LookPath", arg0) + ret := m.ctrl.Call(m, "LookPath", file) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // LookPath indicates an expected call of LookPath -func (mr *MockMounterMockRecorder) LookPath(arg0 interface{}) *gomock.Call { +func (mr *MockMounterMockRecorder) LookPath(file interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LookPath", reflect.TypeOf((*MockMounter)(nil).LookPath), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LookPath", reflect.TypeOf((*MockMounter)(nil).LookPath), file) } -// MakeDir mocks base method -func (m *MockMounter) MakeDir(arg0 string) error { +// FormatAndMount mocks base method +func (m *MockMounter) FormatAndMount(source, target, fstype string, options []string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MakeDir", arg0) + ret := m.ctrl.Call(m, "FormatAndMount", source, target, fstype, options) ret0, _ := ret[0].(error) return ret0 } -// MakeDir indicates an expected call of MakeDir -func (mr *MockMounterMockRecorder) MakeDir(arg0 interface{}) *gomock.Call { +// FormatAndMount indicates an expected call of FormatAndMount +func (mr *MockMounterMockRecorder) FormatAndMount(source, target, fstype, options interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MakeDir", reflect.TypeOf((*MockMounter)(nil).MakeDir), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FormatAndMount", reflect.TypeOf((*MockMounter)(nil).FormatAndMount), source, target, fstype, options) +} + +// GetDeviceName mocks base method +func (m *MockMounter) GetDeviceName(mountPath string) (string, int, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetDeviceName", mountPath) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(int) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetDeviceName indicates an expected call of GetDeviceName +func (mr *MockMounterMockRecorder) GetDeviceName(mountPath interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeviceName", reflect.TypeOf((*MockMounter)(nil).GetDeviceName), mountPath) } // MakeFile mocks base method -func (m *MockMounter) MakeFile(arg0 string) error { +func (m *MockMounter) MakeFile(pathname string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MakeFile", arg0) + ret := m.ctrl.Call(m, "MakeFile", pathname) ret0, _ := ret[0].(error) return ret0 } // MakeFile indicates an expected call of MakeFile -func (mr *MockMounterMockRecorder) MakeFile(arg0 interface{}) *gomock.Call { +func (mr *MockMounterMockRecorder) MakeFile(pathname interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MakeFile", reflect.TypeOf((*MockMounter)(nil).MakeFile), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MakeFile", reflect.TypeOf((*MockMounter)(nil).MakeFile), pathname) } -// Mount mocks base method -func (m *MockMounter) Mount(arg0, arg1, arg2 string, arg3 []string) error { +// MakeDir mocks base method +func (m *MockMounter) MakeDir(pathname string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Mount", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "MakeDir", pathname) ret0, _ := ret[0].(error) return ret0 } -// Mount indicates an expected call of Mount -func (mr *MockMounterMockRecorder) Mount(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { +// MakeDir indicates an expected call of MakeDir +func (mr *MockMounterMockRecorder) MakeDir(pathname interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Mount", reflect.TypeOf((*MockMounter)(nil).Mount), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MakeDir", reflect.TypeOf((*MockMounter)(nil).MakeDir), pathname) } -// Unmount mocks base method -func (m *MockMounter) Unmount(arg0 string) error { +// ExistsPath mocks base method +func (m *MockMounter) ExistsPath(filename string) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Unmount", arg0) - ret0, _ := ret[0].(error) - return ret0 + ret := m.ctrl.Call(m, "ExistsPath", filename) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 } -// Unmount indicates an expected call of Unmount -func (mr *MockMounterMockRecorder) Unmount(arg0 interface{}) *gomock.Call { +// ExistsPath indicates an expected call of ExistsPath +func (mr *MockMounterMockRecorder) ExistsPath(filename interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExistsPath", reflect.TypeOf((*MockMounter)(nil).ExistsPath), filename) +} + +// NeedResize mocks base method +func (m *MockMounter) NeedResize(devicePath, deviceMountPath string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NeedResize", devicePath, deviceMountPath) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// NeedResize indicates an expected call of NeedResize +func (mr *MockMounterMockRecorder) NeedResize(devicePath, deviceMountPath interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unmount", reflect.TypeOf((*MockMounter)(nil).Unmount), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NeedResize", reflect.TypeOf((*MockMounter)(nil).NeedResize), devicePath, deviceMountPath) } diff --git a/pkg/driver/mount.go b/pkg/driver/mount.go index 4c505963b5..b0a13584dc 100644 --- a/pkg/driver/mount.go +++ b/pkg/driver/mount.go @@ -17,10 +17,13 @@ limitations under the License. package driver import ( - "os" - + "fmt" + "k8s.io/klog" "k8s.io/utils/exec" "k8s.io/utils/mount" + "os" + "strconv" + "strings" ) // Mounter is an interface for mount operations @@ -32,6 +35,7 @@ type Mounter interface { MakeFile(pathname string) error MakeDir(pathname string) error ExistsPath(filename string) (bool, error) + NeedResize(devicePath string, deviceMountPath string) (bool, error) } type NodeMounter struct { @@ -90,3 +94,135 @@ func (m *NodeMounter) ExistsPath(filename string) (bool, error) { } return true, nil } + +//TODO: use common util from vendor kubernetes/mount-util +func (m *NodeMounter) NeedResize(devicePath string, deviceMountPath string) (bool, error) { + deviceSize, err := m.getDeviceSize(devicePath) + if err != nil { + return false, err + } + var fsSize, blockSize uint64 + format, err := m.SafeFormatAndMount.GetDiskFormat(devicePath) + if err != nil { + formatErr := fmt.Errorf("ResizeFS.Resize - error checking format for device %s: %v", devicePath, err) + return false, formatErr + } + + // If disk has no format, there is no need to resize the disk because mkfs.* + // by default will use whole disk anyways. + if format == "" { + return false, nil + } + + klog.V(3).Infof("Mount.needResize - checking mounted volume %s", devicePath) + switch format { + case "ext3", "ext4": + blockSize, fsSize, err = m.getExtSize(devicePath) + klog.V(5).Infof("Ext size: filesystem size=%d, block size=%d", fsSize, blockSize) + case "xfs": + blockSize, fsSize, err = m.getXFSSize(deviceMountPath) + klog.V(5).Infof("Xfs size: filesystem size=%d, block size=%d, err=%v", fsSize, blockSize, err) + } + if err != nil { + return false, err + } + // Tolerate one block difference, just in case of rounding errors somewhere. + klog.V(5).Infof("Volume %s: device size=%d, filesystem size=%d, block size=%d", devicePath, deviceSize, fsSize, blockSize) + if deviceSize <= fsSize+blockSize { + return false, nil + } + return true, nil +} +func (m *NodeMounter) getDeviceSize(devicePath string) (uint64, error) { + output, err := m.SafeFormatAndMount.Exec.Command("blockdev", "--getsize64", devicePath).CombinedOutput() + outStr := strings.TrimSpace(string(output)) + if err != nil { + return 0, fmt.Errorf("failed to read size of device %s: %s: %s", devicePath, err, outStr) + } + size, err := strconv.ParseUint(outStr, 10, 64) + if err != nil { + return 0, fmt.Errorf("failed to parse size of device %s %s: %s", devicePath, outStr, err) + } + return size, nil +} + +func (m *NodeMounter) getExtSize(devicePath string) (uint64, uint64, error) { + output, err := m.SafeFormatAndMount.Exec.Command("dumpe2fs", "-h", devicePath).CombinedOutput() + if err != nil { + return 0, 0, fmt.Errorf("failed to read size of filesystem on %s: %s: %s", devicePath, err, string(output)) + } + + lines := strings.Split(string(output), "\n") + var blockSize, blockCount uint64 + + for _, line := range lines { + tokens := strings.Split(line, ":") + if len(tokens) != 2 { + continue + } + key, value := strings.TrimSpace(tokens[0]), strings.TrimSpace(tokens[1]) + if key == "Block count" { + blockCount, err = strconv.ParseUint(value, 10, 64) + if err != nil { + return 0, 0, fmt.Errorf("failed to parse block count %s: %s", value, err) + } + } + if key == "Block size" { + blockSize, err = strconv.ParseUint(value, 10, 64) + if err != nil { + return 0, 0, fmt.Errorf("failed to parse block size %s: %s", value, err) + } + } + } + if blockSize == 0 { + return 0, 0, fmt.Errorf("could not find block size of device %s", devicePath) + } + if blockCount == 0 { + return 0, 0, fmt.Errorf("could not find block count of device %s", devicePath) + } + return blockSize, blockSize * blockCount, nil +} + +func (m *NodeMounter) getXFSSize(devicePath string) (uint64, uint64, error) { + output, err := m.SafeFormatAndMount.Exec.Command("xfs_info", devicePath).CombinedOutput() + if err != nil { + return 0, 0, fmt.Errorf("failed to read size of filesystem on %s: %s: %s", devicePath, err, string(output)) + } + + lines := strings.Split(string(output), "\n") + var blockSize, blockCount uint64 + + for _, line := range lines { + + if !strings.HasPrefix(line, "data") { + continue + } + line = strings.ReplaceAll(line, ",", "") + tokens := strings.FieldsFunc(line, func(c rune) bool { + return c == ' ' || c == '=' + }) + for index := range tokens { + if strings.TrimSpace(tokens[index]) == "bsize" { + index = index + 1 + blockSize, err = strconv.ParseUint(strings.TrimSpace(tokens[index]), 10, 64) + if err != nil { + return 0, 0, fmt.Errorf("failed to parse block size %s: %s", tokens[index], err) + } + } + if strings.TrimSpace(tokens[index]) == "blocks" { + index = index + 1 + blockCount, err = strconv.ParseUint(strings.TrimSpace(tokens[index]), 10, 64) + if err != nil { + return 0, 0, fmt.Errorf("failed to parse block count %s: %s", tokens[index], err) + } + } + } + } + if blockSize == 0 { + return 0, 0, fmt.Errorf("could not find block size of device %s", devicePath) + } + if blockCount == 0 { + return 0, 0, fmt.Errorf("could not find block count of device %s", devicePath) + } + return blockSize, blockSize * blockCount, nil +} diff --git a/pkg/driver/node.go b/pkg/driver/node.go index 6a88710ff1..efc85589be 100644 --- a/pkg/driver/node.go +++ b/pkg/driver/node.go @@ -124,18 +124,18 @@ func (d *nodeService) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol return &csi.NodeStageVolumeResponse{}, nil } - mount := volCap.GetMount() - if mount == nil { + mountVolume := volCap.GetMount() + if mountVolume == nil { return nil, status.Error(codes.InvalidArgument, "NodeStageVolume: mount is nil within volume capability") } - fsType := mount.GetFsType() + fsType := mountVolume.GetFsType() if len(fsType) == 0 { fsType = defaultFsType } var mountOptions []string - for _, f := range mount.MountFlags { + for _, f := range mountVolume.MountFlags { if !hasMountOption(mountOptions, f) { mountOptions = append(mountOptions, f) } @@ -201,7 +201,21 @@ func (d *nodeService) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol msg := fmt.Sprintf("could not format %q and mount it at %q", source, target) return nil, status.Error(codes.Internal, msg) } - + //TODO: use the common function from vendor pkg kubernetes/mount-util + needResize, err := d.mounter.NeedResize(source, target) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not determine if volume %q (%q) need to be resized: %v", req.GetVolumeId(), source, err) + } + if needResize { + r := resizefs.NewResizeFs(&mount.SafeFormatAndMount{ + Interface: mount.New(""), + Exec: exec.New(), + }) + klog.V(2).Infof("Volume %s needs resizing", source) + if _, err := r.Resize(source, target); err != nil { + return nil, status.Errorf(codes.Internal, "Could not resize volume %q (%q): %v", volumeID, source, err) + } + } return &csi.NodeStageVolumeResponse{}, nil } diff --git a/pkg/driver/node_test.go b/pkg/driver/node_test.go index f8114c992f..0fa3551fc8 100644 --- a/pkg/driver/node_test.go +++ b/pkg/driver/node_test.go @@ -83,6 +83,7 @@ func TestNodeStageVolume(t *testing.T) { mockMounter.EXPECT().MakeDir(targetPath).Return(nil) mockMounter.EXPECT().GetDeviceName(targetPath).Return("", 1, nil) mockMounter.EXPECT().FormatAndMount(gomock.Eq(devicePath), gomock.Eq(targetPath), gomock.Eq(defaultFsType), gomock.Any()) + mockMounter.EXPECT().NeedResize(gomock.Eq(devicePath), gomock.Eq(targetPath)).Return(false, nil) _, err := awsDriver.NodeStageVolume(context.TODO(), req) if err != nil { t.Fatalf("Expect no error but got: %v", err) @@ -163,6 +164,7 @@ func TestNodeStageVolume(t *testing.T) { mockMounter.EXPECT().MakeDir(targetPath).Return(nil) mockMounter.EXPECT().GetDeviceName(targetPath).Return("", 1, nil) mockMounter.EXPECT().FormatAndMount(gomock.Eq(devicePath), gomock.Eq(targetPath), gomock.Eq(FSTypeExt4), gomock.Eq([]string{"dirsync", "noexec"})) + mockMounter.EXPECT().NeedResize(gomock.Eq(devicePath), gomock.Eq(targetPath)).Return(false, nil) _, err := awsDriver.NodeStageVolume(context.TODO(), req) if err != nil { t.Fatalf("Expect no error but got: %v", err) @@ -208,6 +210,7 @@ func TestNodeStageVolume(t *testing.T) { mockMounter.EXPECT().MakeDir(targetPath).Return(nil) mockMounter.EXPECT().GetDeviceName(targetPath).Return("", 1, nil) mockMounter.EXPECT().FormatAndMount(gomock.Eq(devicePath), gomock.Eq(targetPath), gomock.Eq(FSTypeExt3), gomock.Any()) + mockMounter.EXPECT().NeedResize(gomock.Eq(devicePath), gomock.Eq(targetPath)).Return(false, nil) _, err := awsDriver.NodeStageVolume(context.TODO(), req) if err != nil { t.Fatalf("Expect no error but got: %v", err) @@ -253,6 +256,44 @@ func TestNodeStageVolume(t *testing.T) { mockMounter.EXPECT().MakeDir(targetPath).Return(nil) mockMounter.EXPECT().GetDeviceName(targetPath).Return("", 1, nil) mockMounter.EXPECT().FormatAndMount(gomock.Eq(devicePath), gomock.Eq(targetPath), gomock.Eq(FSTypeExt4), gomock.Any()) + mockMounter.EXPECT().NeedResize(gomock.Eq(devicePath), gomock.Eq(targetPath)).Return(false, nil) + _, err := awsDriver.NodeStageVolume(context.TODO(), req) + if err != nil { + t.Fatalf("Expect no error but got: %v", err) + } + }, + }, + { + name: "success need resize", + testFunc: func(t *testing.T) { + mockCtl := gomock.NewController(t) + defer mockCtl.Finish() + + mockMetadata := mocks.NewMockMetadataService(mockCtl) + mockMounter := mocks.NewMockMounter(mockCtl) + + awsDriver := &nodeService{ + metadata: mockMetadata, + mounter: mockMounter, + inFlight: internal.NewInFlight(), + } + + req := &csi.NodeStageVolumeRequest{ + PublishContext: map[string]string{DevicePathKey: devicePath}, + StagingTargetPath: targetPath, + VolumeCapability: stdVolCap, + VolumeId: "vol-test", + } + + gomock.InOrder( + mockMounter.EXPECT().ExistsPath(gomock.Eq(devicePath)).Return(true, nil), + mockMounter.EXPECT().ExistsPath(gomock.Eq(targetPath)).Return(false, nil), + ) + + mockMounter.EXPECT().MakeDir(targetPath).Return(nil) + mockMounter.EXPECT().GetDeviceName(targetPath).Return("", 1, nil) + mockMounter.EXPECT().FormatAndMount(gomock.Eq(devicePath), gomock.Eq(targetPath), gomock.Eq(defaultFsType), gomock.Any()) + mockMounter.EXPECT().NeedResize(gomock.Eq(devicePath), gomock.Eq(targetPath)).Return(true, nil) _, err := awsDriver.NodeStageVolume(context.TODO(), req) if err != nil { t.Fatalf("Expect no error but got: %v", err) diff --git a/pkg/driver/sanity_test.go b/pkg/driver/sanity_test.go index 203608d093..b475fad161 100644 --- a/pkg/driver/sanity_test.go +++ b/pkg/driver/sanity_test.go @@ -351,3 +351,7 @@ func (f *fakeMounter) ExistsPath(filename string) (bool, error) { } return true, nil } + +func (f *fakeMounter) NeedResize(source string, path string) (bool, error) { + return false, nil +}