2018-10-13 15:53:52 +02:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2019-03-22 19:41:12 +01:00
|
|
|
|
2018-11-16 11:25:48 +01:00
|
|
|
"github.com/pkg/errors"
|
2018-10-13 15:53:52 +02:00
|
|
|
"github.com/spf13/pflag"
|
2019-03-22 19:41:12 +01:00
|
|
|
|
2018-10-13 15:53:52 +02:00
|
|
|
"github.com/zrepl/zrepl/cli"
|
|
|
|
"github.com/zrepl/zrepl/config"
|
|
|
|
"github.com/zrepl/zrepl/daemon/filters"
|
|
|
|
"github.com/zrepl/zrepl/zfs"
|
|
|
|
)
|
|
|
|
|
2019-03-22 19:41:12 +01:00
|
|
|
var TestCmd = &cli.Subcommand{
|
2018-10-13 15:53:52 +02:00
|
|
|
Use: "test",
|
|
|
|
SetupSubcommands: func() []*cli.Subcommand {
|
2018-11-16 11:25:48 +01:00
|
|
|
return []*cli.Subcommand{testFilter, testPlaceholder}
|
2018-10-13 15:53:52 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var testFilterArgs struct {
|
2019-03-22 19:41:12 +01:00
|
|
|
job string
|
|
|
|
all bool
|
2018-10-13 15:53:52 +02:00
|
|
|
input string
|
|
|
|
}
|
|
|
|
|
|
|
|
var testFilter = &cli.Subcommand{
|
2019-03-22 19:41:12 +01:00
|
|
|
Use: "filesystems --job JOB [--all | --input INPUT]",
|
2018-10-13 15:53:52 +02:00
|
|
|
Short: "test filesystems filter specified in push or source job",
|
|
|
|
SetupFlags: func(f *pflag.FlagSet) {
|
|
|
|
f.StringVar(&testFilterArgs.job, "job", "", "the name of the push or source job")
|
|
|
|
f.StringVar(&testFilterArgs.input, "input", "", "a filesystem name to test against the job's filters")
|
|
|
|
f.BoolVar(&testFilterArgs.all, "all", false, "test all local filesystems")
|
|
|
|
},
|
|
|
|
Run: runTestFilterCmd,
|
|
|
|
}
|
|
|
|
|
|
|
|
func runTestFilterCmd(subcommand *cli.Subcommand, args []string) error {
|
|
|
|
|
|
|
|
if testFilterArgs.job == "" {
|
|
|
|
return fmt.Errorf("must specify --job flag")
|
|
|
|
}
|
|
|
|
if !(testFilterArgs.all != (testFilterArgs.input != "")) { // xor
|
|
|
|
return fmt.Errorf("must set one: --all or --input")
|
|
|
|
}
|
|
|
|
|
|
|
|
conf := subcommand.Config()
|
|
|
|
|
|
|
|
var confFilter config.FilesystemsFilter
|
|
|
|
job, err := conf.Job(testFilterArgs.job)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
switch j := job.Ret.(type) {
|
2019-03-22 19:41:12 +01:00
|
|
|
case *config.SourceJob:
|
|
|
|
confFilter = j.Filesystems
|
|
|
|
case *config.PushJob:
|
|
|
|
confFilter = j.Filesystems
|
2018-10-13 15:53:52 +02:00
|
|
|
default:
|
|
|
|
return fmt.Errorf("job type %T does not have filesystems filter", j)
|
|
|
|
}
|
|
|
|
|
|
|
|
f, err := filters.DatasetMapFilterFromConfig(confFilter)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("filter invalid: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var fsnames []string
|
|
|
|
if testFilterArgs.input != "" {
|
|
|
|
fsnames = []string{testFilterArgs.input}
|
|
|
|
} else {
|
|
|
|
out, err := zfs.ZFSList([]string{"name"})
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("could not list ZFS filesystems: %s", err)
|
|
|
|
}
|
|
|
|
for _, row := range out {
|
|
|
|
|
|
|
|
fsnames = append(fsnames, row[0])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fspaths := make([]*zfs.DatasetPath, len(fsnames))
|
|
|
|
for i, fsname := range fsnames {
|
|
|
|
path, err := zfs.NewDatasetPath(fsname)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
fspaths[i] = path
|
|
|
|
}
|
|
|
|
|
|
|
|
hadFilterErr := false
|
|
|
|
for _, in := range fspaths {
|
|
|
|
var res string
|
|
|
|
var errStr string
|
|
|
|
pass, err := f.Filter(in)
|
|
|
|
if err != nil {
|
|
|
|
res = "ERROR"
|
|
|
|
errStr = err.Error()
|
|
|
|
hadFilterErr = true
|
|
|
|
} else if pass {
|
|
|
|
res = "ACCEPT"
|
|
|
|
} else {
|
|
|
|
res = "REJECT"
|
|
|
|
}
|
|
|
|
fmt.Printf("%s\t%s\t%s\n", res, in.ToString(), errStr)
|
|
|
|
}
|
|
|
|
|
|
|
|
if hadFilterErr {
|
|
|
|
return fmt.Errorf("filter errors occurred")
|
|
|
|
}
|
|
|
|
return nil
|
2018-11-16 11:25:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
var testPlaceholderArgs struct {
|
2019-03-22 20:45:27 +01:00
|
|
|
ds string
|
|
|
|
all bool
|
2018-11-16 11:25:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
var testPlaceholder = &cli.Subcommand{
|
2019-03-19 17:43:28 +01:00
|
|
|
Use: "placeholder [--all | --dataset DATASET]",
|
|
|
|
Short: fmt.Sprintf("list received placeholder filesystems (zfs property %q)", zfs.PlaceholderPropertyName),
|
2018-11-16 11:25:48 +01:00
|
|
|
Example: `
|
|
|
|
placeholder --all
|
2019-03-19 17:43:28 +01:00
|
|
|
placeholder --dataset path/to/sink/clientident/fs`,
|
2018-11-16 11:25:48 +01:00
|
|
|
NoRequireConfig: true,
|
|
|
|
SetupFlags: func(f *pflag.FlagSet) {
|
|
|
|
f.StringVar(&testPlaceholderArgs.ds, "dataset", "", "dataset path (not required to exist)")
|
|
|
|
f.BoolVar(&testPlaceholderArgs.all, "all", false, "list tab-separated placeholder status of all filesystems")
|
|
|
|
},
|
|
|
|
Run: runTestPlaceholder,
|
|
|
|
}
|
|
|
|
|
|
|
|
func runTestPlaceholder(subcommand *cli.Subcommand, args []string) error {
|
|
|
|
|
2019-03-19 17:43:28 +01:00
|
|
|
var checkDPs []*zfs.DatasetPath
|
|
|
|
|
2018-11-16 11:25:48 +01:00
|
|
|
// all actions first
|
|
|
|
if testPlaceholderArgs.all {
|
2019-03-19 17:43:28 +01:00
|
|
|
out, err := zfs.ZFSList([]string{"name"})
|
2018-11-16 11:25:48 +01:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "could not list ZFS filesystems")
|
|
|
|
}
|
|
|
|
for _, row := range out {
|
|
|
|
dp, err := zfs.NewDatasetPath(row[0])
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2019-03-19 17:43:28 +01:00
|
|
|
checkDPs = append(checkDPs, dp)
|
2018-11-16 11:25:48 +01:00
|
|
|
}
|
2019-03-19 17:43:28 +01:00
|
|
|
} else {
|
|
|
|
dp, err := zfs.NewDatasetPath(testPlaceholderArgs.ds)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if dp.Empty() {
|
|
|
|
return fmt.Errorf("must specify --dataset DATASET or --all")
|
|
|
|
}
|
|
|
|
checkDPs = append(checkDPs, dp)
|
2018-11-16 11:25:48 +01:00
|
|
|
}
|
|
|
|
|
2019-03-19 17:43:28 +01:00
|
|
|
fmt.Printf("IS_PLACEHOLDER\tDATASET\tzrepl:placeholder\n")
|
|
|
|
for _, dp := range checkDPs {
|
|
|
|
ph, err := zfs.ZFSGetFilesystemPlaceholderState(dp)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "cannot get placeholder state")
|
2018-11-16 11:25:48 +01:00
|
|
|
}
|
2019-03-19 17:43:28 +01:00
|
|
|
if !ph.FSExists {
|
|
|
|
panic("placeholder state inconsistent: filesystem " + ph.FS + " must exist in this context")
|
|
|
|
}
|
|
|
|
is := "yes"
|
|
|
|
if !ph.IsPlaceholder {
|
|
|
|
is = "no"
|
2018-11-16 11:25:48 +01:00
|
|
|
}
|
2019-03-19 17:43:28 +01:00
|
|
|
fmt.Printf("%s\t%s\t%s\n", is, dp.ToString(), ph.RawLocalPropertyValue)
|
2018-11-16 11:25:48 +01:00
|
|
|
}
|
2019-03-19 17:43:28 +01:00
|
|
|
return nil
|
2018-11-16 11:25:48 +01:00
|
|
|
}
|