From 457cbd136ba73146e096d8e560a6f875cbbd6b76 Mon Sep 17 00:00:00 2001 From: Christian Schwarz Date: Wed, 19 Feb 2020 22:51:07 +0100 Subject: [PATCH] WIP fix racy holds fixes #280 --- endpoint/endpoint_zfs.go | 11 +++++-- platformtest/tests/idempotentHold.go | 36 +++++++++------------ platformtest/tests/listHolds.go | 47 ++++++++++++++++++++++++++++ platformtest/tests/tests.go | 1 + zfs/holds.go | 1 + 5 files changed, 73 insertions(+), 23 deletions(-) create mode 100644 platformtest/tests/listHolds.go diff --git a/endpoint/endpoint_zfs.go b/endpoint/endpoint_zfs.go index b31b589..0ca9ad8 100644 --- a/endpoint/endpoint_zfs.go +++ b/endpoint/endpoint_zfs.go @@ -411,6 +411,7 @@ func ListZFSHoldsAndBookmarks(ctx context.Context, fsfilter zfs.DatasetFilter) ( for _, fs := range fss { err := listZFSHoldsAndBookmarksImplFS(ctx, out, fs) if err != nil { + // FIXME if _, ok := err.(*zfs.DatasetDoesNotExist); ok { noop } return nil, errors.Wrapf(err, "list holds and bookmarks on %q", fs.ToString()) } } @@ -420,7 +421,8 @@ func ListZFSHoldsAndBookmarks(ctx context.Context, fsfilter zfs.DatasetFilter) ( func listZFSHoldsAndBookmarksImplFS(ctx context.Context, out *ListHoldsAndBookmarksOutput, fs *zfs.DatasetPath) error { fsvs, err := zfs.ZFSListFilesystemVersions(fs, nil) if err != nil { - return errors.Wrapf(err, "list filesystem versions of %q", fs) + // FIXME if _, ok := err.(*zfs.DatasetDoesNotExist); ok { noop } + return errors.Wrapf(err, "list filesystem versions of %q", fs.ToString()) } for _, v := range fsvs { switch v.Type { @@ -429,7 +431,12 @@ func listZFSHoldsAndBookmarksImplFS(ctx context.Context, out *ListHoldsAndBookma case zfs.Snapshot: holds, err := zfs.ZFSHolds(ctx, fs.ToString(), v.Name) if err != nil { - return errors.Wrapf(err, "get holds of %q", v.ToAbsPath(fs)) + if _, ok := err.(*zfs.DatasetDoesNotExist); ok { + holds = []string{} + // fallthrough + } else { + return errors.Wrapf(err, "get holds of %q", v.ToAbsPath(fs)) + } } for _, tag := range holds { listZFSHoldsAndBookmarksImplSnapshotTryParseHold(ctx, out, fs, v, tag) diff --git a/platformtest/tests/idempotentHold.go b/platformtest/tests/idempotentHold.go index 575cb4f..8eef904 100644 --- a/platformtest/tests/idempotentHold.go +++ b/platformtest/tests/idempotentHold.go @@ -3,6 +3,7 @@ package tests import ( "fmt" + "github.com/stretchr/testify/require" "github.com/zrepl/zrepl/platformtest" "github.com/zrepl/zrepl/zfs" ) @@ -13,35 +14,28 @@ func IdempotentHold(ctx *platformtest.Context) { DESTROYROOT CREATEROOT + "foo bar" - + "foo bar@1" - `) - defer platformtest.Run(ctx, platformtest.PanicErr, ctx.RootDataset, ` - R zfs release zrepl_platformtest "${ROOTDS}/foo bar@1" - - "foo bar@1" - - "foo bar" + + "foo bar@1 2" `) fs := fmt.Sprintf("%s/foo bar", ctx.RootDataset) - v1 := sendArgVersion(fs, "@1") + snap := sendArgVersion(fs, "@1 2") tag := "zrepl_platformtest" - err := zfs.ZFSHold(ctx, fs, v1, tag) + err := zfs.ZFSHold(ctx, fs, snap, tag) if err != nil { panic(err) } - err = zfs.ZFSHold(ctx, fs, v1, tag) - if err != nil { - panic(err) - } - - vnonexistent := zfs.ZFSSendArgVersion{ - RelName: "@nonexistent", - GUID: 0xbadf00d, - } - err = zfs.ZFSHold(ctx, fs, vnonexistent, tag) - if err == nil { - panic("still expecting error for nonexistent snapshot") - } + // existing holds + holds, err := zfs.ZFSHolds(ctx, fs, "1 2") + require.NoError(ctx, err) + require.Equal(ctx, []string{tag}, holds) + holds, err = zfs.ZFSHolds(ctx, fs, "non existent") + ctx.Logf("holds=%v", holds) + ctx.Logf("errT=%T", err) + ctx.Logf("err=%s", err) + notExist, ok := err.(*zfs.DatasetDoesNotExist) + require.True(ctx, ok) + require.Equal(ctx, fs+"@non existent", notExist.Path) } diff --git a/platformtest/tests/listHolds.go b/platformtest/tests/listHolds.go new file mode 100644 index 0000000..2bdff7e --- /dev/null +++ b/platformtest/tests/listHolds.go @@ -0,0 +1,47 @@ +package tests + +import ( + "fmt" + + "github.com/zrepl/zrepl/platformtest" + "github.com/zrepl/zrepl/zfs" +) + +func ListHolds(ctx *platformtest.Context) { + + platformtest.Run(ctx, platformtest.PanicErr, ctx.RootDataset, ` + DESTROYROOT + CREATEROOT + + "foo bar" + + "foo bar@1" + `) + defer platformtest.Run(ctx, platformtest.PanicErr, ctx.RootDataset, ` + R zfs release zrepl_platformtest "${ROOTDS}/foo bar@1" + - "foo bar@1" + - "foo bar" + `) + + fs := fmt.Sprintf("%s/foo bar", ctx.RootDataset) + v1 := sendArgVersion(fs, "@1") + + tag := "zrepl_platformtest" + err := zfs.ZFSHold(ctx, fs, v1, tag) + if err != nil { + panic(err) + } + + err = zfs.ZFSHold(ctx, fs, v1, tag) + if err != nil { + panic(err) + } + + vnonexistent := zfs.ZFSSendArgVersion{ + RelName: "@nonexistent", + GUID: 0xbadf00d, + } + err = zfs.ZFSHold(ctx, fs, vnonexistent, tag) + if err == nil { + panic("still expecting error for nonexistent snapshot") + } + +} diff --git a/platformtest/tests/tests.go b/platformtest/tests/tests.go index 0853399..baf3d7e 100644 --- a/platformtest/tests/tests.go +++ b/platformtest/tests/tests.go @@ -31,4 +31,5 @@ var Cases = []Case{ SendArgsValidationEncryptedSendOfUnencryptedDatasetForbidden, SendArgsValidationResumeTokenEncryptionMismatchForbidden, SendArgsValidationResumeTokenDifferentFilesystemForbidden, + ListHolds, } diff --git a/zfs/holds.go b/zfs/holds.go index f550997..75f7222 100644 --- a/zfs/holds.go +++ b/zfs/holds.go @@ -59,6 +59,7 @@ success: return nil } +// If the snapshot does not exist, the returned error is of type *DatasetDoesNotExist func ZFSHolds(ctx context.Context, fs, snap string) ([]string, error) { if err := validateZFSFilesystem(fs); err != nil { return nil, errors.Wrap(err, "`fs` is not a valid filesystem path")