zfs: use exec.CommandContext everywhere

Co-authored-by: InsanePrawn <insane.prawny@gmail.com>
This commit is contained in:
InsanePrawn 2020-03-27 00:57:33 +01:00 committed by Christian Schwarz
parent 3187129672
commit 9568e46f05
24 changed files with 133 additions and 126 deletions

View File

@ -99,7 +99,7 @@ func doMigratePlaceholder0_1(sc *cli.Subcommand, args []string) error {
} }
for _, fs := range wi.fss { for _, fs := range wi.fss {
fmt.Printf("\t%q ... ", fs.ToString()) fmt.Printf("\t%q ... ", fs.ToString())
r, err := zfs.ZFSMigrateHashBasedPlaceholderToCurrent(fs, migratePlaceholder0_1Args.dryRun) r, err := zfs.ZFSMigrateHashBasedPlaceholderToCurrent(ctx, fs, migratePlaceholder0_1Args.dryRun)
if err != nil { if err != nil {
fmt.Printf("error: %s\n", err) fmt.Printf("error: %s\n", err)
} else if !r.NeedsModification { } else if !r.NeedsModification {
@ -264,7 +264,7 @@ func doMigrateReplicationCursorFS(ctx context.Context, v1CursorJobs []job.Job, f
if migrateReplicationCursorArgs.dryRun { if migrateReplicationCursorArgs.dryRun {
succ.Printf("DRY RUN\n") succ.Printf("DRY RUN\n")
} else { } else {
if err := zfs.ZFSDestroyFilesystemVersion(fs, oldCursor); err != nil { if err := zfs.ZFSDestroyFilesystemVersion(ctx, fs, oldCursor); err != nil {
return err return err
} }
} }

View File

@ -49,6 +49,7 @@ func runTestFilterCmd(subcommand *cli.Subcommand, args []string) error {
} }
conf := subcommand.Config() conf := subcommand.Config()
ctx := context.Background()
var confFilter config.FilesystemsFilter var confFilter config.FilesystemsFilter
job, err := conf.Job(testFilterArgs.job) job, err := conf.Job(testFilterArgs.job)
@ -75,7 +76,7 @@ func runTestFilterCmd(subcommand *cli.Subcommand, args []string) error {
if testFilterArgs.input != "" { if testFilterArgs.input != "" {
fsnames = []string{testFilterArgs.input} fsnames = []string{testFilterArgs.input}
} else { } else {
out, err := zfs.ZFSList([]string{"name"}) out, err := zfs.ZFSList(ctx, []string{"name"})
if err != nil { if err != nil {
return fmt.Errorf("could not list ZFS filesystems: %s", err) return fmt.Errorf("could not list ZFS filesystems: %s", err)
} }
@ -139,10 +140,11 @@ var testPlaceholder = &cli.Subcommand{
func runTestPlaceholder(subcommand *cli.Subcommand, args []string) error { func runTestPlaceholder(subcommand *cli.Subcommand, args []string) error {
var checkDPs []*zfs.DatasetPath var checkDPs []*zfs.DatasetPath
ctx := context.Background()
// all actions first // all actions first
if testPlaceholderArgs.all { if testPlaceholderArgs.all {
out, err := zfs.ZFSList([]string{"name"}) out, err := zfs.ZFSList(ctx, []string{"name"})
if err != nil { if err != nil {
return errors.Wrap(err, "could not list ZFS filesystems") return errors.Wrap(err, "could not list ZFS filesystems")
} }
@ -166,7 +168,7 @@ func runTestPlaceholder(subcommand *cli.Subcommand, args []string) error {
fmt.Printf("IS_PLACEHOLDER\tDATASET\tzrepl:placeholder\n") fmt.Printf("IS_PLACEHOLDER\tDATASET\tzrepl:placeholder\n")
for _, dp := range checkDPs { for _, dp := range checkDPs {
ph, err := zfs.ZFSGetFilesystemPlaceholderState(dp) ph, err := zfs.ZFSGetFilesystemPlaceholderState(ctx, dp)
if err != nil { if err != nil {
return errors.Wrap(err, "cannot get placeholder state") return errors.Wrap(err, "cannot get placeholder state")
} }

View File

@ -277,7 +277,7 @@ func snapshot(a args, u updater) state {
jobCallback := hooks.NewCallbackHookForFilesystem("snapshot", fs, func(_ context.Context) (err error) { jobCallback := hooks.NewCallbackHookForFilesystem("snapshot", fs, func(_ context.Context) (err error) {
l.Debug("create snapshot") l.Debug("create snapshot")
err = zfs.ZFSSnapshot(fs, snapname, false) // TODO propagate context to ZFSSnapshot err = zfs.ZFSSnapshot(a.ctx, fs, snapname, false) // TODO propagate context to ZFSSnapshot
if err != nil { if err != nil {
l.WithError(err).Error("cannot create snapshot") l.WithError(err).Error("cannot create snapshot")
} }

View File

@ -511,7 +511,7 @@ func (s *Receiver) ListFilesystems(ctx context.Context, req *pdu.ListFilesystemR
fss := make([]*pdu.Filesystem, 0, len(filtered)) fss := make([]*pdu.Filesystem, 0, len(filtered))
for _, a := range filtered { for _, a := range filtered {
l := getLogger(ctx).WithField("fs", a) l := getLogger(ctx).WithField("fs", a)
ph, err := zfs.ZFSGetFilesystemPlaceholderState(a) ph, err := zfs.ZFSGetFilesystemPlaceholderState(ctx, a)
if err != nil { if err != nil {
l.WithError(err).Error("error getting placeholder state") l.WithError(err).Error("error getting placeholder state")
return nil, errors.Wrapf(err, "cannot get placeholder state for fs %q", a) return nil, errors.Wrapf(err, "cannot get placeholder state for fs %q", a)
@ -637,7 +637,7 @@ func (s *Receiver) Receive(ctx context.Context, req *pdu.ReceiveReq, receive zfs
if v.Path.Equal(lp) { if v.Path.Equal(lp) {
return false return false
} }
ph, err := zfs.ZFSGetFilesystemPlaceholderState(v.Path) ph, err := zfs.ZFSGetFilesystemPlaceholderState(ctx, v.Path)
getLogger(ctx). getLogger(ctx).
WithField("fs", v.Path.ToString()). WithField("fs", v.Path.ToString()).
WithField("placeholder_state", fmt.Sprintf("%#v", ph)). WithField("placeholder_state", fmt.Sprintf("%#v", ph)).
@ -661,7 +661,7 @@ func (s *Receiver) Receive(ctx context.Context, req *pdu.ReceiveReq, receive zfs
} }
l := getLogger(ctx).WithField("placeholder_fs", v.Path) l := getLogger(ctx).WithField("placeholder_fs", v.Path)
l.Debug("create placeholder filesystem") l.Debug("create placeholder filesystem")
err := zfs.ZFSCreatePlaceholderFilesystem(v.Path) err := zfs.ZFSCreatePlaceholderFilesystem(ctx, v.Path)
if err != nil { if err != nil {
l.WithError(err).Error("cannot create placeholder filesystem") l.WithError(err).Error("cannot create placeholder filesystem")
visitErr = err visitErr = err
@ -681,19 +681,19 @@ func (s *Receiver) Receive(ctx context.Context, req *pdu.ReceiveReq, receive zfs
// determine whether we need to rollback the filesystem / change its placeholder state // determine whether we need to rollback the filesystem / change its placeholder state
var clearPlaceholderProperty bool var clearPlaceholderProperty bool
var recvOpts zfs.RecvOptions var recvOpts zfs.RecvOptions
ph, err := zfs.ZFSGetFilesystemPlaceholderState(lp) ph, err := zfs.ZFSGetFilesystemPlaceholderState(ctx, lp)
if err == nil && ph.FSExists && ph.IsPlaceholder { if err == nil && ph.FSExists && ph.IsPlaceholder {
recvOpts.RollbackAndForceRecv = true recvOpts.RollbackAndForceRecv = true
clearPlaceholderProperty = true clearPlaceholderProperty = true
} }
if clearPlaceholderProperty { if clearPlaceholderProperty {
if err := zfs.ZFSSetPlaceholder(lp, false); err != nil { if err := zfs.ZFSSetPlaceholder(ctx, lp, false); err != nil {
return nil, fmt.Errorf("cannot clear placeholder property for forced receive: %s", err) return nil, fmt.Errorf("cannot clear placeholder property for forced receive: %s", err)
} }
} }
if req.ClearResumeToken && ph.FSExists { if req.ClearResumeToken && ph.FSExists {
if err := zfs.ZFSRecvClearResumeToken(lp.ToString()); err != nil { if err := zfs.ZFSRecvClearResumeToken(ctx, lp.ToString()); err != nil {
return nil, errors.Wrap(err, "cannot clear resume token") return nil, errors.Wrap(err, "cannot clear resume token")
} }
} }

View File

@ -170,7 +170,7 @@ func MoveReplicationCursor(ctx context.Context, fs string, target *zfs.ZFSSendAr
// idempotently create bookmark (guid is encoded in it, hence we'll most likely add a new one // idempotently create bookmark (guid is encoded in it, hence we'll most likely add a new one
// cleanup the old one afterwards // cleanup the old one afterwards
err = zfs.ZFSBookmark(fs, *target, bookmarkname) err = zfs.ZFSBookmark(ctx, fs, *target, bookmarkname)
if err != nil { if err != nil {
if err == zfs.ErrBookmarkCloningNotSupported { if err == zfs.ErrBookmarkCloningNotSupported {
return nil, err // TODO go1.13 use wrapping return nil, err // TODO go1.13 use wrapping
@ -221,7 +221,7 @@ func HoldStep(ctx context.Context, fs string, v *zfs.ZFSSendArgVersion, jobID Jo
return errors.Wrap(err, "create step bookmark: determine bookmark name") return errors.Wrap(err, "create step bookmark: determine bookmark name")
} }
// idempotently create bookmark // idempotently create bookmark
err = zfs.ZFSBookmark(fs, *v, bmname) err = zfs.ZFSBookmark(ctx, fs, *v, bmname)
if err != nil { if err != nil {
if err == zfs.ErrBookmarkCloningNotSupported { if err == zfs.ErrBookmarkCloningNotSupported {
// TODO we could actually try to find a local snapshot that has the requested GUID // TODO we could actually try to find a local snapshot that has the requested GUID
@ -269,7 +269,7 @@ func ReleaseStep(ctx context.Context, fs string, v *zfs.ZFSSendArgVersion, jobID
} }
// idempotently destroy bookmark // idempotently destroy bookmark
if err := zfs.ZFSDestroyIdempotent(bmname); err != nil { if err := zfs.ZFSDestroyIdempotent(ctx, bmname); err != nil {
return errors.Wrap(err, "step release: bookmark destroy: zfs") return errors.Wrap(err, "step release: bookmark destroy: zfs")
} }

View File

@ -100,7 +100,7 @@ func destroyBookmarksOlderThan(ctx context.Context, fs string, mostRecent *zfs.Z
// FIXME use batch destroy, must adopt code to handle bookmarks // FIXME use batch destroy, must adopt code to handle bookmarks
for _, v := range destroy { for _, v := range destroy {
if err := zfs.ZFSDestroyIdempotent(v.ToAbsPath(fsp)); err != nil { if err := zfs.ZFSDestroyIdempotent(ctx, v.ToAbsPath(fsp)); err != nil {
return nil, errors.Wrap(err, "destroy bookmark") return nil, errors.Wrap(err, "destroy bookmark")
} }
} }

View File

@ -45,7 +45,7 @@ func CreateOrReplaceZpool(ctx context.Context, e Execer, args ZpoolCreateArgs) (
} }
// export pool if it already exists (idempotence) // export pool if it already exists (idempotence)
if _, err := zfs.ZFSGetRawAnySource(args.PoolName, []string{"name"}); err != nil { if _, err := zfs.ZFSGetRawAnySource(ctx, args.PoolName, []string{"name"}); err != nil {
if _, ok := err.(*zfs.DatasetDoesNotExist); ok { if _, ok := err.(*zfs.DatasetDoesNotExist); ok {
// we'll create it shortly // we'll create it shortly
} else { } else {

View File

@ -54,7 +54,7 @@ func rollupReleaseTest(ctx *platformtest.Context, cb func(fs string) []rollupRel
func RollupReleaseIncluding(ctx *platformtest.Context) { func RollupReleaseIncluding(ctx *platformtest.Context) {
rollupReleaseTest(ctx, func(fs string) []rollupReleaseExpectTags { rollupReleaseTest(ctx, func(fs string) []rollupReleaseExpectTags {
guid5, err := zfs.ZFSGetGUID(fs, "@5") guid5, err := zfs.ZFSGetGUID(ctx, fs, "@5")
require.NoError(ctx, err) require.NoError(ctx, err)
err = zfs.ZFSReleaseAllOlderAndIncludingGUID(ctx, fs, guid5, "zrepl_platformtest") err = zfs.ZFSReleaseAllOlderAndIncludingGUID(ctx, fs, guid5, "zrepl_platformtest")
@ -73,7 +73,7 @@ func RollupReleaseIncluding(ctx *platformtest.Context) {
func RollupReleaseExcluding(ctx *platformtest.Context) { func RollupReleaseExcluding(ctx *platformtest.Context) {
rollupReleaseTest(ctx, func(fs string) []rollupReleaseExpectTags { rollupReleaseTest(ctx, func(fs string) []rollupReleaseExpectTags {
guid5, err := zfs.ZFSGetGUID(fs, "@5") guid5, err := zfs.ZFSGetGUID(ctx, fs, "@5")
require.NoError(ctx, err) require.NoError(ctx, err)
err = zfs.ZFSReleaseAllOlderThanGUID(ctx, fs, guid5, "zrepl_platformtest") err = zfs.ZFSReleaseAllOlderThanGUID(ctx, fs, guid5, "zrepl_platformtest")
@ -92,13 +92,13 @@ func RollupReleaseExcluding(ctx *platformtest.Context) {
func RollupReleaseMostRecentIsBookmarkWithoutSnapshot(ctx *platformtest.Context) { func RollupReleaseMostRecentIsBookmarkWithoutSnapshot(ctx *platformtest.Context) {
rollupReleaseTest(ctx, func(fs string) []rollupReleaseExpectTags { rollupReleaseTest(ctx, func(fs string) []rollupReleaseExpectTags {
guid5, err := zfs.ZFSGetGUID(fs, "#5") guid5, err := zfs.ZFSGetGUID(ctx, fs, "#5")
require.NoError(ctx, err) require.NoError(ctx, err)
err = zfs.ZFSRelease(ctx, "zrepl_platformtest", fs+"@5") err = zfs.ZFSRelease(ctx, "zrepl_platformtest", fs+"@5")
require.NoError(ctx, err) require.NoError(ctx, err)
err = zfs.ZFSDestroy(fs + "@5") err = zfs.ZFSDestroy(ctx, fs+"@5")
require.NoError(ctx, err) require.NoError(ctx, err)
err = zfs.ZFSReleaseAllOlderAndIncludingGUID(ctx, fs, guid5, "zrepl_platformtest") err = zfs.ZFSReleaseAllOlderAndIncludingGUID(ctx, fs, guid5, "zrepl_platformtest")
@ -117,7 +117,7 @@ func RollupReleaseMostRecentIsBookmarkWithoutSnapshot(ctx *platformtest.Context)
func RollupReleaseMostRecentIsBookmarkAndSnapshotStillExists(ctx *platformtest.Context) { func RollupReleaseMostRecentIsBookmarkAndSnapshotStillExists(ctx *platformtest.Context) {
rollupReleaseTest(ctx, func(fs string) []rollupReleaseExpectTags { rollupReleaseTest(ctx, func(fs string) []rollupReleaseExpectTags {
guid5, err := zfs.ZFSGetGUID(fs, "#5") guid5, err := zfs.ZFSGetGUID(ctx, fs, "#5")
require.NoError(ctx, err) require.NoError(ctx, err)
err = zfs.ZFSReleaseAllOlderAndIncludingGUID(ctx, fs, guid5, "zrepl_platformtest") err = zfs.ZFSReleaseAllOlderAndIncludingGUID(ctx, fs, guid5, "zrepl_platformtest")

View File

@ -17,14 +17,14 @@ func GetNonexistent(ctx *platformtest.Context) {
`) `)
// test raw // test raw
_, err := zfs.ZFSGetRawAnySource(fmt.Sprintf("%s/foo bar", ctx.RootDataset), []string{"name"}) _, err := zfs.ZFSGetRawAnySource(ctx, fmt.Sprintf("%s/foo bar", ctx.RootDataset), []string{"name"})
if err != nil { if err != nil {
panic(err) panic(err)
} }
// test nonexistent filesystem // test nonexistent filesystem
nonexistent := fmt.Sprintf("%s/nonexistent filesystem", ctx.RootDataset) nonexistent := fmt.Sprintf("%s/nonexistent filesystem", ctx.RootDataset)
props, err := zfs.ZFSGetRawAnySource(nonexistent, []string{"name"}) props, err := zfs.ZFSGetRawAnySource(ctx, nonexistent, []string{"name"})
if err == nil { if err == nil {
panic(props) panic(props)
} }
@ -37,7 +37,7 @@ func GetNonexistent(ctx *platformtest.Context) {
// test nonexistent snapshot // test nonexistent snapshot
nonexistent = fmt.Sprintf("%s/foo bar@non existent", ctx.RootDataset) nonexistent = fmt.Sprintf("%s/foo bar@non existent", ctx.RootDataset)
props, err = zfs.ZFSGetRawAnySource(nonexistent, []string{"name"}) props, err = zfs.ZFSGetRawAnySource(ctx, nonexistent, []string{"name"})
if err == nil { if err == nil {
panic(props) panic(props)
} }
@ -50,7 +50,7 @@ func GetNonexistent(ctx *platformtest.Context) {
// test nonexistent bookmark // test nonexistent bookmark
nonexistent = fmt.Sprintf("%s/foo bar#non existent", ctx.RootDataset) nonexistent = fmt.Sprintf("%s/foo bar#non existent", ctx.RootDataset)
props, err = zfs.ZFSGetRawAnySource(nonexistent, []string{"name"}) props, err = zfs.ZFSGetRawAnySource(ctx, nonexistent, []string{"name"})
if err == nil { if err == nil {
panic(props) panic(props)
} }

View File

@ -14,8 +14,8 @@ import (
"github.com/zrepl/zrepl/zfs" "github.com/zrepl/zrepl/zfs"
) )
func sendArgVersion(fs, relName string) zfs.ZFSSendArgVersion { func sendArgVersion(ctx *platformtest.Context, fs, relName string) zfs.ZFSSendArgVersion {
guid, err := zfs.ZFSGetGUID(fs, relName) guid, err := zfs.ZFSGetGUID(ctx, fs, relName)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -33,7 +33,7 @@ func mustDatasetPath(fs string) *zfs.DatasetPath {
return p return p
} }
func mustSnapshot(snap string) { func mustSnapshot(ctx *platformtest.Context, snap string) {
if err := zfs.EntityNamecheck(snap, zfs.EntityTypeSnapshot); err != nil { if err := zfs.EntityNamecheck(snap, zfs.EntityTypeSnapshot); err != nil {
panic(err) panic(err)
} }
@ -41,14 +41,14 @@ func mustSnapshot(snap string) {
if len(comps) != 2 { if len(comps) != 2 {
panic(comps) panic(comps)
} }
err := zfs.ZFSSnapshot(mustDatasetPath(comps[0]), comps[1], false) err := zfs.ZFSSnapshot(ctx, mustDatasetPath(comps[0]), comps[1], false)
if err != nil { if err != nil {
panic(err) panic(err)
} }
} }
func mustGetProps(entity string) zfs.ZFSPropCreateTxgAndGuidProps { func mustGetProps(ctx *platformtest.Context, entity string) zfs.ZFSPropCreateTxgAndGuidProps {
props, err := zfs.ZFSGetCreateTXGAndGuid(entity) props, err := zfs.ZFSGetCreateTXGAndGuid(ctx, entity)
check(err) check(err)
return props return props
} }
@ -87,7 +87,7 @@ type resumeSituation struct {
func makeDummyDataSnapshots(ctx *platformtest.Context, sendFS string) (situation dummySnapshotSituation) { func makeDummyDataSnapshots(ctx *platformtest.Context, sendFS string) (situation dummySnapshotSituation) {
situation.sendFS = sendFS situation.sendFS = sendFS
sendFSMount, err := zfs.ZFSGetMountpoint(sendFS) sendFSMount, err := zfs.ZFSGetMountpoint(ctx, sendFS)
require.NoError(ctx, err) require.NoError(ctx, err)
require.True(ctx, sendFSMount.Mounted) require.True(ctx, sendFSMount.Mounted)
@ -95,13 +95,13 @@ func makeDummyDataSnapshots(ctx *platformtest.Context, sendFS string) (situation
situation.dummyDataLen = dummyLen situation.dummyDataLen = dummyLen
writeDummyData(path.Join(sendFSMount.Mountpoint, "dummy_data"), dummyLen) writeDummyData(path.Join(sendFSMount.Mountpoint, "dummy_data"), dummyLen)
mustSnapshot(sendFS + "@a snapshot") mustSnapshot(ctx, sendFS+"@a snapshot")
snapA := sendArgVersion(sendFS, "@a snapshot") snapA := sendArgVersion(ctx, sendFS, "@a snapshot")
situation.snapA = &snapA situation.snapA = &snapA
writeDummyData(path.Join(sendFSMount.Mountpoint, "dummy_data"), dummyLen) writeDummyData(path.Join(sendFSMount.Mountpoint, "dummy_data"), dummyLen)
mustSnapshot(sendFS + "@b snapshot") mustSnapshot(ctx, sendFS+"@b snapshot")
snapB := sendArgVersion(sendFS, "@b snapshot") snapB := sendArgVersion(ctx, sendFS, "@b snapshot")
situation.snapB = &snapB situation.snapB = &snapB
return situation return situation

View File

@ -19,22 +19,22 @@ func IdempotentBookmark(ctx *platformtest.Context) {
fs := fmt.Sprintf("%s/foo bar", ctx.RootDataset) fs := fmt.Sprintf("%s/foo bar", ctx.RootDataset)
asnap := sendArgVersion(fs, "@a snap") asnap := sendArgVersion(ctx, fs, "@a snap")
anotherSnap := sendArgVersion(fs, "@another snap") anotherSnap := sendArgVersion(ctx, fs, "@another snap")
err := zfs.ZFSBookmark(fs, asnap, "a bookmark") err := zfs.ZFSBookmark(ctx, fs, asnap, "a bookmark")
if err != nil { if err != nil {
panic(err) panic(err)
} }
// do it again, should be idempotent // do it again, should be idempotent
err = zfs.ZFSBookmark(fs, asnap, "a bookmark") err = zfs.ZFSBookmark(ctx, fs, asnap, "a bookmark")
if err != nil { if err != nil {
panic(err) panic(err)
} }
// should fail for another snapshot // should fail for another snapshot
err = zfs.ZFSBookmark(fs, anotherSnap, "a bookmark") err = zfs.ZFSBookmark(ctx, fs, anotherSnap, "a bookmark")
if err == nil { if err == nil {
panic(err) panic(err)
} }
@ -43,12 +43,12 @@ func IdempotentBookmark(ctx *platformtest.Context) {
} }
// destroy the snapshot // destroy the snapshot
if err := zfs.ZFSDestroy(fmt.Sprintf("%s@a snap", fs)); err != nil { if err := zfs.ZFSDestroy(ctx, fmt.Sprintf("%s@a snap", fs)); err != nil {
panic(err) panic(err)
} }
// do it again, should fail with special error type // do it again, should fail with special error type
err = zfs.ZFSBookmark(fs, asnap, "a bookmark") err = zfs.ZFSBookmark(ctx, fs, asnap, "a bookmark")
if err == nil { if err == nil {
panic(err) panic(err)
} }

View File

@ -18,8 +18,8 @@ func IdempotentDestroy(ctx *platformtest.Context) {
`) `)
fs := fmt.Sprintf("%s/foo bar", ctx.RootDataset) fs := fmt.Sprintf("%s/foo bar", ctx.RootDataset)
asnap := sendArgVersion(fs, "@a snap") asnap := sendArgVersion(ctx, fs, "@a snap")
err := zfs.ZFSBookmark(fs, asnap, "a bookmark") err := zfs.ZFSBookmark(ctx, fs, asnap, "a bookmark")
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -41,17 +41,17 @@ func IdempotentDestroy(ctx *platformtest.Context) {
log.Printf("SUBBEGIN testing idempotent destroy %q for path %q", c.description, c.path) log.Printf("SUBBEGIN testing idempotent destroy %q for path %q", c.description, c.path)
log.Println("destroy existing") log.Println("destroy existing")
err = zfs.ZFSDestroy(c.path) err = zfs.ZFSDestroy(ctx, c.path)
if err != nil { if err != nil {
panic(err) panic(err)
} }
log.Println("destroy again, non-idempotently, must error") log.Println("destroy again, non-idempotently, must error")
err = zfs.ZFSDestroy(c.path) err = zfs.ZFSDestroy(ctx, c.path)
if _, ok := err.(*zfs.DatasetDoesNotExist); !ok { if _, ok := err.(*zfs.DatasetDoesNotExist); !ok {
panic(fmt.Sprintf("%T: %s", err, err)) panic(fmt.Sprintf("%T: %s", err, err))
} }
log.Println("destroy again, idempotently, must not error") log.Println("destroy again, idempotently, must not error")
err = zfs.ZFSDestroyIdempotent(c.path) err = zfs.ZFSDestroyIdempotent(ctx, c.path)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -62,12 +62,12 @@ func IdempotentDestroy(ctx *platformtest.Context) {
} }
// also test idempotent destroy for cases where the parent dataset does not exist // also test idempotent destroy for cases where the parent dataset does not exist
err = zfs.ZFSDestroyIdempotent(fmt.Sprintf("%s/not foo bar@nonexistent snapshot", ctx.RootDataset)) err = zfs.ZFSDestroyIdempotent(ctx, fmt.Sprintf("%s/not foo bar@nonexistent snapshot", ctx.RootDataset))
if err != nil { if err != nil {
panic(err) panic(err)
} }
err = zfs.ZFSDestroyIdempotent(fmt.Sprintf("%s/not foo bar#nonexistent bookmark", ctx.RootDataset)) err = zfs.ZFSDestroyIdempotent(ctx, fmt.Sprintf("%s/not foo bar#nonexistent bookmark", ctx.RootDataset))
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -22,7 +22,7 @@ func IdempotentHold(ctx *platformtest.Context) {
`) `)
fs := fmt.Sprintf("%s/foo bar", ctx.RootDataset) fs := fmt.Sprintf("%s/foo bar", ctx.RootDataset)
v1 := sendArgVersion(fs, "@1") v1 := sendArgVersion(ctx, fs, "@1")
tag := "zrepl_platformtest" tag := "zrepl_platformtest"
err := zfs.ZFSHold(ctx, fs, v1, tag) err := zfs.ZFSHold(ctx, fs, v1, tag)

View File

@ -5,6 +5,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/zrepl/zrepl/endpoint" "github.com/zrepl/zrepl/endpoint"
"github.com/zrepl/zrepl/platformtest" "github.com/zrepl/zrepl/platformtest"
"github.com/zrepl/zrepl/zfs" "github.com/zrepl/zrepl/zfs"
@ -31,7 +32,7 @@ func ReplicationCursor(ctx *platformtest.Context) {
} }
fs := ds.ToString() fs := ds.ToString()
snap := sendArgVersion(fs, "@1 with space") snap := sendArgVersion(ctx, fs, "@1 with space")
destroyed, err := endpoint.MoveReplicationCursor(ctx, fs, &snap, jobid) destroyed, err := endpoint.MoveReplicationCursor(ctx, fs, &snap, jobid)
if err != nil { if err != nil {
@ -39,7 +40,7 @@ func ReplicationCursor(ctx *platformtest.Context) {
} }
assert.Empty(ctx, destroyed) assert.Empty(ctx, destroyed)
snapProps, err := zfs.ZFSGetCreateTXGAndGuid(snap.FullPath(fs)) snapProps, err := zfs.ZFSGetCreateTXGAndGuid(ctx, snap.FullPath(fs))
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -59,7 +60,7 @@ func ReplicationCursor(ctx *platformtest.Context) {
cursor1BookmarkName, err := endpoint.ReplicationCursorBookmarkName(fs, snap.GUID, jobid) cursor1BookmarkName, err := endpoint.ReplicationCursorBookmarkName(fs, snap.GUID, jobid)
require.NoError(ctx, err) require.NoError(ctx, err)
snap2 := sendArgVersion(fs, "@2 with space") snap2 := sendArgVersion(ctx, fs, "@2 with space")
destroyed, err = endpoint.MoveReplicationCursor(ctx, fs, &snap2, jobid) destroyed, err = endpoint.MoveReplicationCursor(ctx, fs, &snap2, jobid)
require.NoError(ctx, err) require.NoError(ctx, err)
require.Equal(ctx, 1, len(destroyed)) require.Equal(ctx, 1, len(destroyed))

View File

@ -16,7 +16,7 @@ type resumeTokenTest struct {
func (rtt *resumeTokenTest) Test(t *platformtest.Context) { func (rtt *resumeTokenTest) Test(t *platformtest.Context) {
resumeSendSupported, err := zfs.ResumeSendSupported() resumeSendSupported, err := zfs.ResumeSendSupported(t)
if err != nil { if err != nil {
t.Errorf("cannot determine whether resume supported: %T %s", err, err) t.Errorf("cannot determine whether resume supported: %T %s", err, err)
t.FailNow() t.FailNow()

View File

@ -23,7 +23,7 @@ func SendArgsValidationEncryptedSendOfUnencryptedDatasetForbidden(ctx *platformt
`) `)
fs := fmt.Sprintf("%s/send er", ctx.RootDataset) fs := fmt.Sprintf("%s/send er", ctx.RootDataset)
props := mustGetProps(fs + "@a snap") props := mustGetProps(ctx, fs+"@a snap")
sendArgs := zfs.ZFSSendArgs{ sendArgs := zfs.ZFSSendArgs{
FS: fs, FS: fs,
@ -58,7 +58,7 @@ func SendArgsValidationResumeTokenEncryptionMismatchForbidden(ctx *platformtest.
if !supported { if !supported {
ctx.SkipNow() ctx.SkipNow()
} }
supported, err = zfs.ResumeSendSupported() supported, err = zfs.ResumeSendSupported(ctx)
check(err) check(err)
if !supported { if !supported {
ctx.SkipNow() ctx.SkipNow()
@ -149,7 +149,7 @@ func SendArgsValidationResumeTokenDifferentFilesystemForbidden(ctx *platformtest
if !supported { if !supported {
ctx.SkipNow() ctx.SkipNow()
} }
supported, err = zfs.ResumeSendSupported() supported, err = zfs.ResumeSendSupported(ctx)
check(err) check(err)
if !supported { if !supported {
ctx.SkipNow() ctx.SkipNow()

View File

@ -20,7 +20,7 @@ func UndestroyableSnapshotParsing(t *platformtest.Context) {
R zfs hold zrepl_platformtest "${ROOTDS}/foo bar@4 5 6" R zfs hold zrepl_platformtest "${ROOTDS}/foo bar@4 5 6"
`) `)
err := zfs.ZFSDestroy(fmt.Sprintf("%s/foo bar@1 2 3,4 5 6,7 8 9", t.RootDataset)) err := zfs.ZFSDestroy(t, fmt.Sprintf("%s/foo bar@1 2 3,4 5 6,7 8 9", t.RootDataset))
if err == nil { if err == nil {
panic("expecting destroy error due to hold") panic("expecting destroy error due to hold")
} }

View File

@ -21,7 +21,7 @@ var encryptionCLISupport struct {
func EncryptionCLISupported(ctx context.Context) (bool, error) { func EncryptionCLISupported(ctx context.Context) (bool, error) {
encryptionCLISupport.once.Do(func() { encryptionCLISupport.once.Do(func() {
// "feature discovery" // "feature discovery"
cmd := exec.Command("zfs", "load-key") cmd := exec.CommandContext(ctx, "zfs", "load-key")
output, err := cmd.CombinedOutput() output, err := cmd.CombinedOutput()
if ee, ok := err.(*exec.ExitError); !ok || ok && !ee.Exited() { if ee, ok := err.(*exec.ExitError); !ok || ok && !ee.Exited() {
encryptionCLISupport.err = errors.Wrap(err, "native encryption cli support feature check failed") encryptionCLISupport.err = errors.Wrap(err, "native encryption cli support feature check failed")
@ -50,7 +50,7 @@ func ZFSGetEncryptionEnabled(ctx context.Context, fs string) (enabled bool, err
return false, err return false, err
} }
props, err := zfsGet(fs, []string{"encryption"}, sourceAny) props, err := zfsGet(ctx, fs, []string{"encryption"}, sourceAny)
if err != nil { if err != nil {
return false, errors.Wrap(err, "cannot get `encryption` property") return false, errors.Wrap(err, "cannot get `encryption` property")
} }

View File

@ -2,6 +2,7 @@ package zfs
import ( import (
"bytes" "bytes"
"context"
"crypto/sha512" "crypto/sha512"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
@ -61,10 +62,10 @@ type FilesystemPlaceholderState struct {
// is a placeholder. Note that the property source must be `local` for the returned value to be valid. // is a placeholder. Note that the property source must be `local` for the returned value to be valid.
// //
// For nonexistent FS, err == nil and state.FSExists == false // For nonexistent FS, err == nil and state.FSExists == false
func ZFSGetFilesystemPlaceholderState(p *DatasetPath) (state *FilesystemPlaceholderState, err error) { func ZFSGetFilesystemPlaceholderState(ctx context.Context, p *DatasetPath) (state *FilesystemPlaceholderState, err error) {
state = &FilesystemPlaceholderState{FS: p.ToString()} state = &FilesystemPlaceholderState{FS: p.ToString()}
state.FS = p.ToString() state.FS = p.ToString()
props, err := zfsGet(p.ToString(), []string{PlaceholderPropertyName}, sourceLocal) props, err := zfsGet(ctx, p.ToString(), []string{PlaceholderPropertyName}, sourceLocal)
var _ error = (*DatasetDoesNotExist)(nil) // weak assertion on zfsGet's interface var _ error = (*DatasetDoesNotExist)(nil) // weak assertion on zfsGet's interface
if _, ok := err.(*DatasetDoesNotExist); ok { if _, ok := err.(*DatasetDoesNotExist); ok {
return state, nil return state, nil
@ -77,11 +78,11 @@ func ZFSGetFilesystemPlaceholderState(p *DatasetPath) (state *FilesystemPlacehol
return state, nil return state, nil
} }
func ZFSCreatePlaceholderFilesystem(p *DatasetPath) (err error) { func ZFSCreatePlaceholderFilesystem(ctx context.Context, p *DatasetPath) (err error) {
if p.Length() == 1 { if p.Length() == 1 {
return fmt.Errorf("cannot create %q: pools cannot be created with zfs create", p.ToString()) return fmt.Errorf("cannot create %q: pools cannot be created with zfs create", p.ToString())
} }
cmd := exec.Command(ZFS_BINARY, "create", cmd := exec.CommandContext(ctx, ZFS_BINARY, "create",
"-o", fmt.Sprintf("%s=%s", PlaceholderPropertyName, placeholderPropertyOn), "-o", fmt.Sprintf("%s=%s", PlaceholderPropertyName, placeholderPropertyOn),
"-o", "mountpoint=none", "-o", "mountpoint=none",
p.ToString()) p.ToString())
@ -103,14 +104,14 @@ func ZFSCreatePlaceholderFilesystem(p *DatasetPath) (err error) {
return return
} }
func ZFSSetPlaceholder(p *DatasetPath, isPlaceholder bool) error { func ZFSSetPlaceholder(ctx context.Context, p *DatasetPath, isPlaceholder bool) error {
props := NewZFSProperties() props := NewZFSProperties()
prop := placeholderPropertyOff prop := placeholderPropertyOff
if isPlaceholder { if isPlaceholder {
prop = placeholderPropertyOn prop = placeholderPropertyOn
} }
props.Set(PlaceholderPropertyName, prop) props.Set(PlaceholderPropertyName, prop)
return zfsSet(p.ToString(), props) return zfsSet(ctx, p.ToString(), props)
} }
type MigrateHashBasedPlaceholderReport struct { type MigrateHashBasedPlaceholderReport struct {
@ -119,8 +120,8 @@ type MigrateHashBasedPlaceholderReport struct {
} }
// fs must exist, will panic otherwise // fs must exist, will panic otherwise
func ZFSMigrateHashBasedPlaceholderToCurrent(fs *DatasetPath, dryRun bool) (*MigrateHashBasedPlaceholderReport, error) { func ZFSMigrateHashBasedPlaceholderToCurrent(ctx context.Context, fs *DatasetPath, dryRun bool) (*MigrateHashBasedPlaceholderReport, error) {
st, err := ZFSGetFilesystemPlaceholderState(fs) st, err := ZFSGetFilesystemPlaceholderState(ctx, fs)
if err != nil { if err != nil {
return nil, fmt.Errorf("error getting placeholder state: %s", err) return nil, fmt.Errorf("error getting placeholder state: %s", err)
} }
@ -137,7 +138,7 @@ func ZFSMigrateHashBasedPlaceholderToCurrent(fs *DatasetPath, dryRun bool) (*Mig
return &report, nil return &report, nil
} }
err = ZFSSetPlaceholder(fs, st.IsPlaceholder) err = ZFSSetPlaceholder(ctx, fs, st.IsPlaceholder)
if err != nil { if err != nil {
return nil, fmt.Errorf("error re-writing placeholder property: %s", err) return nil, fmt.Errorf("error re-writing placeholder property: %s", err)
} }

View File

@ -39,10 +39,10 @@ var resumeSendSupportedCheck struct {
err error err error
} }
func ResumeSendSupported() (bool, error) { func ResumeSendSupported(ctx context.Context) (bool, error) {
resumeSendSupportedCheck.once.Do(func() { resumeSendSupportedCheck.once.Do(func() {
// "feature discovery" // "feature discovery"
cmd := exec.Command("zfs", "send") cmd := exec.CommandContext(ctx, "zfs", "send")
output, err := cmd.CombinedOutput() output, err := cmd.CombinedOutput()
if ee, ok := err.(*exec.ExitError); !ok || ok && !ee.Exited() { if ee, ok := err.(*exec.ExitError); !ok || ok && !ee.Exited() {
resumeSendSupportedCheck.err = errors.Wrap(err, "resumable send cli support feature check failed") resumeSendSupportedCheck.err = errors.Wrap(err, "resumable send cli support feature check failed")
@ -155,7 +155,7 @@ func ResumeRecvSupported(ctx context.Context, fs *DatasetPath) (bool, error) {
// FIXME: implement nvlist unpacking in Go and read through libzfs_sendrecv.c // FIXME: implement nvlist unpacking in Go and read through libzfs_sendrecv.c
func ParseResumeToken(ctx context.Context, token string) (*ResumeToken, error) { func ParseResumeToken(ctx context.Context, token string) (*ResumeToken, error) {
if supported, err := ResumeSendSupported(); err != nil { if supported, err := ResumeSendSupported(ctx); err != nil {
return nil, err return nil, err
} else if !supported { } else if !supported {
return nil, ResumeTokenDecodingNotSupported return nil, ResumeTokenDecodingNotSupported
@ -258,7 +258,7 @@ func ZFSGetReceiveResumeTokenOrEmptyStringIfNotSupported(ctx context.Context, fs
return "", nil return "", nil
} }
const prop_receive_resume_token = "receive_resume_token" const prop_receive_resume_token = "receive_resume_token"
props, err := ZFSGet(fs, []string{prop_receive_resume_token}) props, err := ZFSGet(ctx, fs, []string{prop_receive_resume_token})
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -13,7 +13,7 @@ import (
"github.com/zrepl/zrepl/util/envconst" "github.com/zrepl/zrepl/util/envconst"
) )
func ZFSDestroyFilesystemVersion(filesystem *DatasetPath, version *FilesystemVersion) (err error) { func ZFSDestroyFilesystemVersion(ctx context.Context, filesystem *DatasetPath, version *FilesystemVersion) (err error) {
datasetPath := version.ToAbsPath(filesystem) datasetPath := version.ToAbsPath(filesystem)
@ -22,7 +22,7 @@ func ZFSDestroyFilesystemVersion(filesystem *DatasetPath, version *FilesystemVer
return fmt.Errorf("sanity check failed: no @ or # character found in %q", datasetPath) return fmt.Errorf("sanity check failed: no @ or # character found in %q", datasetPath)
} }
return ZFSDestroy(datasetPath) return ZFSDestroy(ctx, datasetPath)
} }
var destroyerSingleton = destroyerImpl{} var destroyerSingleton = destroyerImpl{}
@ -48,8 +48,8 @@ func setDestroySnapOpErr(b []*DestroySnapOp, err error) {
} }
type destroyer interface { type destroyer interface {
Destroy(args []string) error Destroy(ctx context.Context, args []string) error
DestroySnapshotsCommaSyntaxSupported() (bool, error) DestroySnapshotsCommaSyntaxSupported(context.Context) (bool, error)
} }
func doDestroy(ctx context.Context, reqs []*DestroySnapOp, e destroyer) { func doDestroy(ctx context.Context, reqs []*DestroySnapOp, e destroyer) {
@ -69,7 +69,7 @@ func doDestroy(ctx context.Context, reqs []*DestroySnapOp, e destroyer) {
} }
reqs = validated reqs = validated
commaSupported, err := e.DestroySnapshotsCommaSyntaxSupported() commaSupported, err := e.DestroySnapshotsCommaSyntaxSupported(ctx)
if err != nil { if err != nil {
debug("destroy: comma syntax support detection failed: %s", err) debug("destroy: comma syntax support detection failed: %s", err)
setDestroySnapOpErr(reqs, err) setDestroySnapOpErr(reqs, err)
@ -85,7 +85,7 @@ func doDestroy(ctx context.Context, reqs []*DestroySnapOp, e destroyer) {
func doDestroySeq(ctx context.Context, reqs []*DestroySnapOp, e destroyer) { func doDestroySeq(ctx context.Context, reqs []*DestroySnapOp, e destroyer) {
for _, r := range reqs { for _, r := range reqs {
*r.ErrOut = e.Destroy([]string{fmt.Sprintf("%s@%s", r.Filesystem, r.Name)}) *r.ErrOut = e.Destroy(ctx, []string{fmt.Sprintf("%s@%s", r.Filesystem, r.Name)})
} }
} }
@ -139,7 +139,7 @@ func tryBatch(ctx context.Context, batch []*DestroySnapOp, d destroyer) error {
} }
} }
batchArg := fmt.Sprintf("%s@%s", batchFS, strings.Join(batchNames, ",")) batchArg := fmt.Sprintf("%s@%s", batchFS, strings.Join(batchNames, ","))
return d.Destroy([]string{batchArg}) return d.Destroy(ctx, []string{batchArg})
} }
// fsbatch must be on same filesystem // fsbatch must be on same filesystem
@ -203,7 +203,7 @@ func doDestroyBatchedRec(ctx context.Context, fsbatch []*DestroySnapOp, d destro
type destroyerImpl struct{} type destroyerImpl struct{}
func (d destroyerImpl) Destroy(args []string) error { func (d destroyerImpl) Destroy(ctx context.Context, args []string) error {
if len(args) != 1 { if len(args) != 1 {
// we have no use case for this at the moment, so let's crash (safer than destroying something unexpectedly) // we have no use case for this at the moment, so let's crash (safer than destroying something unexpectedly)
panic(fmt.Sprintf("unexpected number of arguments: %v", args)) panic(fmt.Sprintf("unexpected number of arguments: %v", args))
@ -212,7 +212,7 @@ func (d destroyerImpl) Destroy(args []string) error {
if !strings.ContainsAny(args[0], "@") { if !strings.ContainsAny(args[0], "@") {
panic(fmt.Sprintf("sanity check: expecting '@' in call to Destroy, got %q", args[0])) panic(fmt.Sprintf("sanity check: expecting '@' in call to Destroy, got %q", args[0]))
} }
return ZFSDestroy(args[0]) return ZFSDestroy(ctx, args[0])
} }
var batchDestroyFeatureCheck struct { var batchDestroyFeatureCheck struct {
@ -221,10 +221,10 @@ var batchDestroyFeatureCheck struct {
err error err error
} }
func (d destroyerImpl) DestroySnapshotsCommaSyntaxSupported() (bool, error) { func (d destroyerImpl) DestroySnapshotsCommaSyntaxSupported(ctx context.Context) (bool, error) {
batchDestroyFeatureCheck.once.Do(func() { batchDestroyFeatureCheck.once.Do(func() {
// "feature discovery" // "feature discovery"
cmd := exec.Command(ZFS_BINARY, "destroy") cmd := exec.CommandContext(ctx, ZFS_BINARY, "destroy")
output, err := cmd.CombinedOutput() output, err := cmd.CombinedOutput()
if _, ok := err.(*exec.ExitError); !ok { if _, ok := err.(*exec.ExitError); !ok {
debug("destroy feature check failed: %T %s", err, err) debug("destroy feature check failed: %T %s", err, err)

View File

@ -25,11 +25,11 @@ type mockBatchDestroy struct {
e2biglen int e2biglen int
} }
func (m *mockBatchDestroy) DestroySnapshotsCommaSyntaxSupported() (bool, error) { func (m *mockBatchDestroy) DestroySnapshotsCommaSyntaxSupported(_ context.Context) (bool, error) {
return !m.commaUnsupported, nil return !m.commaUnsupported, nil
} }
func (m *mockBatchDestroy) Destroy(args []string) error { func (m *mockBatchDestroy) Destroy(ctx context.Context, args []string) error {
defer m.mtx.Lock().Unlock() defer m.mtx.Lock().Unlock()
if len(args) != 1 { if len(args) != 1 {
panic("unexpected use of Destroy") panic("unexpected use of Destroy")

View File

@ -164,7 +164,7 @@ func (e *ZFSError) Error() string {
var ZFS_BINARY string = "zfs" var ZFS_BINARY string = "zfs"
func ZFSList(properties []string, zfsArgs ...string) (res [][]string, err error) { func ZFSList(ctx context.Context, properties []string, zfsArgs ...string) (res [][]string, err error) {
args := make([]string, 0, 4+len(zfsArgs)) args := make([]string, 0, 4+len(zfsArgs))
args = append(args, args = append(args,
@ -172,7 +172,7 @@ func ZFSList(properties []string, zfsArgs ...string) (res [][]string, err error)
"-o", strings.Join(properties, ",")) "-o", strings.Join(properties, ","))
args = append(args, zfsArgs...) args = append(args, zfsArgs...)
cmd := exec.Command(ZFS_BINARY, args...) cmd := exec.CommandContext(ctx, ZFS_BINARY, args...)
var stdout io.Reader var stdout io.Reader
stderr := bytes.NewBuffer(make([]byte, 0, 1024)) stderr := bytes.NewBuffer(make([]byte, 0, 1024))
@ -564,7 +564,7 @@ func (a ZFSSendArgVersion) ValidateExistsAndGetCheckedProps(ctx context.Context,
return ZFSPropCreateTxgAndGuidProps{}, nil return ZFSPropCreateTxgAndGuidProps{}, nil
} }
realProps, err := ZFSGetCreateTXGAndGuid(a.FullPath(fs)) realProps, err := ZFSGetCreateTXGAndGuid(ctx, a.FullPath(fs))
if err != nil { if err != nil {
return ZFSPropCreateTxgAndGuidProps{}, err return ZFSPropCreateTxgAndGuidProps{}, err
} }
@ -996,7 +996,7 @@ func ZFSSendDry(ctx context.Context, sendArgs ZFSSendArgs) (_ *DrySendInfo, err
} }
args = append(args, sargs...) args = append(args, sargs...)
cmd := exec.Command(ZFS_BINARY, args...) cmd := exec.CommandContext(ctx, ZFS_BINARY, args...)
output, err := cmd.CombinedOutput() output, err := cmd.CombinedOutput()
if err != nil { if err != nil {
return nil, &ZFSError{output, err} return nil, &ZFSError{output, err}
@ -1090,11 +1090,11 @@ func ZFSRecv(ctx context.Context, fs string, v *ZFSSendArgVersion, streamCopier
rollbackTarget := snaps[0] rollbackTarget := snaps[0]
rollbackTargetAbs := rollbackTarget.ToAbsPath(fsdp) rollbackTargetAbs := rollbackTarget.ToAbsPath(fsdp)
debug("recv: rollback to %q", rollbackTargetAbs) debug("recv: rollback to %q", rollbackTargetAbs)
if err := ZFSRollback(fsdp, rollbackTarget, "-r"); err != nil { if err := ZFSRollback(ctx, fsdp, rollbackTarget, "-r"); err != nil {
return fmt.Errorf("cannot rollback %s to %s for forced receive: %s", fsdp.ToString(), rollbackTarget, err) return fmt.Errorf("cannot rollback %s to %s for forced receive: %s", fsdp.ToString(), rollbackTarget, err)
} }
debug("recv: destroy %q", rollbackTargetAbs) debug("recv: destroy %q", rollbackTargetAbs)
if err := ZFSDestroy(rollbackTargetAbs); err != nil { if err := ZFSDestroy(ctx, rollbackTargetAbs); err != nil {
return fmt.Errorf("cannot destroy %s for forced receive: %s", rollbackTargetAbs, err) return fmt.Errorf("cannot destroy %s for forced receive: %s", rollbackTargetAbs, err)
} }
} }
@ -1225,12 +1225,12 @@ func (e ClearResumeTokenError) Error() string {
} }
// always returns *ClearResumeTokenError // always returns *ClearResumeTokenError
func ZFSRecvClearResumeToken(fs string) (err error) { func ZFSRecvClearResumeToken(ctx context.Context, fs string) (err error) {
if err := validateZFSFilesystem(fs); err != nil { if err := validateZFSFilesystem(fs); err != nil {
return err return err
} }
cmd := exec.Command(ZFS_BINARY, "recv", "-A", fs) cmd := exec.CommandContext(ctx, ZFS_BINARY, "recv", "-A", fs)
o, err := cmd.CombinedOutput() o, err := cmd.CombinedOutput()
if err != nil { if err != nil {
if bytes.Contains(o, []byte("does not have any resumable receive state to abort")) { if bytes.Contains(o, []byte("does not have any resumable receive state to abort")) {
@ -1267,11 +1267,11 @@ func (p *ZFSProperties) appendArgs(args *[]string) (err error) {
return nil return nil
} }
func ZFSSet(fs *DatasetPath, props *ZFSProperties) (err error) { func ZFSSet(ctx context.Context, fs *DatasetPath, props *ZFSProperties) (err error) {
return zfsSet(fs.ToString(), props) return zfsSet(ctx, fs.ToString(), props)
} }
func zfsSet(path string, props *ZFSProperties) (err error) { func zfsSet(ctx context.Context, path string, props *ZFSProperties) (err error) {
args := make([]string, 0) args := make([]string, 0)
args = append(args, "set") args = append(args, "set")
err = props.appendArgs(&args) err = props.appendArgs(&args)
@ -1280,7 +1280,7 @@ func zfsSet(path string, props *ZFSProperties) (err error) {
} }
args = append(args, path) args = append(args, path)
cmd := exec.Command(ZFS_BINARY, args...) cmd := exec.CommandContext(ctx, ZFS_BINARY, args...)
stderr := bytes.NewBuffer(make([]byte, 0, 1024)) stderr := bytes.NewBuffer(make([]byte, 0, 1024))
cmd.Stderr = stderr cmd.Stderr = stderr
@ -1299,12 +1299,12 @@ func zfsSet(path string, props *ZFSProperties) (err error) {
return return
} }
func ZFSGet(fs *DatasetPath, props []string) (*ZFSProperties, error) { func ZFSGet(ctx context.Context, fs *DatasetPath, props []string) (*ZFSProperties, error) {
return zfsGet(fs.ToString(), props, sourceAny) return zfsGet(ctx, fs.ToString(), props, sourceAny)
} }
// The returned error includes requested filesystem and version as quoted strings in its error message // The returned error includes requested filesystem and version as quoted strings in its error message
func ZFSGetGUID(fs string, version string) (g uint64, err error) { func ZFSGetGUID(ctx context.Context, fs string, version string) (g uint64, err error) {
defer func(e *error) { defer func(e *error) {
if *e != nil { if *e != nil {
*e = fmt.Errorf("zfs get guid fs=%q version=%q: %s", fs, version, *e) *e = fmt.Errorf("zfs get guid fs=%q version=%q: %s", fs, version, *e)
@ -1320,7 +1320,7 @@ func ZFSGetGUID(fs string, version string) (g uint64, err error) {
return 0, errors.New("version does not start with @ or #") return 0, errors.New("version does not start with @ or #")
} }
path := fmt.Sprintf("%s%s", fs, version) path := fmt.Sprintf("%s%s", fs, version)
props, err := zfsGet(path, []string{"guid"}, sourceAny) // always local props, err := zfsGet(ctx, path, []string{"guid"}, sourceAny) // always local
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -1332,11 +1332,11 @@ type GetMountpointOutput struct {
Mountpoint string Mountpoint string
} }
func ZFSGetMountpoint(fs string) (*GetMountpointOutput, error) { func ZFSGetMountpoint(ctx context.Context, fs string) (*GetMountpointOutput, error) {
if err := EntityNamecheck(fs, EntityTypeFilesystem); err != nil { if err := EntityNamecheck(fs, EntityTypeFilesystem); err != nil {
return nil, err return nil, err
} }
props, err := zfsGet(fs, []string{"mountpoint", "mounted"}, sourceAny) props, err := zfsGet(ctx, fs, []string{"mountpoint", "mounted"}, sourceAny)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1352,8 +1352,8 @@ func ZFSGetMountpoint(fs string) (*GetMountpointOutput, error) {
return o, nil return o, nil
} }
func ZFSGetRawAnySource(path string, props []string) (*ZFSProperties, error) { func ZFSGetRawAnySource(ctx context.Context, path string, props []string) (*ZFSProperties, error) {
return zfsGet(path, props, sourceAny) return zfsGet(ctx, path, props, sourceAny)
} }
var zfsGetDatasetDoesNotExistRegexp = regexp.MustCompile(`^cannot open '([^)]+)': (dataset does not exist|no such pool or dataset)`) // verified in platformtest var zfsGetDatasetDoesNotExistRegexp = regexp.MustCompile(`^cannot open '([^)]+)': (dataset does not exist|no such pool or dataset)`) // verified in platformtest
@ -1412,9 +1412,9 @@ func (s zfsPropertySource) zfsGetSourceFieldPrefixes() []string {
return prefixes return prefixes
} }
func zfsGet(path string, props []string, allowedSources zfsPropertySource) (*ZFSProperties, error) { func zfsGet(ctx context.Context, path string, props []string, allowedSources zfsPropertySource) (*ZFSProperties, error) {
args := []string{"get", "-Hp", "-o", "property,value,source", strings.Join(props, ","), path} args := []string{"get", "-Hp", "-o", "property,value,source", strings.Join(props, ","), path}
cmd := exec.Command(ZFS_BINARY, args...) cmd := exec.CommandContext(ctx, ZFS_BINARY, args...)
stdout, err := cmd.Output() stdout, err := cmd.Output()
if err != nil { if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok { if exitErr, ok := err.(*exec.ExitError); ok {
@ -1462,8 +1462,8 @@ type ZFSPropCreateTxgAndGuidProps struct {
CreateTXG, Guid uint64 CreateTXG, Guid uint64
} }
func ZFSGetCreateTXGAndGuid(ds string) (ZFSPropCreateTxgAndGuidProps, error) { func ZFSGetCreateTXGAndGuid(ctx context.Context, ds string) (ZFSPropCreateTxgAndGuidProps, error) {
props, err := zfsGetNumberProps(ds, []string{"createtxg", "guid"}, sourceAny) props, err := zfsGetNumberProps(ctx, ds, []string{"createtxg", "guid"}, sourceAny)
if err != nil { if err != nil {
return ZFSPropCreateTxgAndGuidProps{}, err return ZFSPropCreateTxgAndGuidProps{}, err
} }
@ -1474,8 +1474,8 @@ func ZFSGetCreateTXGAndGuid(ds string) (ZFSPropCreateTxgAndGuidProps, error) {
} }
// returns *DatasetDoesNotExist if the dataset does not exist // returns *DatasetDoesNotExist if the dataset does not exist
func zfsGetNumberProps(ds string, props []string, src zfsPropertySource) (map[string]uint64, error) { func zfsGetNumberProps(ctx context.Context, ds string, props []string, src zfsPropertySource) (map[string]uint64, error) {
sps, err := zfsGet(ds, props, sourceAny) sps, err := zfsGet(ctx, ds, props, sourceAny)
if err != nil { if err != nil {
if _, ok := err.(*DatasetDoesNotExist); ok { if _, ok := err.(*DatasetDoesNotExist); ok {
return nil, err // pass through as is return nil, err // pass through as is
@ -1557,7 +1557,7 @@ func tryParseDestroySnapshotsError(arg string, stderr []byte) *DestroySnapshotsE
} }
} }
func ZFSDestroy(arg string) (err error) { func ZFSDestroy(ctx context.Context, arg string) (err error) {
var dstype, filesystem string var dstype, filesystem string
idx := strings.IndexAny(arg, "@#") idx := strings.IndexAny(arg, "@#")
@ -1576,7 +1576,7 @@ func ZFSDestroy(arg string) (err error) {
defer prometheus.NewTimer(prom.ZFSDestroyDuration.WithLabelValues(dstype, filesystem)) defer prometheus.NewTimer(prom.ZFSDestroyDuration.WithLabelValues(dstype, filesystem))
cmd := exec.Command(ZFS_BINARY, "destroy", arg) cmd := exec.CommandContext(ctx, ZFS_BINARY, "destroy", arg)
var stderr bytes.Buffer var stderr bytes.Buffer
cmd.Stderr = &stderr cmd.Stderr = &stderr
@ -1607,15 +1607,15 @@ func ZFSDestroy(arg string) (err error) {
} }
func ZFSDestroyIdempotent(path string) error { func ZFSDestroyIdempotent(ctx context.Context, path string) error {
err := ZFSDestroy(path) err := ZFSDestroy(ctx, path)
if _, ok := err.(*DatasetDoesNotExist); ok { if _, ok := err.(*DatasetDoesNotExist); ok {
return nil return nil
} }
return err return err
} }
func ZFSSnapshot(fs *DatasetPath, name string, recursive bool) (err error) { func ZFSSnapshot(ctx context.Context, fs *DatasetPath, name string, recursive bool) (err error) {
promTimer := prometheus.NewTimer(prom.ZFSSnapshotDuration.WithLabelValues(fs.ToString())) promTimer := prometheus.NewTimer(prom.ZFSSnapshotDuration.WithLabelValues(fs.ToString()))
defer promTimer.ObserveDuration() defer promTimer.ObserveDuration()
@ -1624,7 +1624,7 @@ func ZFSSnapshot(fs *DatasetPath, name string, recursive bool) (err error) {
if err := EntityNamecheck(snapname, EntityTypeSnapshot); err != nil { if err := EntityNamecheck(snapname, EntityTypeSnapshot); err != nil {
return errors.Wrap(err, "zfs snapshot") return errors.Wrap(err, "zfs snapshot")
} }
cmd := exec.Command(ZFS_BINARY, "snapshot", snapname) cmd := exec.CommandContext(ctx, ZFS_BINARY, "snapshot", snapname)
stderr := bytes.NewBuffer(make([]byte, 0, 1024)) stderr := bytes.NewBuffer(make([]byte, 0, 1024))
cmd.Stderr = stderr cmd.Stderr = stderr
@ -1667,7 +1667,7 @@ var ErrBookmarkCloningNotSupported = fmt.Errorf("bookmark cloning feature is not
// //
// does not destroy an existing bookmark, returns // does not destroy an existing bookmark, returns
// //
func ZFSBookmark(fs string, v ZFSSendArgVersion, bookmark string) (err error) { func ZFSBookmark(ctx context.Context, fs string, v ZFSSendArgVersion, bookmark string) (err error) {
promTimer := prometheus.NewTimer(prom.ZFSBookmarkDuration.WithLabelValues(fs)) promTimer := prometheus.NewTimer(prom.ZFSBookmarkDuration.WithLabelValues(fs))
defer promTimer.ObserveDuration() defer promTimer.ObserveDuration()
@ -1687,7 +1687,7 @@ func ZFSBookmark(fs string, v ZFSSendArgVersion, bookmark string) (err error) {
debug("bookmark: %q %q", snapname, bookmarkname) debug("bookmark: %q %q", snapname, bookmarkname)
cmd := exec.Command(ZFS_BINARY, "bookmark", snapname, bookmarkname) cmd := exec.CommandContext(ctx, ZFS_BINARY, "bookmark", snapname, bookmarkname)
stderr := bytes.NewBuffer(make([]byte, 0, 1024)) stderr := bytes.NewBuffer(make([]byte, 0, 1024))
cmd.Stderr = stderr cmd.Stderr = stderr
@ -1703,7 +1703,7 @@ func ZFSBookmark(fs string, v ZFSSendArgVersion, bookmark string) (err error) {
} else if zfsBookmarkExistsRegex.Match(stderr.Bytes()) { } else if zfsBookmarkExistsRegex.Match(stderr.Bytes()) {
// check if this was idempotent // check if this was idempotent
bookGuid, err := ZFSGetGUID(fs, "#"+bookmark) bookGuid, err := ZFSGetGUID(ctx, fs, "#"+bookmark)
if err != nil { if err != nil {
return errors.Wrap(err, "bookmark idempotency check") // guid error expressive enough return errors.Wrap(err, "bookmark idempotency check") // guid error expressive enough
} }
@ -1731,7 +1731,7 @@ func ZFSBookmark(fs string, v ZFSSendArgVersion, bookmark string) (err error) {
} }
func ZFSRollback(fs *DatasetPath, snapshot FilesystemVersion, rollbackArgs ...string) (err error) { func ZFSRollback(ctx context.Context, fs *DatasetPath, snapshot FilesystemVersion, rollbackArgs ...string) (err error) {
snapabs := snapshot.ToAbsPath(fs) snapabs := snapshot.ToAbsPath(fs)
if snapshot.Type != Snapshot { if snapshot.Type != Snapshot {
@ -1742,7 +1742,7 @@ func ZFSRollback(fs *DatasetPath, snapshot FilesystemVersion, rollbackArgs ...st
args = append(args, rollbackArgs...) args = append(args, rollbackArgs...)
args = append(args, snapabs) args = append(args, snapabs)
cmd := exec.Command(ZFS_BINARY, args...) cmd := exec.CommandContext(ctx, ZFS_BINARY, args...)
stderr := bytes.NewBuffer(make([]byte, 0, 1024)) stderr := bytes.NewBuffer(make([]byte, 0, 1024))
cmd.Stderr = stderr cmd.Stderr = stderr

View File

@ -1,6 +1,7 @@
package zfs package zfs
import ( import (
"context"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -11,9 +12,11 @@ func TestZFSListHandlesProducesZFSErrorOnNonZeroExit(t *testing.T) {
var err error var err error
ctx := context.Background()
ZFS_BINARY = "./test_helpers/zfs_failer.sh" ZFS_BINARY = "./test_helpers/zfs_failer.sh"
_, err = ZFSList([]string{"fictionalprop"}, "nonexistent/dataset") _, err = ZFSList(ctx, []string{"fictionalprop"}, "nonexistent/dataset")
assert.Error(t, err) assert.Error(t, err)
zfsError, ok := err.(*ZFSError) zfsError, ok := err.(*ZFSError)