summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorWolfy-J <[email protected]>2019-01-05 15:51:15 +0300
committerWolfy-J <[email protected]>2019-01-05 15:51:15 +0300
commit6f1a668f5d67bc81d1ac26be84620dc5c87e1581 (patch)
tree2208fb4c411f200e666694e35ef6eb48b3c5abc8 /cmd
parent94f4083b1d85d0a45b48cbf80d83e3049c30096e (diff)
override config flags
Diffstat (limited to 'cmd')
-rw-r--r--cmd/rr/cmd/root.go87
-rw-r--r--cmd/util/config.go114
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
+}