This commit is contained in:
Alexander Neumann 2018-05-14 23:21:24 +02:00
parent 8d21bb92db
commit abb1dc4eb6
5 changed files with 97 additions and 55 deletions

View File

@ -27,9 +27,25 @@ type Config struct {
// Backend configures a backend. // Backend configures a backend.
type Backend struct { type Backend struct {
Type string `hcl:"type"` Type string `hcl:"type"`
User string `hcl:"user" valid_for:"sftp"`
Host string `hcl:"host" valid_for:"sftp"` *BackendLocal `hcl:"-" json:"local"`
Path string `hcl:"path" valid_for:"sftp,local"` *BackendSFTP `hcl:"-" json:"sftp"`
}
// BackendLocal configures a local backend.
type BackendLocal struct {
Type string `hcl:"type"`
Path string `hcl:"path"`
}
// BackendSFTP configures an sftp backend.
type BackendSFTP struct {
Type string `hcl:"type"`
User string `hcl:"user"`
Host string `hcl:"host"`
Path string `hcl:"path"`
} }
// Backup sets the options for the "backup" command. // Backup sets the options for the "backup" command.
@ -166,58 +182,49 @@ func parseBackends(root *ast.ObjectList) (map[string]Backend, error) {
be.Type = "local" be.Type = "local"
} }
if _, ok := backends[name]; ok { var target interface{}
return nil, errors.Errorf("backend %q at line %v, column %v already configured", switch be.Type {
name, obj.Pos().Line, obj.Pos().Column) case "local":
be.BackendLocal = &BackendLocal{}
target = be.BackendLocal
case "sftp":
be.BackendSFTP = &BackendSFTP{}
target = be.BackendSFTP
default:
return nil, errors.Errorf("unknown backend type %q at line %v, column %v",
be.Type, obj.Pos().Line, obj.Pos().Column)
} }
// check structure of the backend object // check structure of the backend object
innerBlock, ok := obj.Val.(*ast.ObjectType) innerBlock, ok := obj.Val.(*ast.ObjectType)
if !ok { if !ok {
return nil, errors.Errorf("unable to verify structure of backend %q at line %v, column %v already configured", return nil, errors.Errorf("unable to verify structure of backend %q at line %v, column %v",
name, obj.Pos().Line, obj.Pos().Column) name, obj.Pos().Line, obj.Pos().Column)
} }
// check valid fields // check allowed types
err = validateObjects(innerBlock.List, validBackendFieldNames(be.Type)) err = validateObjects(innerBlock.List, listTags(target, "hcl"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = hcl.DecodeObject(target, innerBlock)
if err != nil {
return nil, errors.Errorf("parsing backend %q (type %s) at line %v, column %v failed: %v",
name, be.Type, obj.Pos().Line, obj.Pos().Column, err)
}
if _, ok := backends[name]; ok {
return nil, errors.Errorf("backend %q at line %v, column %v already configured",
name, obj.Pos().Line, obj.Pos().Column)
}
backends[name] = be backends[name] = be
} }
return backends, nil return backends, nil
} }
// validBackendFieldNames returns a set of names of valid options for the backend type be.
func validBackendFieldNames(be string) map[string]struct{} {
target := Backend{}
vi := reflect.ValueOf(target)
attr := make(map[string]struct{})
for i := 0; i < vi.NumField(); i++ {
typeField := vi.Type().Field(i)
tag := typeField.Tag.Get("valid_for")
name := typeField.Tag.Get("hcl")
if tag == "" {
// if the tag is not specified, it's valid for all backend types
attr[name] = struct{}{}
continue
}
for _, v := range strings.Split(tag, ",") {
if be == v {
attr[name] = struct{}{}
break
}
}
}
return attr
}
// Load loads a config from a file. // Load loads a config from a file.
func Load(filename string) (Config, error) { func Load(filename string) (Config, error) {
buf, err := ioutil.ReadFile(filename) buf, err := ioutil.ReadFile(filename)
@ -351,3 +358,8 @@ func ApplyEnv(cfg interface{}, env []string) error {
return nil return nil
} }
// ApplyOptions takes a list of Options and applies them to the config.
func ApplyOptions(cfg interface{}, opts map[string]string) error {
return errors.New("not implemented")
}

View File

@ -100,6 +100,22 @@ func TestInvalidConfigs(t *testing.T) {
}`, }`,
err: `unknown option "user"`, err: `unknown option "user"`,
}, },
{
config: `backend "foo" {
path = "/foo"
}
backend "foo" {
path = "/bar"
}`,
err: `backend "foo" already configured`,
},
{
config: `backend "foo" {
type = "xxx"
}`,
err: `unknown backend type "xxx"`,
},
} }
for _, test := range tests { for _, test := range tests {

View File

@ -5,21 +5,29 @@
"Backends": { "Backends": {
"local": { "local": {
"Type": "local", "Type": "local",
"User": "", "local": {
"Host": "", "Type": "local",
"Path": "/foo/bar" "Path": "/foo/bar"
},
"sftp": null
}, },
"local2": { "local2": {
"Type": "local", "Type": "local",
"User": "", "local": {
"Host": "", "Type": "",
"Path": "/foo/bar" "Path": "/foo/bar"
},
"sftp": null
}, },
"sftp": { "sftp": {
"Type": "sftp", "Type": "sftp",
"User": "foo", "local": null,
"Host": "bar", "sftp": {
"Path": "/foo/bar" "Type": "sftp",
"User": "foo",
"Host": "bar",
"Path": "/foo/bar"
}
} }
}, },
"Backup": { "Backup": {

View File

@ -5,15 +5,19 @@
"Backends": { "Backends": {
"bar": { "bar": {
"Type": "local", "Type": "local",
"User": "", "local": {
"Host": "", "Type": "",
"Path": "/srv/data/repo" "Path": "/srv/data/repo"
},
"sftp": null
}, },
"foo": { "foo": {
"Type": "local", "Type": "local",
"User": "", "local": {
"Host": "", "Type": "local",
"Path": "/srv/data/repo" "Path": "/srv/data/repo"
},
"sftp": null
} }
}, },
"Backup": { "Backup": {

View File

@ -5,9 +5,11 @@
"Backends": { "Backends": {
"test": { "test": {
"Type": "local", "Type": "local",
"User": "", "local": {
"Host": "", "Type": "local",
"Path": "/foo/bar/baz" "Path": "/foo/bar/baz"
},
"sftp": null
} }
}, },
"Backup": { "Backup": {