2017-09-10 16:13:05 +02:00
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io/ioutil"
|
|
|
|
|
2017-09-11 13:43:18 +02:00
|
|
|
"fmt"
|
2017-09-10 16:13:05 +02:00
|
|
|
"github.com/mitchellh/mapstructure"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
yaml "gopkg.in/yaml.v2"
|
|
|
|
)
|
|
|
|
|
|
|
|
func ParseConfig(path string) (config *Config, err error) {
|
|
|
|
|
|
|
|
var i interface{}
|
|
|
|
|
|
|
|
var bytes []byte
|
|
|
|
|
|
|
|
if bytes, err = ioutil.ReadFile(path); err != nil {
|
|
|
|
err = errors.WithStack(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = yaml.Unmarshal(bytes, &i); err != nil {
|
|
|
|
err = errors.WithStack(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
return parseConfig(i)
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseConfig(i interface{}) (c *Config, err error) {
|
|
|
|
|
2017-09-11 13:43:18 +02:00
|
|
|
var asMap struct {
|
|
|
|
Global map[string]interface{}
|
|
|
|
Jobs []map[string]interface{}
|
2017-09-10 16:13:05 +02:00
|
|
|
}
|
2017-09-11 13:43:18 +02:00
|
|
|
if err := mapstructure.Decode(i, &asMap); err != nil {
|
|
|
|
return nil, errors.Wrap(err, "config root must be a dict")
|
2017-09-10 16:13:05 +02:00
|
|
|
}
|
|
|
|
|
2017-09-11 13:43:18 +02:00
|
|
|
c = &Config{}
|
2017-09-10 16:13:05 +02:00
|
|
|
|
2017-09-11 13:43:18 +02:00
|
|
|
// Parse global with defaults
|
|
|
|
c.Global.Serve.Stdinserver.SockDir = "/var/run/zrepl/stdinserver"
|
|
|
|
err = mapstructure.Decode(asMap.Global, &c.Global)
|
|
|
|
if err != nil {
|
|
|
|
err = errors.Wrap(err, "cannot parse global section: %s")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse Jobs
|
|
|
|
c.Jobs = make(map[string]Job, len(asMap.Jobs))
|
|
|
|
for i := range asMap.Jobs {
|
|
|
|
job, err := parseJob(asMap.Jobs[i])
|
2017-09-10 16:13:05 +02:00
|
|
|
if err != nil {
|
2017-09-11 13:43:18 +02:00
|
|
|
// Try to find its name
|
|
|
|
namei, ok := asMap.Jobs[i]["name"]
|
|
|
|
if !ok {
|
|
|
|
namei = fmt.Sprintf("<no name, %i in list>", i)
|
|
|
|
}
|
|
|
|
err = errors.Wrapf(err, "cannot parse job '%v'", namei)
|
2017-09-10 16:13:05 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
2017-09-11 13:43:18 +02:00
|
|
|
c.Jobs[job.JobName()] = job
|
2017-09-10 16:13:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return c, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-09-11 13:43:18 +02:00
|
|
|
func extractStringField(i map[string]interface{}, key string, notempty bool) (field string, err error) {
|
|
|
|
vi, ok := i[key]
|
2017-09-10 16:13:05 +02:00
|
|
|
if !ok {
|
2017-09-11 13:43:18 +02:00
|
|
|
err = errors.Errorf("must have field '%s'", key)
|
|
|
|
return "", err
|
2017-09-10 16:13:05 +02:00
|
|
|
}
|
2017-09-11 13:43:18 +02:00
|
|
|
field, ok = vi.(string)
|
2017-09-10 16:13:05 +02:00
|
|
|
if !ok {
|
2017-09-11 13:43:18 +02:00
|
|
|
err = errors.Errorf("'%s' field must have type string", key)
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if notempty && len(field) <= 0 {
|
|
|
|
err = errors.Errorf("'%s' field must not be empty", key)
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseJob(i map[string]interface{}) (j Job, err error) {
|
|
|
|
|
|
|
|
name, err := extractStringField(i, "name", true)
|
|
|
|
if err != nil {
|
|
|
|
return
|
2017-09-10 16:13:05 +02:00
|
|
|
}
|
|
|
|
|
2017-09-11 13:43:18 +02:00
|
|
|
jobtype, err := extractStringField(i, "type", true)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
switch jobtype {
|
2017-09-10 16:13:05 +02:00
|
|
|
case "pull":
|
|
|
|
return parsePullJob(name, i)
|
|
|
|
case "source":
|
|
|
|
return parseSourceJob(name, i)
|
|
|
|
case "local":
|
|
|
|
return parseLocalJob(name, i)
|
|
|
|
default:
|
2017-09-11 13:43:18 +02:00
|
|
|
return nil, errors.Errorf("unknown job type '%s'", jobtype)
|
2017-09-10 16:13:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
panic("implementation error")
|
|
|
|
return nil, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-09-11 15:45:10 +02:00
|
|
|
func parseConnect(i map[string]interface{}) (c RWCConnecter, err error) {
|
2017-09-11 13:43:18 +02:00
|
|
|
|
|
|
|
t, err := extractStringField(i, "type", true)
|
|
|
|
if err != nil {
|
2017-09-10 16:13:05 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-09-11 13:43:18 +02:00
|
|
|
switch t {
|
2017-09-10 16:13:05 +02:00
|
|
|
case "ssh+stdinserver":
|
|
|
|
return parseSSHStdinserverConnecter(i)
|
|
|
|
default:
|
2017-09-11 13:43:18 +02:00
|
|
|
return nil, errors.Errorf("unknown connection type '%s'", t)
|
2017-09-10 16:13:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
panic("implementation error")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseInitialReplPolicy(v interface{}, defaultPolicy InitialReplPolicy) (p InitialReplPolicy, err error) {
|
|
|
|
s, ok := v.(string)
|
|
|
|
if !ok {
|
|
|
|
goto err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case s == "":
|
|
|
|
p = defaultPolicy
|
|
|
|
case s == "most_recent":
|
|
|
|
p = InitialReplPolicyMostRecent
|
|
|
|
case s == "all":
|
|
|
|
p = InitialReplPolicyAll
|
|
|
|
default:
|
|
|
|
goto err
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
err:
|
|
|
|
err = errors.New(fmt.Sprintf("expected InitialReplPolicy, got %#v", v))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func parsePrunePolicy(v map[string]interface{}) (p PrunePolicy, err error) {
|
|
|
|
|
2017-09-11 13:43:18 +02:00
|
|
|
policyName, err := extractStringField(v, "policy", true)
|
|
|
|
if err != nil {
|
2017-09-10 16:13:05 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
switch policyName {
|
|
|
|
case "grid":
|
|
|
|
return parseGridPrunePolicy(v)
|
2017-09-13 23:46:15 +02:00
|
|
|
case "noprune":
|
|
|
|
return NoPrunePolicy{}, nil
|
2017-09-10 16:13:05 +02:00
|
|
|
default:
|
|
|
|
err = errors.Errorf("unknown policy '%s'", policyName)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
panic("implementation error")
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseAuthenticatedChannelListenerFactory(v map[string]interface{}) (p AuthenticatedChannelListenerFactory, err error) {
|
|
|
|
|
2017-09-11 13:43:18 +02:00
|
|
|
t, err := extractStringField(v, "type", true)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2017-09-10 16:13:05 +02:00
|
|
|
}
|
|
|
|
|
2017-09-11 13:43:18 +02:00
|
|
|
switch t {
|
2017-09-10 16:13:05 +02:00
|
|
|
case "stdinserver":
|
|
|
|
return parseStdinserverListenerFactory(v)
|
|
|
|
default:
|
2017-09-11 13:43:18 +02:00
|
|
|
err = errors.Errorf("unknown type '%s'", t)
|
2017-09-10 16:13:05 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
panic("implementation error")
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|