This commit is contained in:
Alexander Neumann 2018-05-13 22:12:41 +02:00
parent 8b0092908a
commit 0758c92afc
6 changed files with 118 additions and 10 deletions

View file

@ -8,6 +8,7 @@ import (
"github.com/hashicorp/hcl"
"github.com/hashicorp/hcl/hcl/ast"
"github.com/hashicorp/hcl/hcl/token"
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors"
"github.com/spf13/pflag"
@ -19,8 +20,13 @@ 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]interface{} `hcl:"backend"`
Backup Backup `hcl:"backup"`
Backends map[string]interface{}
Backup Backup `hcl:"backup"`
}
// Backend configures a backend.
type Backend struct {
Type string `hcl:"type"`
}
// BackendLocal is a backend in a local directory.
@ -29,6 +35,13 @@ type BackendLocal struct {
Path string `hcl:"path"`
}
// BackendSFTP is a backend stored on a server via sftp.
type BackendSFTP struct {
Type string `hcl:"type"`
User string `hcl:"user"`
Host string `hcl:"host"`
}
// Backup sets the options for the "backup" command.
type Backup struct {
Target []string `hcl:"target"`
@ -76,11 +89,20 @@ func Parse(buf []byte) (cfg Config, err error) {
return Config{}, err
}
// check for additional unknown items
root := parsed.Node.(*ast.ObjectList)
// load all 'backend' sections
cfg.Backends, err = parseBackends(root)
if err != nil {
return Config{}, err
}
// check for additional unknown items
rootTags := listTags(cfg, "hcl")
rootTags["backend"] = struct{}{}
checks := map[string]map[string]struct{}{
"": listTags(cfg, "hcl"),
"": rootTags,
"backup": listTags(Backup{}, "hcl"),
}
@ -109,6 +131,88 @@ func Parse(buf []byte) (cfg Config, err error) {
return cfg, nil
}
// parseBackends parses the backend configuration sections.
func parseBackends(root *ast.ObjectList) (map[string]interface{}, error) {
backends := make(map[string]interface{})
// find top-level backend objects
for _, item := range root.Items {
// is not an object block
if len(item.Keys) == 0 {
continue
}
// does not start with an an identifier
if item.Keys[0].Token.Type != token.IDENT {
continue
}
// something other than a backend section
if s, ok := item.Keys[0].Token.Value().(string); !ok || s != "backend" {
continue
}
// missing name
if len(item.Keys) != 2 {
return nil, errors.Errorf("backend has no name at line %v, column %v",
item.Pos().Line, item.Pos().Column)
}
// check that the name is not empty
name := item.Keys[1].Token.Value().(string)
if len(name) == 0 {
return nil, errors.Errorf("backend name is empty at line %v, column %v",
item.Pos().Line, item.Pos().Column)
}
// get the type of the backend by decoding it into the Backend truct
var be Backend
err := hcl.DecodeObject(&be, item)
if err != nil {
return nil, err
}
// then decode it into the right type
var target interface{}
switch be.Type {
case "local", "":
target = &BackendLocal{Type: "local"}
case "sftp":
target = &BackendSFTP{}
default:
return nil, errors.Errorf("backend type %q is unknown at line %v, column %v",
be.Type, item.Pos().Line, item.Pos().Column)
}
err = hcl.DecodeObject(target, item)
if err != nil {
return nil, err
}
if _, ok := backends[name]; ok {
return nil, errors.Errorf("backend %q at line %v, column %v already configured",
name, item.Pos().Line, item.Pos().Column)
}
// check structure of the backend object
innerBlock, ok := item.Val.(*ast.ObjectType)
if !ok {
return nil, errors.Errorf("unable to verify structure of backend %q at line %v, column %v already configured",
name, item.Pos().Line, item.Pos().Column)
}
// check allowed types
err = validateObjects(innerBlock.List, listTags(target, "hcl"))
if err != nil {
return nil, err
}
backends[name] = target
}
return backends, nil
}
// Load loads a config from a file.
func Load(filename string) (Config, error) {
buf, err := ioutil.ReadFile(filename)

View file

@ -2,7 +2,7 @@
"Repo": "sftp:user@server:/srv/repo",
"Password": "secret",
"PasswordFile": "/root/secret.txt",
"Backends": null,
"Backends": {},
"Backup": {
"Target": [
"/home/user/",

View file

@ -1,3 +1,5 @@
password = "geheim"
backend "foo" {
type = "local"
path = "/srv/data/repo"

View file

@ -1,13 +1,15 @@
{
"Repo": "",
"Password": "",
"Password": "geheim",
"PasswordFile": "",
"Backends": {
"bar": {
"Type": ""
"Type": "local",
"Path": "/srv/data/repo"
},
"foo": {
"Type": "local"
"Type": "local",
"Path": "/srv/data/repo"
}
},
"Backup": {

View file

@ -2,7 +2,7 @@
"Repo": "",
"Password": "",
"PasswordFile": "",
"Backends": null,
"Backends": {},
"Backup": {
"Target": [
"foo",

View file

@ -4,7 +4,7 @@
"PasswordFile": "",
"Backends": {
"test": {
"Backend": "local",
"Type": "",
"Path": "/foo/bar/baz"
}
},