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