summaryrefslogtreecommitdiffstats
path: root/utils/config.go
diff options
context:
space:
mode:
authorHarrison Healey <harrisonmhealey@gmail.com>2018-04-06 12:17:43 -0400
committerGitHub <noreply@github.com>2018-04-06 12:17:43 -0400
commitff077c6761bd4b6d170831f7f2ba474c2a9bd5e0 (patch)
tree3c3236165a8df4d273bd57ded31a8aca3c8fcd3e /utils/config.go
parentf9015a37f3f3ffe9dac9d3c3a44c7795b8b8b8b0 (diff)
downloadchat-ff077c6761bd4b6d170831f7f2ba474c2a9bd5e0.tar.gz
chat-ff077c6761bd4b6d170831f7f2ba474c2a9bd5e0.tar.bz2
chat-ff077c6761bd4b6d170831f7f2ba474c2a9bd5e0.zip
MM-8400 Provide default config values to viper so that it reads all environment variables (#8581)
* MM-8400 Provide default config values to viper so that it reads all environment variables * Added unit tests
Diffstat (limited to 'utils/config.go')
-rw-r--r--utils/config.go93
1 files changed, 85 insertions, 8 deletions
diff --git a/utils/config.go b/utils/config.go
index 13295b362..b87f164ee 100644
--- a/utils/config.go
+++ b/utils/config.go
@@ -10,6 +10,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
+ "reflect"
"strconv"
"strings"
@@ -212,15 +213,8 @@ func (w *ConfigWatcher) Close() {
// ReadConfig reads and parses the given configuration.
func ReadConfig(r io.Reader, allowEnvironmentOverrides bool) (*model.Config, error) {
- v := viper.New()
+ v := newViper(allowEnvironmentOverrides)
- if allowEnvironmentOverrides {
- v.SetEnvPrefix("mm")
- v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
- v.AutomaticEnv()
- }
-
- v.SetConfigType("json")
if err := v.ReadConfig(r); err != nil {
return nil, err
}
@@ -236,6 +230,89 @@ func ReadConfig(r io.Reader, allowEnvironmentOverrides bool) (*model.Config, err
return &config, unmarshalErr
}
+func newViper(allowEnvironmentOverrides bool) *viper.Viper {
+ v := viper.New()
+
+ v.SetConfigType("json")
+
+ if allowEnvironmentOverrides {
+ v.SetEnvPrefix("mm")
+ v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
+ v.AutomaticEnv()
+ }
+
+ // Set zeroed defaults for all the config settings so that Viper knows what environment variables
+ // it needs to be looking for. The correct defaults will later be applied using Config.SetDefaults.
+ defaults := flattenStructToMap(structToMap(reflect.TypeOf(model.Config{})))
+
+ for key, value := range defaults {
+ v.SetDefault(key, value)
+ }
+
+ return v
+}
+
+// Converts a struct type into a nested map with keys matching the struct's fields and values
+// matching the zeroed value of the corresponding field.
+func structToMap(t reflect.Type) map[string]interface{} {
+ if t.Kind() != reflect.Struct {
+ // Should never hit this, but this will prevent a panic if that does happen somehow
+ return nil
+ }
+
+ out := make(map[string]interface{})
+
+ for i := 0; i < t.NumField(); i++ {
+ field := t.Field(i)
+
+ var value interface{}
+
+ switch field.Type.Kind() {
+ case reflect.Struct:
+ value = structToMap(field.Type)
+ case reflect.Ptr:
+ value = nil
+ default:
+ value = reflect.Zero(field.Type).Interface()
+ }
+
+ out[field.Name] = value
+ }
+
+ return out
+}
+
+// Flattens a nested map so that the result is a single map with keys corresponding to the
+// path through the original map. For example,
+// {
+// "a": {
+// "b": 1
+// },
+// "c": "sea"
+// }
+// would flatten to
+// {
+// "a.b": 1,
+// "c": "sea"
+// }
+func flattenStructToMap(in map[string]interface{}) map[string]interface{} {
+ out := make(map[string]interface{})
+
+ for key, value := range in {
+ if valueAsMap, ok := value.(map[string]interface{}); ok {
+ sub := flattenStructToMap(valueAsMap)
+
+ for subKey, subValue := range sub {
+ out[key+"."+subKey] = subValue
+ }
+ } else {
+ out[key] = value
+ }
+ }
+
+ return out
+}
+
// ReadConfigFile reads and parses the configuration at the given file path.
func ReadConfigFile(path string, allowEnvironmentOverrides bool) (*model.Config, error) {
f, err := os.Open(path)