github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/config/unmarshal.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package config
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  	"io"
    10  	"io/ioutil"
    11  	"reflect"
    12  	"strings"
    13  
    14  	"github.com/mattermost/viper"
    15  	"github.com/pkg/errors"
    16  
    17  	"github.com/mattermost/mattermost-server/v5/mlog"
    18  	"github.com/mattermost/mattermost-server/v5/model"
    19  	"github.com/mattermost/mattermost-server/v5/utils/jsonutils"
    20  )
    21  
    22  // newViper creates an instance of viper.Viper configured for parsing a configuration.
    23  func newViper(allowEnvironmentOverrides bool) *viper.Viper {
    24  	v := viper.New()
    25  
    26  	v.SetConfigType("json")
    27  
    28  	v.AllowEmptyEnv(true)
    29  
    30  	if allowEnvironmentOverrides {
    31  		v.SetEnvPrefix("mm")
    32  		v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
    33  		v.AutomaticEnv()
    34  	}
    35  
    36  	// Set zeroed defaults for all the config settings so that Viper knows what environment variables
    37  	// it needs to be looking for. The correct defaults will later be applied using Config.SetDefaults.
    38  	defaults := getDefaultsFromStruct(model.Config{})
    39  
    40  	for key, value := range defaults {
    41  		if key == "PluginSettings.Plugins" || key == "PluginSettings.PluginStates" {
    42  			continue
    43  		}
    44  
    45  		v.SetDefault(key, value)
    46  	}
    47  
    48  	return v
    49  }
    50  
    51  func getDefaultsFromStruct(s interface{}) map[string]interface{} {
    52  	return flattenStructToMap(structToMap(reflect.TypeOf(s)))
    53  }
    54  
    55  // Converts a struct type into a nested map with keys matching the struct's fields and values
    56  // matching the zeroed value of the corresponding field.
    57  func structToMap(t reflect.Type) (out map[string]interface{}) {
    58  	defer func() {
    59  		if r := recover(); r != nil {
    60  			mlog.Error("Panicked in structToMap. This should never happen.", mlog.Any("err", r))
    61  		}
    62  	}()
    63  
    64  	if t.Kind() != reflect.Struct {
    65  		// Should never hit this, but this will prevent a panic if that does happen somehow
    66  		return nil
    67  	}
    68  
    69  	out = map[string]interface{}{}
    70  
    71  	for i := 0; i < t.NumField(); i++ {
    72  		field := t.Field(i)
    73  
    74  		var value interface{}
    75  
    76  		switch field.Type.Kind() {
    77  		case reflect.Struct:
    78  			value = structToMap(field.Type)
    79  		case reflect.Ptr:
    80  			indirectType := field.Type.Elem()
    81  
    82  			if indirectType.Kind() == reflect.Struct {
    83  				// Follow pointers to structs since we need to define defaults for their fields
    84  				value = structToMap(indirectType)
    85  			} else {
    86  				value = nil
    87  			}
    88  		default:
    89  			value = reflect.Zero(field.Type).Interface()
    90  		}
    91  
    92  		out[field.Name] = value
    93  	}
    94  
    95  	return
    96  }
    97  
    98  // Flattens a nested map so that the result is a single map with keys corresponding to the
    99  // path through the original map. For example,
   100  // {
   101  //     "a": {
   102  //         "b": 1
   103  //     },
   104  //     "c": "sea"
   105  // }
   106  // would flatten to
   107  // {
   108  //     "a.b": 1,
   109  //     "c": "sea"
   110  // }
   111  func flattenStructToMap(in map[string]interface{}) map[string]interface{} {
   112  	out := make(map[string]interface{})
   113  
   114  	for key, value := range in {
   115  		if valueAsMap, ok := value.(map[string]interface{}); ok {
   116  			sub := flattenStructToMap(valueAsMap)
   117  
   118  			for subKey, subValue := range sub {
   119  				out[key+"."+subKey] = subValue
   120  			}
   121  		} else {
   122  			out[key] = value
   123  		}
   124  	}
   125  
   126  	return out
   127  }
   128  
   129  // marshalConfig converts the given configuration into JSON bytes for persistence.
   130  func marshalConfig(cfg *model.Config) ([]byte, error) {
   131  	return json.MarshalIndent(cfg, "", "    ")
   132  }
   133  
   134  // unmarshalConfig unmarshals a raw configuration into a Config model and environment variable overrides.
   135  func unmarshalConfig(r io.Reader, allowEnvironmentOverrides bool) (*model.Config, map[string]interface{}, error) {
   136  	// Pre-flight check the syntax of the configuration file to improve error messaging.
   137  	configData, err := ioutil.ReadAll(r)
   138  	if err != nil {
   139  		return nil, nil, errors.Wrapf(err, "failed to read")
   140  	}
   141  
   142  	var rawConfig interface{}
   143  	if err = json.Unmarshal(configData, &rawConfig); err != nil {
   144  		return nil, nil, jsonutils.HumanizeJsonError(err, configData)
   145  	}
   146  
   147  	v := newViper(allowEnvironmentOverrides)
   148  	if err := v.ReadConfig(bytes.NewReader(configData)); err != nil {
   149  		return nil, nil, err
   150  	}
   151  
   152  	var config model.Config
   153  	unmarshalErr := v.Unmarshal(&config)
   154  	// https://github.com/spf13/viper/issues/324
   155  	// https://github.com/spf13/viper/issues/348
   156  	if unmarshalErr == nil {
   157  		config.PluginSettings.Plugins = make(map[string]map[string]interface{})
   158  		unmarshalErr = v.UnmarshalKey("pluginsettings.plugins", &config.PluginSettings.Plugins)
   159  	}
   160  	if unmarshalErr == nil {
   161  		config.PluginSettings.PluginStates = make(map[string]*model.PluginState)
   162  		unmarshalErr = v.UnmarshalKey("pluginsettings.pluginstates", &config.PluginSettings.PluginStates)
   163  	}
   164  
   165  	envConfig := v.EnvSettings()
   166  
   167  	var envErr error
   168  	if envConfig, envErr = fixEnvSettingsCase(envConfig); envErr != nil {
   169  		return nil, nil, envErr
   170  	}
   171  
   172  	return &config, envConfig, unmarshalErr
   173  }
   174  
   175  // Fixes the case of the environment variables sent back from Viper since Viper stores everything
   176  // as lower case.
   177  func fixEnvSettingsCase(in map[string]interface{}) (out map[string]interface{}, err error) {
   178  	defer func() {
   179  		if r := recover(); r != nil {
   180  			mlog.Error("Panicked in fixEnvSettingsCase. This should never happen.", mlog.Any("err", r))
   181  			out = in
   182  		}
   183  	}()
   184  
   185  	var fixCase func(map[string]interface{}, reflect.Type) map[string]interface{}
   186  	fixCase = func(in map[string]interface{}, t reflect.Type) map[string]interface{} {
   187  		if t.Kind() != reflect.Struct {
   188  			// Should never hit this, but this will prevent a panic if that does happen somehow
   189  			return nil
   190  		}
   191  
   192  		fixCaseOut := make(map[string]interface{}, len(in))
   193  
   194  		for i := 0; i < t.NumField(); i++ {
   195  			field := t.Field(i)
   196  
   197  			key := field.Name
   198  			if value, ok := in[strings.ToLower(key)]; ok {
   199  				if valueAsMap, ok := value.(map[string]interface{}); ok {
   200  					fixCaseOut[key] = fixCase(valueAsMap, field.Type)
   201  				} else {
   202  					fixCaseOut[key] = value
   203  				}
   204  			}
   205  		}
   206  
   207  		return fixCaseOut
   208  	}
   209  
   210  	out = fixCase(in, reflect.TypeOf(model.Config{}))
   211  
   212  	return
   213  }