diff options
author | Wolfy-J <[email protected]> | 2019-01-05 15:51:15 +0300 |
---|---|---|
committer | Wolfy-J <[email protected]> | 2019-01-05 15:51:15 +0300 |
commit | 6f1a668f5d67bc81d1ac26be84620dc5c87e1581 (patch) | |
tree | 2208fb4c411f200e666694e35ef6eb48b3c5abc8 /cmd | |
parent | 94f4083b1d85d0a45b48cbf80d83e3049c30096e (diff) |
override config flags
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/rr/cmd/root.go | 87 | ||||
-rw-r--r-- | cmd/util/config.go | 114 |
2 files changed, 131 insertions, 70 deletions
diff --git a/cmd/rr/cmd/root.go b/cmd/rr/cmd/root.go index 15a004e0..ceeeb840 100644 --- a/cmd/rr/cmd/root.go +++ b/cmd/rr/cmd/root.go @@ -23,16 +23,15 @@ package cmd import ( "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "github.com/spf13/viper" "github.com/spiral/roadrunner/cmd/util" "github.com/spiral/roadrunner/service" "os" - "path/filepath" ) // Service bus for all the commands. var ( - cfgFile string + cfgFile string + override []string // Verbose enables verbosity mode (container specific). Verbose bool @@ -59,26 +58,6 @@ var ( } ) -// ViperWrapper provides interface bridge between v configs and service.Config. -type ViperWrapper struct { - v *viper.Viper -} - -// Get nested config section (sub-map), returns nil if section not found. -func (w *ViperWrapper) Get(key string) service.Config { - sub := w.v.Sub(key) - if sub == nil { - return nil - } - - return &ViperWrapper{sub} -} - -// Unmarshal unmarshal config data into given struct. -func (w *ViperWrapper) Unmarshal(out interface{}) error { - return w.v.Unmarshal(out) -} - // Execute adds all child commands to the CLI command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the CLI. func Execute() { @@ -92,60 +71,28 @@ func init() { CLI.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "Verbose output") CLI.PersistentFlags().BoolVarP(&Debug, "debug", "d", false, "debug mode") CLI.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file (default is .rr.yaml)") + CLI.PersistentFlags().StringArrayVarP( + &override, + "override", + "o", + nil, + "override config value (dot.notation=value)", + ) cobra.OnInitialize(func() { if Verbose { Logger.SetLevel(logrus.DebugLevel) } - if cfg := initConfig(cfgFile, []string{"."}, ".rr"); cfg != nil { - if err := Container.Init(cfg); err != nil { - util.Printf("<red+hb>Error:</reset> <red>%s</reset>\n", err) - os.Exit(1) - } + cfg, err := util.LoadConfig(cfgFile, []string{"."}, ".rr", override) + if err != nil { + Logger.Warnf("config: %s", err) + return } - }) -} - -func initConfig(cfgFile string, path []string, name string) service.Config { - cfg := viper.New() - - if cfgFile != "" { - if absPath, err := filepath.Abs(cfgFile); err == nil { - cfgFile = absPath - // force working absPath related to config file - if err := os.Chdir(filepath.Dir(absPath)); err != nil { - Logger.Error(err) - } + if err := Container.Init(cfg); err != nil { + util.Printf("<red+hb>Error:</reset> <red>%s</reset>\n", err) + os.Exit(1) } - - // Use cfg file from the flag. - cfg.SetConfigFile(cfgFile) - - if dir, err := filepath.Abs(cfgFile); err == nil { - // force working absPath related to config file - if err := os.Chdir(filepath.Dir(dir)); err != nil { - Logger.Error(err) - } - } - } else { - // automatic location - for _, p := range path { - cfg.AddConfigPath(p) - } - - cfg.SetConfigName(name) - } - - // read in environment variables that match - cfg.AutomaticEnv() - - // If a cfg file is found, read it in. - if err := cfg.ReadInConfig(); err != nil { - Logger.Warnf("config: %s", err) - return nil - } - - return &ViperWrapper{cfg} + }) } diff --git a/cmd/util/config.go b/cmd/util/config.go new file mode 100644 index 00000000..a829f44c --- /dev/null +++ b/cmd/util/config.go @@ -0,0 +1,114 @@ +package util + +import ( + "fmt" + "github.com/spf13/viper" + "github.com/spiral/roadrunner/service" + "os" + "path/filepath" + "strings" +) + +// configWrapper provides interface bridge between v configs and service.Config. +type configWrapper struct { + v *viper.Viper +} + +// Get nested config section (sub-map), returns nil if section not found. +func (w *configWrapper) Get(key string) service.Config { + sub := w.v.Sub(key) + if sub == nil { + return nil + } + + return &configWrapper{sub} +} + +// Unmarshal unmarshal config data into given struct. +func (w *configWrapper) Unmarshal(out interface{}) error { + return w.v.Unmarshal(out) +} + +// LoadConfig config and merge it's values with set of flags. +func LoadConfig(cfgFile string, path []string, name string, flags []string) (*configWrapper, error) { + cfg := viper.New() + + if cfgFile != "" { + if absPath, err := filepath.Abs(cfgFile); err == nil { + cfgFile = absPath + + // force working absPath related to config file + if err := os.Chdir(filepath.Dir(absPath)); err != nil { + return nil, err + } + } + + // Use cfg file from the flag. + cfg.SetConfigFile(cfgFile) + + if dir, err := filepath.Abs(cfgFile); err == nil { + // force working absPath related to config file + if err := os.Chdir(filepath.Dir(dir)); err != nil { + return nil, err + } + } + } else { + // automatic location + for _, p := range path { + cfg.AddConfigPath(p) + } + + cfg.SetConfigName(name) + } + + // read in environment variables that match + cfg.AutomaticEnv() + + // If a cfg file is found, read it in. + if err := cfg.ReadInConfig(); err != nil { + return nil, err + } + + if len(flags) != 0 { + for _, f := range flags { + k, v, err := parseFlag(f) + if err != nil { + return nil, err + } + + cfg.Set(k, v) + } + + merged := viper.New() + + // we have to copy all the merged values into new config in order normalize it (viper bug?) + if err := merged.MergeConfigMap(cfg.AllSettings()); err != nil { + return nil, err + } + + return &configWrapper{merged}, nil + } + + return &configWrapper{cfg}, nil +} + +func parseFlag(flag string) (string, string, error) { + if !strings.Contains(flag, "=") { + return "", "", fmt.Errorf("invalid flag `%s`", flag) + } + + parts := strings.SplitN(strings.TrimLeft(flag, " \"'`"), "=", 2) + + return strings.Trim(parts[0], " \n\t"), parseValue(strings.Trim(parts[1], " \n\t")), nil +} + +func parseValue(value string) string { + escape := []rune(value)[0] + + if escape == '"' || escape == '\'' || escape == '`' { + value = strings.Trim(value, string(escape)) + value = strings.Replace(value, fmt.Sprintf("\\%s", string(escape)), string(escape), -1) + } + + return value +} |