From c8f2295d6880959365d2f4c3bb8e45233b5ee100 Mon Sep 17 00:00:00 2001 From: Hemant Kumar Date: Wed, 30 Apr 2025 11:43:36 -0400 Subject: [PATCH 1/2] Disable disk size checking when calling NeedsResize function --- .../src/k8s.io/mount-utils/resizefs_linux.go | 153 +----- .../k8s.io/mount-utils/resizefs_linux_test.go | 478 ------------------ 2 files changed, 5 insertions(+), 626 deletions(-) diff --git a/staging/src/k8s.io/mount-utils/resizefs_linux.go b/staging/src/k8s.io/mount-utils/resizefs_linux.go index 3a5fa1be7cb..16adc0cb33e 100644 --- a/staging/src/k8s.io/mount-utils/resizefs_linux.go +++ b/staging/src/k8s.io/mount-utils/resizefs_linux.go @@ -21,9 +21,9 @@ package mount import ( "fmt" - "strconv" "strings" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/klog/v2" utilexec "k8s.io/utils/exec" ) @@ -117,11 +117,6 @@ func (resizefs *ResizeFs) NeedResize(devicePath string, deviceMountPath string) return false, nil } - deviceSize, err := resizefs.getDeviceSize(devicePath) - if err != nil { - return false, err - } - var fsSize, blockSize uint64 format, err := getDiskFormat(resizefs.exec, devicePath) if err != nil { formatErr := fmt.Errorf("ResizeFS.Resize - error checking format for device %s: %v", devicePath, err) @@ -134,45 +129,13 @@ func (resizefs *ResizeFs) NeedResize(devicePath string, deviceMountPath string) return false, nil } - klog.V(3).Infof("ResizeFs.needResize - checking mounted volume %s", devicePath) - switch format { - case "ext3", "ext4": - blockSize, fsSize, err = resizefs.getExtSize(devicePath) - klog.V(5).Infof("Ext size: filesystem size=%d, block size=%d", fsSize, blockSize) - case "xfs": - blockSize, fsSize, err = resizefs.getXFSSize(deviceMountPath) - klog.V(5).Infof("Xfs size: filesystem size=%d, block size=%d, err=%v", fsSize, blockSize, err) - case "btrfs": - blockSize, fsSize, err = resizefs.getBtrfsSize(devicePath) - klog.V(5).Infof("Btrfs 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 + supportedFormats := sets.New("ext3", "ext4", "xfs", "btrfs") + if !supportedFormats.Has(format) { + return false, fmt.Errorf("could not parse fs info of given filesystem format: %s. Supported fs types are: xfs, ext3, ext4", format) } return true, nil } -func (resizefs *ResizeFs) getDeviceSize(devicePath string) (uint64, error) { - output, err := resizefs.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 (resizefs *ResizeFs) getDeviceRO(devicePath string) (bool, error) { output, err := resizefs.exec.Command(blockDev, "--getro", devicePath).CombinedOutput() outStr := strings.TrimSpace(string(output)) @@ -185,112 +148,6 @@ func (resizefs *ResizeFs) getDeviceRO(devicePath string) (bool, error) { case "1": return true, nil default: - return false, fmt.Errorf("Failed readonly device check. Expected 1 or 0, got '%s'", outStr) + return false, fmt.Errorf("failed readonly device check. Expected 1 or 0, got '%s'", outStr) } } - -func (resizefs *ResizeFs) getExtSize(devicePath string) (uint64, uint64, error) { - output, err := resizefs.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, _ := resizefs.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 (resizefs *ResizeFs) getXFSSize(devicePath string) (uint64, uint64, error) { - output, err := resizefs.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, _ := resizefs.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 (resizefs *ResizeFs) getBtrfsSize(devicePath string) (uint64, uint64, error) { - output, err := resizefs.exec.Command("btrfs", "inspect-internal", "dump-super", "-f", 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, totalBytes, _ := resizefs.parseBtrfsInfoOutput(string(output), "sectorsize", "total_bytes") - - if blockSize == 0 { - return 0, 0, fmt.Errorf("could not find block size of device %s", devicePath) - } - if totalBytes == 0 { - return 0, 0, fmt.Errorf("could not find total size of device %s", devicePath) - } - return blockSize, totalBytes, nil -} - -func (resizefs *ResizeFs) parseBtrfsInfoOutput(cmdOutput string, blockSizeKey string, totalBytesKey string) (uint64, uint64, error) { - lines := strings.Split(cmdOutput, "\n") - var blockSize, blockCount uint64 - var err error - - for _, line := range lines { - tokens := strings.Fields(line) - 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 == totalBytesKey { - blockCount, err = strconv.ParseUint(value, 10, 64) - if err != nil { - return 0, 0, fmt.Errorf("failed to parse total size %s: %s", value, err) - } - } - } - return blockSize, blockCount, err -} - -func (resizefs *ResizeFs) 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 -} diff --git a/staging/src/k8s.io/mount-utils/resizefs_linux_test.go b/staging/src/k8s.io/mount-utils/resizefs_linux_test.go index 5acc1632e46..270027bbab0 100644 --- a/staging/src/k8s.io/mount-utils/resizefs_linux_test.go +++ b/staging/src/k8s.io/mount-utils/resizefs_linux_test.go @@ -20,473 +20,12 @@ limitations under the License. package mount import ( - "fmt" "testing" "k8s.io/utils/exec" fakeexec "k8s.io/utils/exec/testing" ) -func TestGetFileSystemSize(t *testing.T) { - cmdOutputSuccessXfs := ` - statfs.f_bsize = 4096 - statfs.f_blocks = 1832448 - statfs.f_bavail = 1822366 - statfs.f_files = 3670016 - statfs.f_ffree = 3670012 - statfs.f_flags = 0x1020 - geom.bsize = 4096 - geom.agcount = 4 - geom.agblocks = 458752 - geom.datablocks = 1835008 - geom.rtblocks = 0 - geom.rtextents = 0 - geom.rtextsize = 1 - geom.sunit = 0 - geom.swidth = 0 - counts.freedata = 1822372 - counts.freertx = 0 - counts.freeino = 61 - counts.allocino = 64 -` - cmdOutputNoDataXfs := ` - statfs.f_bsize = 4096 - statfs.f_blocks = 1832448 - statfs.f_bavail = 1822366 - statfs.f_files = 3670016 - statfs.f_ffree = 3670012 - statfs.f_flags = 0x1020 - geom.agcount = 4 - geom.agblocks = 458752 - geom.rtblocks = 0 - geom.rtextents = 0 - geom.rtextsize = 1 - geom.sunit = 0 - geom.swidth = 0 - counts.freedata = 1822372 - counts.freertx = 0 - counts.freeino = 61 - counts.allocino = 64 -` - cmdOutputSuccessExt4 := ` -Filesystem volume name: cloudimg-rootfs -Last mounted on: / -Filesystem UUID: testUUID -Filesystem magic number: 0xEF53 -Filesystem revision #: 1 (dynamic) -Filesystem features: has_journal ext_attr resize_inode dir_index filetype needs_recovery extent 64bit -Default mount options: user_xattr acl -Filesystem state: clean -Errors behavior: Continue -Filesystem OS type: Linux -Inode count: 3840000 -Block count: 5242880 -Reserved block count: 0 -Free blocks: 5514413 -Free inodes: 3677492 -First block: 0 -Block size: 4096 -Fragment size: 4096 -Group descriptor size: 64 -Reserved GDT blocks: 252 -Blocks per group: 32768 -Fragments per group: 32768 -Inodes per group: 16000 -Inode blocks per group: 1000 -Flex block group size: 16 -Mount count: 2 -Maximum mount count: -1 -Check interval: 0 () -Lifetime writes: 180 GB -Reserved blocks uid: 0 (user root) -Reserved blocks gid: 0 (group root) -First inode: 11 -Inode size: 256 -Required extra isize: 32 -Desired extra isize: 32 -Journal inode: 8 -Default directory hash: half_md4 -Directory Hash Seed: Test Hashing -Journal backup: inode blocks -Checksum type: crc32c -Checksum: 0x57705f62 -Journal features: journal_incompat_revoke journal_64bit journal_checksum_v3 -Journal size: 64M -Journal length: 16384 -Journal sequence: 0x00037109 -Journal start: 1 -Journal checksum type: crc32c -Journal checksum: 0xb7df3c6e -` - cmdOutputNoDataExt4 := `Filesystem volume name: cloudimg-rootfs -Last mounted on: / -Filesystem UUID: testUUID -Filesystem magic number: 0xEF53 -Filesystem revision #: 1 (dynamic) -Filesystem features: has_journal ext_attr resize_inode dir_index filetype needs_recovery extent 64bit -Default mount options: user_xattr acl -Filesystem state: clean -Errors behavior: Continue -Filesystem OS type: Linux -Inode count: 3840000 -Reserved block count: 0 -Free blocks: 5514413 -Free inodes: 3677492 -First block: 0 -Fragment size: 4096 -Group descriptor size: 64 -Reserved GDT blocks: 252 -Blocks per group: 32768 -Fragments per group: 32768 -Inodes per group: 16000 -Inode blocks per group: 1000 -Flex block group size: 16 -Mount count: 2 -Maximum mount count: -1 -Check interval: 0 () -Lifetime writes: 180 GB -Reserved blocks uid: 0 (user root) -Reserved blocks gid: 0 (group root) -First inode: 11 -Inode size: 256 -Required extra isize: 32 -Desired extra isize: 32 -Journal inode: 8 -Default directory hash: half_md4 -Directory Hash Seed: Test Hashing -Journal backup: inode blocks -Checksum type: crc32c -Checksum: 0x57705f62 -Journal features: journal_incompat_revoke journal_64bit journal_checksum_v3 -Journal size: 64M -Journal length: 16384 -Journal sequence: 0x00037109 -Journal start: 1 -Journal checksum type: crc32c -Journal checksum: 0xb7df3c6e -` - cmdOutputSuccessBtrfs := `superblock: bytenr=65536, device=/dev/loop0 ---------------------------------------------------------- -csum_type 0 (crc32c) -csum_size 4 -csum 0x31693b11 [match] -bytenr 65536 -flags 0x1 - ( WRITTEN ) -magic _BHRfS_M [match] -fsid 3f53c8f7-3c57-4185-bf1d-b305b42cce97 -metadata_uuid 3f53c8f7-3c57-4185-bf1d-b305b42cce97 -label -generation 7 -root 30441472 -sys_array_size 129 -chunk_root_generation 6 -root_level 0 -chunk_root 22036480 -chunk_root_level 0 -log_root 0 -log_root_transid 0 -log_root_level 0 -total_bytes 1048576000 -bytes_used 147456 -sectorsize 4096 -nodesize 16384 -leafsize (deprecated) 16384 -stripesize 4096 -root_dir 6 -num_devices 1 -compat_flags 0x0 -compat_ro_flags 0x3 - ( FREE_SPACE_TREE | - FREE_SPACE_TREE_VALID ) -incompat_flags 0x341 - ( MIXED_BACKREF | - EXTENDED_IREF | - SKINNY_METADATA | - NO_HOLES ) -cache_generation 0 -uuid_tree_generation 7 -dev_item.uuid 987c8423-fba3-4168-9892-560a116feb81 -dev_item.fsid 3f53c8f7-3c57-4185-bf1d-b305b42cce97 [match] -dev_item.type 0 -dev_item.total_bytes 1048576000 -dev_item.bytes_used 130023424 -dev_item.io_align 4096 -dev_item.io_width 4096 -dev_item.sector_size 4096 -dev_item.devid 1 -dev_item.dev_group 0 -dev_item.seek_speed 0 -dev_item.bandwidth 0 -dev_item.generation 0 -sys_chunk_array[2048]: - item 0 key (FIRST_CHUNK_TREE CHUNK_ITEM 22020096) - length 8388608 owner 2 stripe_len 65536 type SYSTEM|DUP - io_align 65536 io_width 65536 sector_size 4096 - num_stripes 2 sub_stripes 1 - stripe 0 devid 1 offset 22020096 - dev_uuid 987c8423-fba3-4168-9892-560a116feb81 - stripe 1 devid 1 offset 30408704 - dev_uuid 987c8423-fba3-4168-9892-560a116feb81 -backup_roots[4]: - backup 0: - backup_tree_root: 30441472 gen: 5 level: 0 - backup_chunk_root: 22020096 gen: 5 level: 0 - backup_extent_root: 30474240 gen: 5 level: 0 - backup_fs_root: 30425088 gen: 5 level: 0 - backup_dev_root: 30457856 gen: 5 level: 0 - backup_csum_root: 30490624 gen: 5 level: 0 - backup_total_bytes: 1048576000 - backup_bytes_used: 147456 - backup_num_devices: 1 - - backup 1: - backup_tree_root: 30588928 gen: 6 level: 0 - backup_chunk_root: 22036480 gen: 6 level: 0 - backup_extent_root: 30408704 gen: 6 level: 0 - backup_fs_root: 30425088 gen: 5 level: 0 - backup_dev_root: 30556160 gen: 6 level: 0 - backup_csum_root: 30490624 gen: 5 level: 0 - backup_total_bytes: 1048576000 - backup_bytes_used: 147456 - backup_num_devices: 1 - - backup 2: - backup_tree_root: 30441472 gen: 7 level: 0 - backup_chunk_root: 22036480 gen: 6 level: 0 - backup_extent_root: 30474240 gen: 7 level: 0 - backup_fs_root: 30425088 gen: 5 level: 0 - backup_dev_root: 30457856 gen: 7 level: 0 - backup_csum_root: 30490624 gen: 5 level: 0 - backup_total_bytes: 1048576000 - backup_bytes_used: 147456 - backup_num_devices: 1 - - backup 3: - backup_tree_root: 30408704 gen: 4 level: 0 - backup_chunk_root: 1064960 gen: 4 level: 0 - backup_extent_root: 5341184 gen: 4 level: 0 - backup_fs_root: 5324800 gen: 3 level: 0 - backup_dev_root: 5242880 gen: 4 level: 0 - backup_csum_root: 1130496 gen: 1 level: 0 - backup_total_bytes: 1048576000 - backup_bytes_used: 114688 - backup_num_devices: 1 - -` - cmdOutputNoDataBtrfs := `superblock: bytenr=65536, device=/dev/loop0 ---------------------------------------------------------- -csum_type 0 (crc32c) -csum_size 4 -csum 0x31693b11 [match] -bytenr 65536 -flags 0x1 - ( WRITTEN ) -magic _BHRfS_M [match] -fsid 3f53c8f7-3c57-4185-bf1d-b305b42cce97 -metadata_uuid 3f53c8f7-3c57-4185-bf1d-b305b42cce97 -label -generation 7 -root 30441472 -sys_array_size 129 -chunk_root_generation 6 -root_level 0 -chunk_root 22036480 -chunk_root_level 0 -log_root 0 -log_root_transid 0 -log_root_level 0 -bytes_used 147456 -nodesize 16384 -leafsize (deprecated) 16384 -stripesize 4096 -root_dir 6 -num_devices 1 -compat_flags 0x0 -compat_ro_flags 0x3 - ( FREE_SPACE_TREE | - FREE_SPACE_TREE_VALID ) -incompat_flags 0x341 - ( MIXED_BACKREF | - EXTENDED_IREF | - SKINNY_METADATA | - NO_HOLES ) -cache_generation 0 -uuid_tree_generation 7 -dev_item.uuid 987c8423-fba3-4168-9892-560a116feb81 -dev_item.fsid 3f53c8f7-3c57-4185-bf1d-b305b42cce97 [match] -dev_item.type 0 -dev_item.total_bytes 1048576000 -dev_item.bytes_used 130023424 -dev_item.io_align 4096 -dev_item.io_width 4096 -dev_item.sector_size 4096 -dev_item.devid 1 -dev_item.dev_group 0 -dev_item.seek_speed 0 -dev_item.bandwidth 0 -dev_item.generation 0 -sys_chunk_array[2048]: - item 0 key (FIRST_CHUNK_TREE CHUNK_ITEM 22020096) - length 8388608 owner 2 stripe_len 65536 type SYSTEM|DUP - io_align 65536 io_width 65536 sector_size 4096 - num_stripes 2 sub_stripes 1 - stripe 0 devid 1 offset 22020096 - dev_uuid 987c8423-fba3-4168-9892-560a116feb81 - stripe 1 devid 1 offset 30408704 - dev_uuid 987c8423-fba3-4168-9892-560a116feb81 -backup_roots[4]: - backup 0: - backup_tree_root: 30441472 gen: 5 level: 0 - backup_chunk_root: 22020096 gen: 5 level: 0 - backup_extent_root: 30474240 gen: 5 level: 0 - backup_fs_root: 30425088 gen: 5 level: 0 - backup_dev_root: 30457856 gen: 5 level: 0 - backup_csum_root: 30490624 gen: 5 level: 0 - backup_total_bytes: 1048576000 - backup_bytes_used: 147456 - backup_num_devices: 1 - - backup 1: - backup_tree_root: 30588928 gen: 6 level: 0 - backup_chunk_root: 22036480 gen: 6 level: 0 - backup_extent_root: 30408704 gen: 6 level: 0 - backup_fs_root: 30425088 gen: 5 level: 0 - backup_dev_root: 30556160 gen: 6 level: 0 - backup_csum_root: 30490624 gen: 5 level: 0 - backup_total_bytes: 1048576000 - backup_bytes_used: 147456 - backup_num_devices: 1 - - backup 2: - backup_tree_root: 30441472 gen: 7 level: 0 - backup_chunk_root: 22036480 gen: 6 level: 0 - backup_extent_root: 30474240 gen: 7 level: 0 - backup_fs_root: 30425088 gen: 5 level: 0 - backup_dev_root: 30457856 gen: 7 level: 0 - backup_csum_root: 30490624 gen: 5 level: 0 - backup_total_bytes: 1048576000 - backup_bytes_used: 147456 - backup_num_devices: 1 - - backup 3: - backup_tree_root: 30408704 gen: 4 level: 0 - backup_chunk_root: 1064960 gen: 4 level: 0 - backup_extent_root: 5341184 gen: 4 level: 0 - backup_fs_root: 5324800 gen: 3 level: 0 - backup_dev_root: 5242880 gen: 4 level: 0 - backup_csum_root: 1130496 gen: 1 level: 0 - backup_total_bytes: 1048576000 - backup_bytes_used: 114688 - backup_num_devices: 1 - -` - testcases := []struct { - name string - devicePath string - blocksize uint64 - blockCount uint64 - cmdOutput string - expectError bool - fsType string - }{ - { - name: "success parse xfs info", - devicePath: "/dev/test1", - blocksize: 4096, - blockCount: 1835008, - cmdOutput: cmdOutputSuccessXfs, - expectError: false, - fsType: "xfs", - }, - { - name: "block size not present - xfs", - devicePath: "/dev/test1", - blocksize: 0, - blockCount: 0, - cmdOutput: cmdOutputNoDataXfs, - expectError: true, - fsType: "xfs", - }, - { - name: "success parse ext info", - devicePath: "/dev/test1", - blocksize: 4096, - blockCount: 5242880, - cmdOutput: cmdOutputSuccessExt4, - expectError: false, - fsType: "ext4", - }, - { - name: "block size not present - ext4", - devicePath: "/dev/test1", - blocksize: 0, - blockCount: 0, - cmdOutput: cmdOutputNoDataExt4, - expectError: true, - fsType: "ext4", - }, - { - name: "success parse btrfs info", - devicePath: "/dev/test1", - blocksize: 4096, - blockCount: 256000, - cmdOutput: cmdOutputSuccessBtrfs, - expectError: false, - fsType: "btrfs", - }, - { - name: "block size not present - btrfs", - devicePath: "/dev/test1", - blocksize: 0, - blockCount: 0, - cmdOutput: cmdOutputNoDataBtrfs, - expectError: true, - fsType: "btrfs", - }, - } - - for _, test := range testcases { - t.Run(test.name, func(t *testing.T) { - fcmd := fakeexec.FakeCmd{ - CombinedOutputScript: []fakeexec.FakeAction{ - func() ([]byte, []byte, error) { return []byte(test.cmdOutput), nil, nil }, - }, - } - fexec := &fakeexec.FakeExec{ - CommandScript: []fakeexec.FakeCommandAction{ - func(cmd string, args ...string) exec.Cmd { - return fakeexec.InitFakeCmd(&fcmd, cmd, args...) - }, - }, - } - resizefs := ResizeFs{exec: fexec} - - var blockSize uint64 - var fsSize uint64 - var err error - switch test.fsType { - case "xfs": - blockSize, fsSize, err = resizefs.getXFSSize(test.devicePath) - case "ext4": - blockSize, fsSize, err = resizefs.getExtSize(test.devicePath) - case "btrfs": - blockSize, fsSize, err = resizefs.getBtrfsSize(test.devicePath) - } - - if blockSize != test.blocksize { - t.Fatalf("Parse wrong block size value, expect %d, but got %d", test.blocksize, blockSize) - } - if fsSize != test.blocksize*test.blockCount { - t.Fatalf("Parse wrong fs size value, expect %d, but got %d", test.blocksize*test.blockCount, fsSize) - } - if !test.expectError && err != nil { - t.Fatalf("Expect no error but got %v", err) - } - }) - } -} - func TestNeedResize(t *testing.T) { testcases := []struct { name string @@ -521,17 +60,6 @@ func TestNeedResize(t *testing.T) { expectError: false, expectResult: false, }, - { - name: "False - Not needed by size", - devicePath: "/dev/test1", - deviceMountPath: "/mnt/test1", - readonly: "0", - deviceSize: "20", - cmdOutputFsType: "TYPE=ext3", - extSize: "2048", - expectError: false, - expectResult: false, - }, { name: "False - Unsupported fs type", devicePath: "/dev/test1", @@ -550,19 +78,13 @@ func TestNeedResize(t *testing.T) { fcmd := fakeexec.FakeCmd{ CombinedOutputScript: []fakeexec.FakeAction{ func() ([]byte, []byte, error) { return []byte(test.readonly), nil, nil }, - func() ([]byte, []byte, error) { return []byte(test.deviceSize), nil, nil }, func() ([]byte, []byte, error) { return []byte(test.cmdOutputFsType), nil, nil }, - func() ([]byte, []byte, error) { - return []byte(fmt.Sprintf("block size: %s\nblock count: 1", test.extSize)), nil, nil - }, }, } fexec := &fakeexec.FakeExec{ CommandScript: []fakeexec.FakeCommandAction{ func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, }, } resizefs := ResizeFs{exec: fexec} From c916dba607332f7619614868ea7164a11e18b6d2 Mon Sep 17 00:00:00 2001 From: Hemant Kumar Date: Wed, 30 Apr 2025 14:12:14 -0400 Subject: [PATCH 2/2] Disable size check for xfs/ext3/ext4 filesystems before expansion --- .../src/k8s.io/mount-utils/resizefs_linux.go | 85 ++++- .../k8s.io/mount-utils/resizefs_linux_test.go | 314 ++++++++++++++++++ 2 files changed, 394 insertions(+), 5 deletions(-) diff --git a/staging/src/k8s.io/mount-utils/resizefs_linux.go b/staging/src/k8s.io/mount-utils/resizefs_linux.go index 16adc0cb33e..3eaf46a49a1 100644 --- a/staging/src/k8s.io/mount-utils/resizefs_linux.go +++ b/staging/src/k8s.io/mount-utils/resizefs_linux.go @@ -21,9 +21,9 @@ package mount import ( "fmt" + "strconv" "strings" - "k8s.io/apimachinery/pkg/util/sets" "k8s.io/klog/v2" utilexec "k8s.io/utils/exec" ) @@ -129,18 +129,93 @@ func (resizefs *ResizeFs) NeedResize(devicePath string, deviceMountPath string) return false, nil } - supportedFormats := sets.New("ext3", "ext4", "xfs", "btrfs") - if !supportedFormats.Has(format) { + switch format { + case "ext3", "ext4", "xfs": + // For ext3/ext4/xfs, recommendation received from linux filesystem folks is to let + // resize2fs/xfs_growfs do the check for us. So we will not do any check here. + return true, nil + case "btrfs": + deviceSize, err := resizefs.getDeviceSize(devicePath) + if err != nil { + return false, err + } + blockSize, fsSize, err := resizefs.getBtrfsSize(devicePath) + klog.V(5).Infof("Btrfs size: filesystem size=%d, block size=%d, err=%v", fsSize, blockSize, err) + if err != nil { + return false, err + } + if deviceSize <= fsSize+blockSize { + return false, nil + } + return true, nil + default: return false, fmt.Errorf("could not parse fs info of given filesystem format: %s. Supported fs types are: xfs, ext3, ext4", format) } - return true, nil +} + +func (resizefs *ResizeFs) getDeviceSize(devicePath string) (uint64, error) { + output, err := resizefs.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 (resizefs *ResizeFs) getBtrfsSize(devicePath string) (uint64, uint64, error) { + output, err := resizefs.exec.Command("btrfs", "inspect-internal", "dump-super", "-f", 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, totalBytes, _ := resizefs.parseBtrfsInfoOutput(string(output), "sectorsize", "total_bytes") + + if blockSize == 0 { + return 0, 0, fmt.Errorf("could not find block size of device %s", devicePath) + } + if totalBytes == 0 { + return 0, 0, fmt.Errorf("could not find total size of device %s", devicePath) + } + return blockSize, totalBytes, nil +} + +func (resizefs *ResizeFs) parseBtrfsInfoOutput(cmdOutput string, blockSizeKey string, totalBytesKey string) (uint64, uint64, error) { + lines := strings.Split(cmdOutput, "\n") + var blockSize, blockCount uint64 + var err error + + for _, line := range lines { + tokens := strings.Fields(line) + 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 == totalBytesKey { + blockCount, err = strconv.ParseUint(value, 10, 64) + if err != nil { + return 0, 0, fmt.Errorf("failed to parse total size %s: %s", value, err) + } + } + } + return blockSize, blockCount, err } func (resizefs *ResizeFs) getDeviceRO(devicePath string) (bool, error) { output, err := resizefs.exec.Command(blockDev, "--getro", devicePath).CombinedOutput() outStr := strings.TrimSpace(string(output)) if err != nil { - return false, fmt.Errorf("failed to get readonly bit from device %s: %s: %s", devicePath, err, outStr) + return false, fmt.Errorf("failed to get readonly bit from device %s: %w: %s", devicePath, err, outStr) } switch outStr { case "0": diff --git a/staging/src/k8s.io/mount-utils/resizefs_linux_test.go b/staging/src/k8s.io/mount-utils/resizefs_linux_test.go index 270027bbab0..9a39a574bd6 100644 --- a/staging/src/k8s.io/mount-utils/resizefs_linux_test.go +++ b/staging/src/k8s.io/mount-utils/resizefs_linux_test.go @@ -20,12 +20,290 @@ limitations under the License. package mount import ( + "fmt" "testing" "k8s.io/utils/exec" fakeexec "k8s.io/utils/exec/testing" ) +func TestGetFileSystemSize(t *testing.T) { + cmdOutputSuccessBtrfs := `superblock: bytenr=65536, device=/dev/loop0 + --------------------------------------------------------- + csum_type 0 (crc32c) + csum_size 4 + csum 0x31693b11 [match] + bytenr 65536 + flags 0x1 + ( WRITTEN ) + magic _BHRfS_M [match] + fsid 3f53c8f7-3c57-4185-bf1d-b305b42cce97 + metadata_uuid 3f53c8f7-3c57-4185-bf1d-b305b42cce97 + label + generation 7 + root 30441472 + sys_array_size 129 + chunk_root_generation 6 + root_level 0 + chunk_root 22036480 + chunk_root_level 0 + log_root 0 + log_root_transid 0 + log_root_level 0 + total_bytes 1048576000 + bytes_used 147456 + sectorsize 4096 + nodesize 16384 + leafsize (deprecated) 16384 + stripesize 4096 + root_dir 6 + num_devices 1 + compat_flags 0x0 + compat_ro_flags 0x3 + ( FREE_SPACE_TREE | + FREE_SPACE_TREE_VALID ) + incompat_flags 0x341 + ( MIXED_BACKREF | + EXTENDED_IREF | + SKINNY_METADATA | + NO_HOLES ) + cache_generation 0 + uuid_tree_generation 7 + dev_item.uuid 987c8423-fba3-4168-9892-560a116feb81 + dev_item.fsid 3f53c8f7-3c57-4185-bf1d-b305b42cce97 [match] + dev_item.type 0 + dev_item.total_bytes 1048576000 + dev_item.bytes_used 130023424 + dev_item.io_align 4096 + dev_item.io_width 4096 + dev_item.sector_size 4096 + dev_item.devid 1 + dev_item.dev_group 0 + dev_item.seek_speed 0 + dev_item.bandwidth 0 + dev_item.generation 0 + sys_chunk_array[2048]: + item 0 key (FIRST_CHUNK_TREE CHUNK_ITEM 22020096) + length 8388608 owner 2 stripe_len 65536 type SYSTEM|DUP + io_align 65536 io_width 65536 sector_size 4096 + num_stripes 2 sub_stripes 1 + stripe 0 devid 1 offset 22020096 + dev_uuid 987c8423-fba3-4168-9892-560a116feb81 + stripe 1 devid 1 offset 30408704 + dev_uuid 987c8423-fba3-4168-9892-560a116feb81 + backup_roots[4]: + backup 0: + backup_tree_root: 30441472 gen: 5 level: 0 + backup_chunk_root: 22020096 gen: 5 level: 0 + backup_extent_root: 30474240 gen: 5 level: 0 + backup_fs_root: 30425088 gen: 5 level: 0 + backup_dev_root: 30457856 gen: 5 level: 0 + backup_csum_root: 30490624 gen: 5 level: 0 + backup_total_bytes: 1048576000 + backup_bytes_used: 147456 + backup_num_devices: 1 + + backup 1: + backup_tree_root: 30588928 gen: 6 level: 0 + backup_chunk_root: 22036480 gen: 6 level: 0 + backup_extent_root: 30408704 gen: 6 level: 0 + backup_fs_root: 30425088 gen: 5 level: 0 + backup_dev_root: 30556160 gen: 6 level: 0 + backup_csum_root: 30490624 gen: 5 level: 0 + backup_total_bytes: 1048576000 + backup_bytes_used: 147456 + backup_num_devices: 1 + + backup 2: + backup_tree_root: 30441472 gen: 7 level: 0 + backup_chunk_root: 22036480 gen: 6 level: 0 + backup_extent_root: 30474240 gen: 7 level: 0 + backup_fs_root: 30425088 gen: 5 level: 0 + backup_dev_root: 30457856 gen: 7 level: 0 + backup_csum_root: 30490624 gen: 5 level: 0 + backup_total_bytes: 1048576000 + backup_bytes_used: 147456 + backup_num_devices: 1 + + backup 3: + backup_tree_root: 30408704 gen: 4 level: 0 + backup_chunk_root: 1064960 gen: 4 level: 0 + backup_extent_root: 5341184 gen: 4 level: 0 + backup_fs_root: 5324800 gen: 3 level: 0 + backup_dev_root: 5242880 gen: 4 level: 0 + backup_csum_root: 1130496 gen: 1 level: 0 + backup_total_bytes: 1048576000 + backup_bytes_used: 114688 + backup_num_devices: 1 + + ` + cmdOutputNoDataBtrfs := `superblock: bytenr=65536, device=/dev/loop0 + --------------------------------------------------------- + csum_type 0 (crc32c) + csum_size 4 + csum 0x31693b11 [match] + bytenr 65536 + flags 0x1 + ( WRITTEN ) + magic _BHRfS_M [match] + fsid 3f53c8f7-3c57-4185-bf1d-b305b42cce97 + metadata_uuid 3f53c8f7-3c57-4185-bf1d-b305b42cce97 + label + generation 7 + root 30441472 + sys_array_size 129 + chunk_root_generation 6 + root_level 0 + chunk_root 22036480 + chunk_root_level 0 + log_root 0 + log_root_transid 0 + log_root_level 0 + bytes_used 147456 + nodesize 16384 + leafsize (deprecated) 16384 + stripesize 4096 + root_dir 6 + num_devices 1 + compat_flags 0x0 + compat_ro_flags 0x3 + ( FREE_SPACE_TREE | + FREE_SPACE_TREE_VALID ) + incompat_flags 0x341 + ( MIXED_BACKREF | + EXTENDED_IREF | + SKINNY_METADATA | + NO_HOLES ) + cache_generation 0 + uuid_tree_generation 7 + dev_item.uuid 987c8423-fba3-4168-9892-560a116feb81 + dev_item.fsid 3f53c8f7-3c57-4185-bf1d-b305b42cce97 [match] + dev_item.type 0 + dev_item.total_bytes 1048576000 + dev_item.bytes_used 130023424 + dev_item.io_align 4096 + dev_item.io_width 4096 + dev_item.sector_size 4096 + dev_item.devid 1 + dev_item.dev_group 0 + dev_item.seek_speed 0 + dev_item.bandwidth 0 + dev_item.generation 0 + sys_chunk_array[2048]: + item 0 key (FIRST_CHUNK_TREE CHUNK_ITEM 22020096) + length 8388608 owner 2 stripe_len 65536 type SYSTEM|DUP + io_align 65536 io_width 65536 sector_size 4096 + num_stripes 2 sub_stripes 1 + stripe 0 devid 1 offset 22020096 + dev_uuid 987c8423-fba3-4168-9892-560a116feb81 + stripe 1 devid 1 offset 30408704 + dev_uuid 987c8423-fba3-4168-9892-560a116feb81 + backup_roots[4]: + backup 0: + backup_tree_root: 30441472 gen: 5 level: 0 + backup_chunk_root: 22020096 gen: 5 level: 0 + backup_extent_root: 30474240 gen: 5 level: 0 + backup_fs_root: 30425088 gen: 5 level: 0 + backup_dev_root: 30457856 gen: 5 level: 0 + backup_csum_root: 30490624 gen: 5 level: 0 + backup_total_bytes: 1048576000 + backup_bytes_used: 147456 + backup_num_devices: 1 + + backup 1: + backup_tree_root: 30588928 gen: 6 level: 0 + backup_chunk_root: 22036480 gen: 6 level: 0 + backup_extent_root: 30408704 gen: 6 level: 0 + backup_fs_root: 30425088 gen: 5 level: 0 + backup_dev_root: 30556160 gen: 6 level: 0 + backup_csum_root: 30490624 gen: 5 level: 0 + backup_total_bytes: 1048576000 + backup_bytes_used: 147456 + backup_num_devices: 1 + + backup 2: + backup_tree_root: 30441472 gen: 7 level: 0 + backup_chunk_root: 22036480 gen: 6 level: 0 + backup_extent_root: 30474240 gen: 7 level: 0 + backup_fs_root: 30425088 gen: 5 level: 0 + backup_dev_root: 30457856 gen: 7 level: 0 + backup_csum_root: 30490624 gen: 5 level: 0 + backup_total_bytes: 1048576000 + backup_bytes_used: 147456 + backup_num_devices: 1 + + backup 3: + backup_tree_root: 30408704 gen: 4 level: 0 + backup_chunk_root: 1064960 gen: 4 level: 0 + backup_extent_root: 5341184 gen: 4 level: 0 + backup_fs_root: 5324800 gen: 3 level: 0 + backup_dev_root: 5242880 gen: 4 level: 0 + backup_csum_root: 1130496 gen: 1 level: 0 + backup_total_bytes: 1048576000 + backup_bytes_used: 114688 + backup_num_devices: 1 + + ` + + testcases := []struct { + name string + devicePath string + blocksize uint64 + blockCount uint64 + cmdOutput string + expectError bool + fsType string + }{ + { + name: "success parse btrfs info", + devicePath: "/dev/test1", + blocksize: 4096, + blockCount: 256000, + cmdOutput: cmdOutputSuccessBtrfs, + expectError: false, + fsType: "btrfs", + }, + { + name: "block size not present - btrfs", + devicePath: "/dev/test1", + blocksize: 0, + blockCount: 0, + cmdOutput: cmdOutputNoDataBtrfs, + expectError: true, + fsType: "btrfs", + }, + } + + for _, test := range testcases { + t.Run(test.name, func(t *testing.T) { + fcmd := fakeexec.FakeCmd{ + CombinedOutputScript: []fakeexec.FakeAction{ + func() ([]byte, []byte, error) { return []byte(test.cmdOutput), nil, nil }, + }, + } + fexec := &fakeexec.FakeExec{ + CommandScript: []fakeexec.FakeCommandAction{ + func(cmd string, args ...string) exec.Cmd { + return fakeexec.InitFakeCmd(&fcmd, cmd, args...) + }, + }, + } + resizefs := ResizeFs{exec: fexec} + + blockSize, fsSize, err := resizefs.getBtrfsSize(test.devicePath) + + if blockSize != test.blocksize { + t.Fatalf("Parse wrong block size value, expect %d, but got %d", test.blocksize, blockSize) + } + if fsSize != test.blocksize*test.blockCount { + t.Fatalf("Parse wrong fs size value, expect %d, but got %d", test.blocksize*test.blockCount, fsSize) + } + if !test.expectError && err != nil { + t.Fatalf("Expect no error but got %v", err) + } + }) + } +} func TestNeedResize(t *testing.T) { testcases := []struct { name string @@ -60,6 +338,28 @@ func TestNeedResize(t *testing.T) { expectError: false, expectResult: false, }, + { + name: "False - Not needed by size for btrfs", + devicePath: "/dev/test1", + deviceMountPath: "/mnt/test1", + readonly: "0", + deviceSize: "20", + cmdOutputFsType: "TYPE=btrfs", + extSize: "2048", + expectError: false, + expectResult: false, + }, + { + name: "True - needed by size for btrfs", + devicePath: "/dev/test1", + deviceMountPath: "/mnt/test1", + readonly: "0", + deviceSize: "2048", + cmdOutputFsType: "TYPE=btrfs", + extSize: "20", + expectError: false, + expectResult: true, + }, { name: "False - Unsupported fs type", devicePath: "/dev/test1", @@ -87,6 +387,20 @@ func TestNeedResize(t *testing.T) { func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, }, } + if test.cmdOutputFsType == "TYPE=btrfs" { + t.Logf("Adding btrfs size command") + fcmd.CombinedOutputScript = append(fcmd.CombinedOutputScript, func() ([]byte, []byte, error) { return []byte(test.deviceSize), nil, nil }) + fcmd.CombinedOutputScript = append(fcmd.CombinedOutputScript, func() ([]byte, []byte, error) { + return []byte(fmt.Sprintf("sectorsize %s\ntotal_bytes 1\n", test.extSize)), nil, nil + }) + + fexec.CommandScript = append(fexec.CommandScript, func(cmd string, args ...string) exec.Cmd { + return fakeexec.InitFakeCmd(&fcmd, cmd, args...) + }) + fexec.CommandScript = append(fexec.CommandScript, func(cmd string, args ...string) exec.Cmd { + return fakeexec.InitFakeCmd(&fcmd, cmd, args...) + }) + } resizefs := ResizeFs{exec: fexec} needResize, err := resizefs.NeedResize(test.devicePath, test.deviceMountPath)