Skip to content

Commit

Permalink
Add basic XFS powerfailure tests
Browse files Browse the repository at this point in the history
This also introduces mkfs options, in case we need to accomodate for
non-default parameters here in the future.

Signed-off-by: Thomas Jungblut <[email protected]>
(cherry picked from commit c27eedc)
Signed-off-by: Wei Fu <[email protected]>
  • Loading branch information
tjungblu authored and fuweid committed Aug 9, 2024
1 parent 0719297 commit 70ab151
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 33 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/robustness_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ jobs:
with:
go-version: ${{ steps.goversion.outputs.goversion }}
- run: |
set -euo pipefail
sudo apt-get install -y dmsetup xfsprogs
make gofail-enable
# build bbolt with failpoint
go install ./cmd/bbolt
Expand Down
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`.
// 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 @@ -185,10 +241,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 @@ -239,7 +295,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

0 comments on commit 70ab151

Please sign in to comment.