This commit is contained in:
Alexander Neumann 2018-05-13 15:11:21 +02:00
parent 6bad560324
commit ffd7bc1021
6 changed files with 173 additions and 25 deletions

View File

@ -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

View File

@ -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))
}
})
}
}

12
internal/ui/config/testdata/all.conf vendored Normal file
View File

@ -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"]
}

15
internal/ui/config/testdata/all.golden vendored Normal file
View File

@ -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"
]
}
}

View File

@ -1,10 +1,13 @@
{
"Backends": null,
"Repo": "",
"Password": "",
"PasswordFile": "",
"Backends": null,
"Backup": {
"Target": [
"foo",
"/home/user"
]
],
"Excludes": null
}
}

View File

@ -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
}
}