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

Add basic XFS powerfailure tests #707

Merged
merged 1 commit into from
Mar 13, 2024
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
2 changes: 1 addition & 1 deletion .github/workflows/robustness_template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ jobs:
- name: test-robustness
run: |
set -euo pipefail
sudo apt-get install -y dmsetup
sudo apt-get install -y dmsetup xfsprogs
ROBUSTNESS_TESTFLAGS="--count ${{ inputs.count }} --timeout ${{ inputs.testTimeout }} -failfast" make test-robustness
18 changes: 12 additions & 6 deletions tests/dmflakey/dmflakey.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,9 @@ const (
// The device-mapper device will be /dev/mapper/$flakeyDevice. And the filesystem
// image will be created at $dataStorePath/$flakeyDevice.img. By default, the
// device is available for 2 minutes and size is 10 GiB.
func InitFlakey(flakeyDevice, dataStorePath string, fsType FSType) (_ Flakey, retErr error) {
func InitFlakey(flakeyDevice, dataStorePath string, fsType FSType, mkfsOpt string) (_ Flakey, retErr error) {
imgPath := filepath.Join(dataStorePath, fmt.Sprintf("%s.img", flakeyDevice))
if err := createEmptyFSImage(imgPath, fsType); err != nil {
if err := createEmptyFSImage(imgPath, fsType, mkfsOpt); err != nil {
return nil, err
}
defer func() {
Expand Down Expand Up @@ -276,7 +276,7 @@ func (f *flakey) Teardown() error {

// createEmptyFSImage creates empty filesystem on dataStorePath folder with
// default size - 10 GiB.
func createEmptyFSImage(imgPath string, fsType FSType) error {
func createEmptyFSImage(imgPath string, fsType FSType, mkfsOpt string) error {
if err := validateFSType(fsType); err != nil {
return err
}
Expand Down Expand Up @@ -308,10 +308,16 @@ func createEmptyFSImage(imgPath string, fsType FSType) error {
imgPath, defaultImgSize, err)
}

output, err := exec.Command(mkfs, imgPath).CombinedOutput()
args := []string{imgPath}
if mkfsOpt != "" {
splitArgs := strings.Split(mkfsOpt, " ")
args = append(splitArgs, imgPath)
}

output, err := exec.Command(mkfs, args...).CombinedOutput()
if err != nil {
return fmt.Errorf("failed to mkfs.%s on %s (out: %s): %w",
fsType, imgPath, string(output), err)
return fmt.Errorf("failed to mkfs on %s (%s %v) (out: %s): %w",
imgPath, mkfs, args, string(output), err)
}
return nil
}
Expand Down
42 changes: 23 additions & 19 deletions tests/dmflakey/dmflakey_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,31 +26,35 @@ func TestMain(m *testing.M) {
}

func TestBasic(t *testing.T) {
tmpDir := t.TempDir()
for _, fsType := range []FSType{FSTypeEXT4, FSTypeXFS} {
t.Run(string(fsType), func(t *testing.T) {
tmpDir := t.TempDir()

flakey, err := InitFlakey("go-dmflakey", tmpDir, FSTypeEXT4)
require.NoError(t, err, "init flakey")
defer func() {
assert.NoError(t, flakey.Teardown())
}()
flakey, err := InitFlakey("go-dmflakey", tmpDir, fsType, "")
require.NoError(t, err, "init flakey")
defer func() {
assert.NoError(t, flakey.Teardown())
}()

target := filepath.Join(tmpDir, "root")
require.NoError(t, os.MkdirAll(target, 0600))
target := filepath.Join(tmpDir, "root")
require.NoError(t, os.MkdirAll(target, 0600))

require.NoError(t, mount(target, flakey.DevicePath(), ""))
defer func() {
assert.NoError(t, unmount(target))
}()
require.NoError(t, mount(target, flakey.DevicePath(), ""))
defer func() {
assert.NoError(t, unmount(target))
}()

file := filepath.Join(target, "test")
assert.NoError(t, writeFile(file, []byte("hello, world"), 0600, true))
file := filepath.Join(target, "test")
assert.NoError(t, writeFile(file, []byte("hello, world"), 0600, true))

assert.NoError(t, unmount(target))
assert.NoError(t, unmount(target))

assert.NoError(t, flakey.Teardown())
assert.NoError(t, flakey.Teardown())
})
}
}

func TestDropWrites(t *testing.T) {
func TestDropWritesExt4(t *testing.T) {
flakey, root := initFlakey(t, FSTypeEXT4)

// commit=1000 is to delay commit triggered by writeback thread
Expand Down Expand Up @@ -82,7 +86,7 @@ func TestDropWrites(t *testing.T) {
assert.True(t, errors.Is(err, os.ErrNotExist))
}

func TestErrorWrites(t *testing.T) {
func TestErrorWritesExt4(t *testing.T) {
flakey, root := initFlakey(t, FSTypeEXT4)

// commit=1000 is to delay commit triggered by writeback thread
Expand Down Expand Up @@ -114,7 +118,7 @@ func initFlakey(t *testing.T, fsType FSType) (_ Flakey, root string) {
target := filepath.Join(tmpDir, "root")
require.NoError(t, os.MkdirAll(target, 0600))

flakey, err := InitFlakey("go-dmflakey", tmpDir, FSTypeEXT4)
flakey, err := InitFlakey("go-dmflakey", tmpDir, fsType, "")
require.NoError(t, err, "init flakey")

t.Cleanup(func() {
Expand Down
72 changes: 64 additions & 8 deletions tests/robustness/powerfailure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ var panicFailpoints = []string{
"unmapError",
}

// TestRestartFromPowerFailure is to test data after unexpected power failure.
func TestRestartFromPowerFailure(t *testing.T) {
// TestRestartFromPowerFailureExt4 is to test data after unexpected power failure on ext4.
func TestRestartFromPowerFailureExt4(t *testing.T) {
for _, tc := range []struct {
name string
du time.Duration
Expand Down Expand Up @@ -78,13 +78,69 @@ func TestRestartFromPowerFailure(t *testing.T) {
},
} {
t.Run(tc.name, func(t *testing.T) {
doPowerFailure(t, tc.du, tc.fsMountOpt, tc.useFailpoint)
doPowerFailure(t, tc.du, dmflakey.FSTypeEXT4, "", tc.fsMountOpt, tc.useFailpoint)
})
}
}

func doPowerFailure(t *testing.T, du time.Duration, fsMountOpt string, useFailpoint bool) {
flakey := initFlakeyDevice(t, strings.Replace(t.Name(), "/", "_", -1), dmflakey.FSTypeEXT4, fsMountOpt)
func TestRestartFromPowerFailureXFS(t *testing.T) {
for _, tc := range []struct {
name string
mkfsOpt string
fsMountOpt string
useFailpoint bool
}{
{
name: "xfs_no_opts",
mkfsOpt: "",
fsMountOpt: "",
useFailpoint: true,
},
{
name: "lazy-log",
mkfsOpt: "-l lazy-count=1",
fsMountOpt: "",
useFailpoint: true,
},
{
name: "odd-allocsize",
mkfsOpt: "",
fsMountOpt: "allocsize=" + fmt.Sprintf("%d", 4096*5),
useFailpoint: true,
},
{
name: "nolargeio",
mkfsOpt: "",
fsMountOpt: "nolargeio",
useFailpoint: true,
},
{
name: "odd-alignment",
mkfsOpt: "-d sunit=1024,swidth=1024",
fsMountOpt: "noalign",
useFailpoint: true,
},
{
name: "openshift-sno-options",
mkfsOpt: "-m bigtime=1,finobt=1,rmapbt=0,reflink=1 -i sparse=1 -l lazy-count=1",
// openshift also supplies seclabel,relatime,prjquota on RHEL, but that's not supported on our CI
// prjquota is only unsupported on our ARM runners.
// You can find more information in either the man page with `man xfs` or `man mkfs.xfs`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// You can find more information in either the man page with `man xfs` or `man mkfs.xfs`.
// You can find more information in either the man page with `man xfs` or `man mkfs.xfs`.
// Also refer to https://man7.org/linux/man-pages/man8/mkfs.xfs.8.html.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whatever you prefer, I personally would avoid it due to link rot....

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some parameters (e.g. -m bigtime=1) used in this PR are not supported by old version of mkfs.xfs. Probably my linux VM is too old (3.10).

Providing a link should be better.

  • The link should be relatively stable.
  • When you google it, you will find lots of links, not all links are correct (e.g. https://linux.die.net/man/8/mkfs.xfs doesn't include all parameters/flags). So providing a referenced link should be better. Even it rots, it also provides a reference on which link returned by google is correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added your link

// Also refer to https://man7.org/linux/man-pages/man8/mkfs.xfs.8.html.
fsMountOpt: "rw,attr2,inode64,logbufs=8,logbsize=32k",
useFailpoint: true,
},
} {
t.Run(tc.name, func(t *testing.T) {
t.Logf("mkfs opts: %s", tc.mkfsOpt)
t.Logf("mount opts: %s", tc.fsMountOpt)
doPowerFailure(t, 5*time.Second, dmflakey.FSTypeXFS, tc.mkfsOpt, tc.fsMountOpt, tc.useFailpoint)
})
}
}

func doPowerFailure(t *testing.T, du time.Duration, fsType dmflakey.FSType, mkfsOpt string, fsMountOpt string, useFailpoint bool) {
flakey := initFlakeyDevice(t, strings.Replace(t.Name(), "/", "_", -1), fsType, mkfsOpt, fsMountOpt)
root := flakey.RootFS()

dbPath := filepath.Join(root, "boltdb")
Expand Down Expand Up @@ -186,10 +242,10 @@ type FlakeyDevice interface {
}

// initFlakeyDevice returns FlakeyDevice instance with a given filesystem.
func initFlakeyDevice(t *testing.T, name string, fsType dmflakey.FSType, mntOpt string) FlakeyDevice {
func initFlakeyDevice(t *testing.T, name string, fsType dmflakey.FSType, mkfsOpt string, mntOpt string) FlakeyDevice {
imgDir := t.TempDir()

flakey, err := dmflakey.InitFlakey(name, imgDir, fsType)
flakey, err := dmflakey.InitFlakey(name, imgDir, fsType, mkfsOpt)
require.NoError(t, err, "init flakey %s", name)
t.Cleanup(func() {
assert.NoError(t, flakey.Teardown())
Expand Down Expand Up @@ -240,7 +296,7 @@ func (f *flakeyT) PowerFailure(mntOpt string) error {
}

if err := unix.Mount(f.DevicePath(), f.rootDir, string(f.Filesystem()), 0, mntOpt); err != nil {
return fmt.Errorf("failed to mount rootfs %s: %w", f.rootDir, err)
return fmt.Errorf("failed to mount rootfs %s (%s): %w", f.rootDir, mntOpt, err)
}
return nil
}
Expand Down
Loading