Skip to content

Commit

Permalink
Merge pull request eksctl-io#753 from AndyXiangLi/restore-smaller-sna…
Browse files Browse the repository at this point in the history
…pshot

Resize filesystem when restore a snapshot to larger size volume
  • Loading branch information
AndyXiangLi committed Apr 23, 2021
2 parents aa4427a + 31ce352 commit f1ad3b1
Show file tree
Hide file tree
Showing 8 changed files with 719 additions and 4 deletions.
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

0 comments on commit f1ad3b1

Please sign in to comment.