Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resize filesystem when restore a snapshot to larger size volume #753

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions pkg/driver/mocks/mock_mount.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

122 changes: 122 additions & 0 deletions pkg/driver/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ limitations under the License.
package driver

import (
"fmt"
"k8s.io/klog"
"os"
"strconv"
"strings"

"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/mounter"
mountutils "k8s.io/mount-utils"
Expand All @@ -39,6 +43,7 @@ type Mounter interface {
MakeFile(path string) error
MakeDir(path string) error
PathExists(path string) (bool, error)
NeedResize(devicePath string, deviceMountPath string) (bool, error)
}

type NodeMounter struct {
Expand Down Expand Up @@ -91,3 +96,120 @@ func (m *NodeMounter) MakeDir(path string) error {
func (m *NodeMounter) PathExists(path string) (bool, error) {
return mountutils.PathExists(path)
}

//TODO: use common util from vendor kubernetes/mount-util
func (m *NodeMounter) NeedResize(devicePath string, deviceMountPath string) (bool, error) {
// TODO(xiangLi) resize fs size on formatted file system following this PR https://github.com/kubernetes/kubernetes/pull/99223
// Port the in-tree un-released change first, need to remove after in-tree release
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("ResizeFs.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)
default:
klog.Errorf("Not able to parse given filesystem info. fsType: %s, will not resize", format)
return false, fmt.Errorf("Could not parse fs info on given filesystem format: %s. Supported fs types are: xfs, ext3, ext4", format)
}
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))
}

blockSize, blockCount, _ := m.parseFsInfoOutput(string(output), ":", "block size", "block count")

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_io", "-c", "statfs", devicePath).CombinedOutput()
if err != nil {
return 0, 0, fmt.Errorf("failed to read size of filesystem on %s: %s: %s", devicePath, err, string(output))
}

blockSize, blockCount, _ := m.parseFsInfoOutput(string(output), "=", "geom.bsize", "geom.datablocks")

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) parseFsInfoOutput(cmdOutput string, spliter string, blockSizeKey string, blockCountKey string) (uint64, uint64, error) {
lines := strings.Split(cmdOutput, "\n")
var blockSize, blockCount uint64
var err error

for _, line := range lines {
tokens := strings.Split(line, spliter)
if len(tokens) != 2 {
continue
}
key, value := strings.ToLower(strings.TrimSpace(tokens[0])), strings.ToLower(strings.TrimSpace(tokens[1]))
if key == blockSizeKey {
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 key == blockCountKey {
blockCount, err = strconv.ParseUint(value, 10, 64)
if err != nil {
return 0, 0, fmt.Errorf("failed to parse block count %s: %s", value, err)
}
}
}
return blockSize, blockCount, err
}
Loading