Skip to content

Commit

Permalink
Merge pull request etcd-io#707 from tjungblu/xfs
Browse files Browse the repository at this point in the history
Add basic XFS powerfailure tests
  • Loading branch information
ahrtr authored Mar 13, 2024
2 parents 9109fcb + c27eedc commit c57b353
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 34 deletions.
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`.
// 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

0 comments on commit c57b353

Please sign in to comment.