2019-03-19 17:43:28 +01:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/spf13/pflag"
|
2019-03-22 19:41:12 +01:00
|
|
|
|
2019-03-19 17:43:28 +01:00
|
|
|
"github.com/zrepl/zrepl/zfs"
|
|
|
|
|
|
|
|
"github.com/zrepl/zrepl/cli"
|
|
|
|
"github.com/zrepl/zrepl/config"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
MigrateCmd = &cli.Subcommand{
|
|
|
|
Use: "migrate",
|
|
|
|
Short: "perform migration of the on-disk / zfs properties",
|
|
|
|
SetupSubcommands: func() []*cli.Subcommand {
|
|
|
|
return migrations
|
|
|
|
},
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
var migrations = []*cli.Subcommand{
|
|
|
|
&cli.Subcommand{
|
|
|
|
Use: "0.0.X:0.1:placeholder",
|
|
|
|
Run: doMigratePlaceholder0_1,
|
|
|
|
SetupFlags: func(f *pflag.FlagSet) {
|
|
|
|
f.BoolVar(&migratePlaceholder0_1Args.dryRun, "dry-run", false, "dry run")
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var migratePlaceholder0_1Args struct {
|
|
|
|
dryRun bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func doMigratePlaceholder0_1(sc *cli.Subcommand, args []string) error {
|
|
|
|
if len(args) != 0 {
|
|
|
|
return fmt.Errorf("migration does not take arguments, got %v", args)
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg := sc.Config()
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
allFSS, err := zfs.ZFSListMapping(ctx, zfs.NoFilter())
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "cannot list filesystems")
|
|
|
|
}
|
|
|
|
|
|
|
|
type workItem struct {
|
|
|
|
jobName string
|
|
|
|
rootFS *zfs.DatasetPath
|
|
|
|
fss []*zfs.DatasetPath
|
|
|
|
}
|
|
|
|
var wis []workItem
|
|
|
|
for i, j := range cfg.Jobs {
|
|
|
|
var rfsS string
|
|
|
|
switch job := j.Ret.(type) {
|
|
|
|
case *config.SinkJob:
|
|
|
|
rfsS = job.RootFS
|
|
|
|
case *config.PullJob:
|
|
|
|
rfsS = job.RootFS
|
|
|
|
default:
|
|
|
|
fmt.Printf("ignoring job %q (%d/%d, type %T)\n", j.Name(), i, len(cfg.Jobs), j.Ret)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
rfs, err := zfs.NewDatasetPath(rfsS)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, "root fs for job %q is not a valid dataset path", j.Name())
|
|
|
|
}
|
|
|
|
var fss []*zfs.DatasetPath
|
|
|
|
for _, fs := range allFSS {
|
|
|
|
if fs.HasPrefix(rfs) {
|
|
|
|
fss = append(fss, fs)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
wis = append(wis, workItem{j.Name(), rfs, fss})
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, wi := range wis {
|
|
|
|
fmt.Printf("job %q => migrate filesystems below root_fs %q\n", wi.jobName, wi.rootFS.ToString())
|
|
|
|
if len(wi.fss) == 0 {
|
|
|
|
fmt.Printf("\tno filesystems\n")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for _, fs := range wi.fss {
|
|
|
|
fmt.Printf("\t%q ... ", fs.ToString())
|
|
|
|
r, err := zfs.ZFSMigrateHashBasedPlaceholderToCurrent(fs, migratePlaceholder0_1Args.dryRun)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("error: %s\n", err)
|
|
|
|
} else if !r.NeedsModification {
|
|
|
|
fmt.Printf("unchanged (placeholder=%v)\n", r.OriginalState.IsPlaceholder)
|
|
|
|
} else {
|
|
|
|
fmt.Printf("migrate (placeholder=%v) (old value = %q)\n",
|
|
|
|
r.OriginalState.IsPlaceholder, r.OriginalState.RawLocalPropertyValue)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|