diff --git a/platformtest/harness/harness.go b/platformtest/harness/harness.go index 65c0e98..ed9e47c 100644 --- a/platformtest/harness/harness.go +++ b/platformtest/harness/harness.go @@ -37,6 +37,7 @@ func main() { flag.StringVar(&args.CreateArgs.Mountpoint, "mountpoint", "", "") flag.BoolVar(&args.StopAndKeepPoolOnFail, "failure.stop-and-keep-pool", false, "if a test case fails, stop test execution and keep pool as it was when the test failed") flag.StringVar(&args.Run, "run", "", "") + flag.DurationVar(&platformtest.ZpoolExportTimeout, "zfs.zpool-export-timeout", platformtest.ZpoolExportTimeout, "") flag.Parse() if err := HarnessRun(args); err != nil { diff --git a/platformtest/platformtest_zpool.go b/platformtest/platformtest_zpool.go index f31d6f3..33ef985 100644 --- a/platformtest/platformtest_zpool.go +++ b/platformtest/platformtest_zpool.go @@ -5,12 +5,17 @@ import ( "fmt" "os" "path/filepath" + "runtime" + "strings" + "time" "github.com/pkg/errors" "github.com/zrepl/zrepl/zfs" ) +var ZpoolExportTimeout time.Duration = 500 * time.Millisecond + type Zpool struct { args ZpoolCreateArgs } @@ -93,8 +98,23 @@ func (p *Zpool) Name() string { return p.args.PoolName } func (p *Zpool) Destroy(ctx context.Context, e Execer) error { - if err := e.RunExpectSuccessNoOutput(ctx, "zpool", "export", p.args.PoolName); err != nil { - return errors.Wrapf(err, "export pool %q", p.args.PoolName) + exportDeadline := time.Now().Add(ZpoolExportTimeout) + + for { + if time.Now().After(exportDeadline) { + return errors.Errorf("could not zpool export (got 'pool is busy'): %s", p.args.PoolName) + } + err := e.RunExpectSuccessNoOutput(ctx, "zpool", "export", p.args.PoolName) + if err == nil { + break + } + if strings.Contains(err.Error(), "pool is busy") { + runtime.Gosched() + continue + } + if err != nil { + return errors.Wrapf(err, "export pool %q", p.args.PoolName) + } } if err := os.Remove(p.args.ImagePath); err != nil {