From ffd7bc1021764f05fedcdf7cefd610abe8f6feb8 Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Sun, 13 May 2018 15:11:21 +0200 Subject: [PATCH] wip --- internal/ui/config/config.go | 26 ++-- internal/ui/config/config_test.go | 129 ++++++++++++++++-- internal/ui/config/testdata/all.conf | 12 ++ internal/ui/config/testdata/all.golden | 15 ++ internal/ui/config/testdata/backup.golden | 7 +- internal/ui/config/testdata/repo_local.golden | 9 +- 6 files changed, 173 insertions(+), 25 deletions(-) create mode 100644 internal/ui/config/testdata/all.conf create mode 100644 internal/ui/config/testdata/all.golden diff --git a/internal/ui/config/config.go b/internal/ui/config/config.go index 70902b3f0..acaafb124 100644 --- a/internal/ui/config/config.go +++ b/internal/ui/config/config.go @@ -19,14 +19,14 @@ type Config struct { Password string `hcl:"password" env:"RESTIC_PASSWORD"` PasswordFile string `hcl:"password_file" flag:"password-file" env:"RESTIC_PASSWORD_FILE"` - Backends map[string]Backend `hcl:"backend"` - Backup Backup `hcl:"backup"` + Backends map[string]interface{} `hcl:"backend"` + Backup Backup `hcl:"backup"` } -// Backend is a configured backend to store a repository. -type Backend struct { - Backend string - Path string +// BackendLocal is a backend in a local directory. +type BackendLocal struct { + Type string `hcl:"type"` + Path string `hcl:"path"` } // Backup sets the options for the "backup" command. @@ -80,8 +80,8 @@ func Parse(buf []byte) (cfg Config, err error) { root := parsed.Node.(*ast.ObjectList) checks := map[string]map[string]struct{}{ - "": listTags(cfg, "config"), - "backup": listTags(Backup{}, "config"), + "": listTags(cfg, "hcl"), + "backup": listTags(Backup{}, "hcl"), } for name, valid := range checks { @@ -150,6 +150,8 @@ func ApplyFlags(cfg interface{}, fset *pflag.FlagSet) error { panic("target config is not a pointer") } + debug.Log("apply flags") + attr := getFieldsForTag("flag", cfg) var visitError error @@ -197,7 +199,13 @@ func ApplyFlags(cfg interface{}, fset *pflag.FlagSet) error { visitError = err return } - field.SetSlice(v) + + slice := reflect.MakeSlice(reflect.TypeOf(v), len(v), len(v)) + field.Set(slice) + + for i, s := range v { + slice.Index(i).SetString(s) + } default: visitError = errors.Errorf("flag %v has unknown type %v", flag.Name, flag.Value.Type()) return diff --git a/internal/ui/config/config_test.go b/internal/ui/config/config_test.go index 43f8123cc..09e517930 100644 --- a/internal/ui/config/config_test.go +++ b/internal/ui/config/config_test.go @@ -9,18 +9,11 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/spf13/pflag" ) var updateGoldenFiles = flag.Bool("update", false, "update golden files in testdata/") -func readTestFile(t testing.TB, filename string) []byte { - data, err := ioutil.ReadFile(filepath.Join("testdata", filename)) - if err != nil { - t.Fatal(err) - } - return data -} - func saveGoldenFile(t testing.TB, base string, cfg Config) { buf, err := json.MarshalIndent(cfg, "", " ") if err != nil { @@ -48,7 +41,7 @@ func loadGoldenFile(t testing.TB, base string) Config { return cfg } -func TestRead(t *testing.T) { +func TestConfigLoad(t *testing.T) { entries, err := ioutil.ReadDir("testdata") if err != nil { t.Fatal(err) @@ -62,9 +55,7 @@ func TestRead(t *testing.T) { base := strings.TrimSuffix(filename, ".conf") t.Run(base, func(t *testing.T) { - buf := readTestFile(t, filename) - - cfg, err := Parse(buf) + cfg, err := Load(filepath.Join("testdata", filename)) if err != nil { t.Fatal(err) } @@ -81,3 +72,117 @@ func TestRead(t *testing.T) { }) } } + +func TestConfigApplyFlags(t *testing.T) { + var tests = []struct { + filename string + applyFlags func(cfg *Config) error + want Config + }{ + { + filename: "backup.conf", + applyFlags: func(cfg *Config) error { + args := []string{"--exclude", "foo/*.go"} + + s := pflag.NewFlagSet("", pflag.ContinueOnError) + s.StringArrayP("exclude", "e", nil, "exclude files") + + err := s.Parse(args) + if err != nil { + return err + } + + return ApplyFlags(&cfg.Backup, s) + }, + want: Config{ + Backup: Backup{ + Target: []string{"foo", "/home/user"}, + Excludes: []string{"foo/*.go"}, + }, + }, + }, + { + filename: "backup.conf", + applyFlags: func(cfg *Config) error { + args := []string{"--repo", "sftp:user@server:/srv/backup/repo"} + + s := pflag.NewFlagSet("", pflag.ContinueOnError) + s.StringP("repo", "r", "", "repository to backup to or restore from") + + err := s.Parse(args) + if err != nil { + return err + } + + return ApplyFlags(cfg, s) + }, + want: Config{ + Backup: Backup{ + Target: []string{"foo", "/home/user"}, + }, + Repo: "sftp:user@server:/srv/backup/repo", + }, + }, + } + + for _, test := range tests { + t.Run("", func(t *testing.T) { + cfg, err := Load(filepath.Join("testdata", test.filename)) + if err != nil { + t.Fatal(err) + } + + err = test.applyFlags(&cfg) + if err != nil { + t.Fatal(err) + } + + if !cmp.Equal(test.want, cfg) { + t.Error(cmp.Diff(test.want, cfg)) + } + }) + } +} + +func TestConfigApplyEnv(t *testing.T) { + var tests = []struct { + filename string + env []string + want Config + }{ + { + filename: "backup.conf", + env: []string{ + "RESTIC_REPOSITORY=/tmp/repo", + "RESTIC_PASSWORD=foobar", + "RESTIC_PASSWORD_FILE=/root/secret.txt", + }, + want: Config{ + Password: "foobar", + PasswordFile: "/root/secret.txt", + Repo: "/tmp/repo", + Backup: Backup{ + Target: []string{"foo", "/home/user"}, + }, + }, + }, + } + + for _, test := range tests { + t.Run("", func(t *testing.T) { + cfg, err := Load(filepath.Join("testdata", test.filename)) + if err != nil { + t.Fatal(err) + } + + err = ApplyEnv(&cfg, test.env) + if err != nil { + t.Fatal(err) + } + + if !cmp.Equal(test.want, cfg) { + t.Error(cmp.Diff(test.want, cfg)) + } + }) + } +} diff --git a/internal/ui/config/testdata/all.conf b/internal/ui/config/testdata/all.conf new file mode 100644 index 000000000..900631e28 --- /dev/null +++ b/internal/ui/config/testdata/all.conf @@ -0,0 +1,12 @@ +repo = "sftp:user@server:/srv/repo" +password = "secret" +password_file = "/root/secret.txt" + +backup { + target = [ + "/home/user/", + "/home/otheruser", + ] + + exclude = ["*.c"] +} diff --git a/internal/ui/config/testdata/all.golden b/internal/ui/config/testdata/all.golden new file mode 100644 index 000000000..967451307 --- /dev/null +++ b/internal/ui/config/testdata/all.golden @@ -0,0 +1,15 @@ +{ + "Repo": "sftp:user@server:/srv/repo", + "Password": "secret", + "PasswordFile": "/root/secret.txt", + "Backends": null, + "Backup": { + "Target": [ + "/home/user/", + "/home/otheruser" + ], + "Excludes": [ + "*.c" + ] + } +} diff --git a/internal/ui/config/testdata/backup.golden b/internal/ui/config/testdata/backup.golden index 74155a7d9..7ae6e09ea 100644 --- a/internal/ui/config/testdata/backup.golden +++ b/internal/ui/config/testdata/backup.golden @@ -1,10 +1,13 @@ { - "Backends": null, "Repo": "", + "Password": "", + "PasswordFile": "", + "Backends": null, "Backup": { "Target": [ "foo", "/home/user" - ] + ], + "Excludes": null } } diff --git a/internal/ui/config/testdata/repo_local.golden b/internal/ui/config/testdata/repo_local.golden index 6940b3eaf..fb93b985f 100644 --- a/internal/ui/config/testdata/repo_local.golden +++ b/internal/ui/config/testdata/repo_local.golden @@ -1,10 +1,15 @@ { + "Repo": "test", + "Password": "", + "PasswordFile": "", "Backends": { "test": { "Backend": "local", "Path": "/foo/bar/baz" } }, - "Repo": "test", - "Backup": null + "Backup": { + "Target": null, + "Excludes": null + } }