From d6d54ed0bcf3b583fe681db790740cef137401d3 Mon Sep 17 00:00:00 2001 From: Niall Sheridan Date: Sat, 8 Oct 2016 00:57:10 -0500 Subject: Replace the 'datastore' option with a 'database' option The 'datastore' string option is deprecated and will be removed in a future version. The new 'database' map option is preferred. --- server/config/config.go | 115 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 86 insertions(+), 29 deletions(-) (limited to 'server/config') diff --git a/server/config/config.go b/server/config/config.go index 9678f6d..fa580b0 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -2,25 +2,30 @@ package config import ( "errors" + "fmt" "io" + "log" "os" "strconv" + "strings" "github.com/hashicorp/go-multierror" "github.com/nsheridan/cashier/server/helpers/vault" "github.com/spf13/viper" ) -// Config holds the server configuration. +// Config holds the final server configuration. type Config struct { - Server *Server `mapstructure:"server"` - Auth *Auth `mapstructure:"auth"` - SSH *SSH `mapstructure:"ssh"` - AWS *AWS `mapstructure:"aws"` - Vault *Vault `mapstructure:"vault"` + Server *Server + Auth *Auth + SSH *SSH + AWS *AWS + Vault *Vault } // unmarshalled holds the raw config. +// The original hcl config is a series of slices. The config is unmarshalled from hcl into this structure and from there +// we perform some validation checks, other overrides and then produce a final Config struct. type unmarshalled struct { Server []Server `mapstructure:"server"` Auth []Auth `mapstructure:"auth"` @@ -29,18 +34,22 @@ type unmarshalled struct { Vault []Vault `mapstructure:"vault"` } +// Database config +type Database map[string]string + // Server holds the configuration specific to the web server and sessions. type Server struct { - UseTLS bool `mapstructure:"use_tls"` - TLSKey string `mapstructure:"tls_key"` - TLSCert string `mapstructure:"tls_cert"` - Addr string `mapstructure:"address"` - Port int `mapstructure:"port"` - User string `mapstructure:"user"` - CookieSecret string `mapstructure:"cookie_secret"` - CSRFSecret string `mapstructure:"csrf_secret"` - HTTPLogFile string `mapstructure:"http_logfile"` - Datastore string `mapstructure:"datastore"` + UseTLS bool `mapstructure:"use_tls"` + TLSKey string `mapstructure:"tls_key"` + TLSCert string `mapstructure:"tls_cert"` + Addr string `mapstructure:"address"` + Port int `mapstructure:"port"` + User string `mapstructure:"user"` + CookieSecret string `mapstructure:"cookie_secret"` + CSRFSecret string `mapstructure:"csrf_secret"` + HTTPLogFile string `mapstructure:"http_logfile"` + Database Database `mapstructure:"database"` + Datastore string `mapstructure:"datastore"` // Deprecated. } // Auth holds the configuration specific to the OAuth provider. @@ -78,13 +87,13 @@ type Vault struct { func verifyConfig(u *unmarshalled) error { var err error if len(u.SSH) == 0 { - err = multierror.Append(errors.New("missing ssh config section")) + err = multierror.Append(err, errors.New("missing ssh config section")) } if len(u.Auth) == 0 { - err = multierror.Append(errors.New("missing auth config section")) + err = multierror.Append(err, errors.New("missing auth config section")) } if len(u.Server) == 0 { - err = multierror.Append(errors.New("missing server config section")) + err = multierror.Append(err, errors.New("missing server config section")) } if len(u.AWS) == 0 { // AWS config is optional @@ -94,9 +103,48 @@ func verifyConfig(u *unmarshalled) error { // Vault config is optional u.Vault = append(u.Vault, Vault{}) } + if u.Server[0].Datastore != "" { + log.Println("The `datastore` option has been deprecated in favour of the `database` option. You should update your config.") + log.Println("The new config (passwords have been redacted) should look something like:") + fmt.Printf("server {\n database {\n") + for k, v := range u.Server[0].Database { + if v == "" { + continue + } + if k == "password" { + fmt.Printf(" password = \"[ REDACTED ]\"\n") + continue + } + fmt.Printf(" %s = \"%s\"\n", k, v) + } + fmt.Printf(" }\n}\n") + } return err } +func convertDatastoreConfig(u *unmarshalled) { + // Convert the deprecated 'datastore' config to the new 'database' config. + if len(u.Server[0].Database) == 0 && u.Server[0].Datastore != "" { + c := u.Server[0].Datastore + engine := strings.Split(c, ":")[0] + switch engine { + case "mysql", "mongo": + s := strings.SplitN(c, ":", 4) + engine, user, passwd, addrs := s[0], s[1], s[2], s[3] + u.Server[0].Database = map[string]string{ + "type": engine, + "username": user, + "password": passwd, + "address": addrs, + } + case "sqlite": + s := strings.Split(c, ":") + u.Server[0].Database = map[string]string{"type": s[0], "filename": s[1]} + case "mem": + u.Server[0].Database = map[string]string{"type": "mem"} + } + } +} func setFromEnv(u *unmarshalled) { port, err := strconv.Atoi(os.Getenv("PORT")) if err == nil { @@ -128,42 +176,49 @@ func setFromVault(u *unmarshalled) error { return err } get := func(value string) (string, error) { - if value[:7] == "/vault/" { + if len(value) > 0 && value[:7] == "/vault/" { return v.Read(value) } return value, nil } + var errors error if len(u.Auth) > 0 { u.Auth[0].OauthClientID, err = get(u.Auth[0].OauthClientID) if err != nil { - err = multierror.Append(err) + errors = multierror.Append(errors, err) } u.Auth[0].OauthClientSecret, err = get(u.Auth[0].OauthClientSecret) if err != nil { - err = multierror.Append(err) + errors = multierror.Append(errors, err) } } if len(u.Server) > 0 { u.Server[0].CSRFSecret, err = get(u.Server[0].CSRFSecret) if err != nil { - err = multierror.Append(err) + errors = multierror.Append(errors, err) } u.Server[0].CookieSecret, err = get(u.Server[0].CookieSecret) if err != nil { - err = multierror.Append(err) + errors = multierror.Append(errors, err) + } + if len(u.Server[0].Database) > 0 { + u.Server[0].Database["password"], err = get(u.Server[0].Database["password"]) + if err != nil { + errors = multierror.Append(errors, err) + } } } if len(u.AWS) > 0 { u.AWS[0].AccessKey, err = get(u.AWS[0].AccessKey) if err != nil { - err = multierror.Append(err) + errors = multierror.Append(errors, err) } u.AWS[0].SecretKey, err = get(u.AWS[0].SecretKey) if err != nil { - err = multierror.Append(err) + errors = multierror.Append(errors, err) } } - return err + return errors } // ReadConfig parses a JSON configuration file into a Config struct. @@ -181,14 +236,16 @@ func ReadConfig(r io.Reader) (*Config, error) { if err := setFromVault(u); err != nil { return nil, err } + convertDatastoreConfig(u) if err := verifyConfig(u); err != nil { return nil, err } - return &Config{ + c := &Config{ Server: &u.Server[0], Auth: &u.Auth[0], SSH: &u.SSH[0], AWS: &u.AWS[0], Vault: &u.Vault[0], - }, nil + } + return c, nil } -- cgit v1.2.3