mirror of
https://github.com/zrepl/zrepl.git
synced 2024-11-25 01:44:43 +01:00
add 'test filesystems' subcommand for testing filesystem filters
This commit is contained in:
parent
5c3c83b2cb
commit
63169c51b7
15
cli/cli.go
15
cli/cli.go
@ -45,6 +45,7 @@ type Subcommand struct {
|
|||||||
NoRequireConfig bool
|
NoRequireConfig bool
|
||||||
Run func(subcommand *Subcommand, args []string) error
|
Run func(subcommand *Subcommand, args []string) error
|
||||||
SetupFlags func(f *pflag.FlagSet)
|
SetupFlags func(f *pflag.FlagSet)
|
||||||
|
SetupSubcommands func() []*Subcommand
|
||||||
|
|
||||||
config *config.Config
|
config *config.Config
|
||||||
configErr error
|
configErr error
|
||||||
@ -86,15 +87,25 @@ func (s *Subcommand) tryParseConfig() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func AddSubcommand(s *Subcommand) {
|
func AddSubcommand(s *Subcommand) {
|
||||||
|
addSubcommandToCobraCmd(rootCmd, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addSubcommandToCobraCmd(c *cobra.Command, s *Subcommand) {
|
||||||
cmd := cobra.Command{
|
cmd := cobra.Command{
|
||||||
Use: s.Use,
|
Use: s.Use,
|
||||||
Short: s.Short,
|
Short: s.Short,
|
||||||
Run: s.run,
|
}
|
||||||
|
if s.SetupSubcommands == nil {
|
||||||
|
cmd.Run = s.run
|
||||||
|
} else {
|
||||||
|
for _, sub := range s.SetupSubcommands() {
|
||||||
|
addSubcommandToCobraCmd(&cmd, sub)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if s.SetupFlags != nil {
|
if s.SetupFlags != nil {
|
||||||
s.SetupFlags(cmd.Flags())
|
s.SetupFlags(cmd.Flags())
|
||||||
}
|
}
|
||||||
rootCmd.AddCommand(&cmd)
|
c.AddCommand(&cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
108
client/testcmd.go
Normal file
108
client/testcmd.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
"github.com/zrepl/zrepl/cli"
|
||||||
|
"github.com/zrepl/zrepl/config"
|
||||||
|
"github.com/zrepl/zrepl/daemon/filters"
|
||||||
|
"github.com/zrepl/zrepl/zfs"
|
||||||
|
)
|
||||||
|
|
||||||
|
var TestCmd = &cli.Subcommand {
|
||||||
|
Use: "test",
|
||||||
|
SetupSubcommands: func() []*cli.Subcommand {
|
||||||
|
return []*cli.Subcommand{testFilter}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var testFilterArgs struct {
|
||||||
|
job string
|
||||||
|
all bool
|
||||||
|
input string
|
||||||
|
}
|
||||||
|
|
||||||
|
var testFilter = &cli.Subcommand{
|
||||||
|
Use: "filesystems --job JOB [--all | --input INPUT]",
|
||||||
|
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) {
|
||||||
|
case *config.SourceJob: confFilter = j.Filesystems
|
||||||
|
case *config.PushJob: confFilter = j.Filesystems
|
||||||
|
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
|
||||||
|
}
|
@ -17,10 +17,32 @@ type Config struct {
|
|||||||
Global *Global `yaml:"global,optional,fromdefaults"`
|
Global *Global `yaml:"global,optional,fromdefaults"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Config) Job(name string) (*JobEnum, error) {
|
||||||
|
for _, j := range c.Jobs {
|
||||||
|
if j.Name() == name {
|
||||||
|
return &j, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("job %q not defined in config", name)
|
||||||
|
}
|
||||||
|
|
||||||
type JobEnum struct {
|
type JobEnum struct {
|
||||||
Ret interface{}
|
Ret interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (j JobEnum) Name() string {
|
||||||
|
var name string
|
||||||
|
switch v := j.Ret.(type) {
|
||||||
|
case *PushJob: name = v.Name
|
||||||
|
case *SinkJob: name = v.Name
|
||||||
|
case *PullJob: name = v.Name
|
||||||
|
case *SourceJob: name = v.Name
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unknownn job type %T", v))
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
type ActiveJob struct {
|
type ActiveJob struct {
|
||||||
Type string `yaml:"type"`
|
Type string `yaml:"type"`
|
||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
|
@ -18,7 +18,7 @@ The following rules determine which result is chosen for a given filesystem path
|
|||||||
The **subtree wildcard** ``<`` means "the dataset left of ``<`` and all its children".
|
The **subtree wildcard** ``<`` means "the dataset left of ``<`` and all its children".
|
||||||
|
|
||||||
.. TIP::
|
.. TIP::
|
||||||
You can try out patterns for a configured job using the ``zrepl test`` subcommand.
|
You can try out patterns for a configured job using the ``zrepl test filesystems`` subcommand for push and source jobs.
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
|
1
main.go
1
main.go
@ -15,6 +15,7 @@ func init() {
|
|||||||
cli.AddSubcommand(client.ConfigcheckCmd)
|
cli.AddSubcommand(client.ConfigcheckCmd)
|
||||||
cli.AddSubcommand(client.VersionCmd)
|
cli.AddSubcommand(client.VersionCmd)
|
||||||
cli.AddSubcommand(client.PprofCmd)
|
cli.AddSubcommand(client.PprofCmd)
|
||||||
|
cli.AddSubcommand(client.TestCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
Loading…
Reference in New Issue
Block a user