github.com/adacta-ru/mattermost-server/v6@v6.0.0/config/environment.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  	"os"
     8  	"reflect"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/adacta-ru/mattermost-server/v6/model"
    13  )
    14  
    15  func GetEnvironment() map[string]string {
    16  	mmenv := make(map[string]string)
    17  	for _, env := range os.Environ() {
    18  		kv := strings.SplitN(env, "=", 2)
    19  		key := strings.ToUpper(kv[0])
    20  		if strings.HasPrefix(key, "MM") {
    21  			mmenv[key] = kv[1]
    22  		}
    23  	}
    24  
    25  	return mmenv
    26  }
    27  
    28  func applyEnvKey(key, value string, rValueSubject reflect.Value) {
    29  	keyParts := strings.SplitN(key, "_", 2)
    30  	if len(keyParts) < 1 {
    31  		return
    32  	}
    33  	rFieldValue := rValueSubject.FieldByNameFunc(func(candidate string) bool {
    34  		candidateUpper := strings.ToUpper(candidate)
    35  		return candidateUpper == keyParts[0]
    36  	})
    37  
    38  	if !rFieldValue.IsValid() {
    39  		return
    40  	}
    41  
    42  	if rFieldValue.Kind() == reflect.Ptr {
    43  		rFieldValue = rFieldValue.Elem()
    44  		if !rFieldValue.IsValid() {
    45  			return
    46  		}
    47  	}
    48  
    49  	switch rFieldValue.Kind() {
    50  	case reflect.Struct:
    51  		// If we have only one part left, we can't deal with a struct
    52  		// the env var is incomplete so give up.
    53  		if len(keyParts) < 2 {
    54  			return
    55  		}
    56  		applyEnvKey(keyParts[1], value, rFieldValue)
    57  	case reflect.String:
    58  		rFieldValue.Set(reflect.ValueOf(value))
    59  	case reflect.Bool:
    60  		boolVal, err := strconv.ParseBool(value)
    61  		if err == nil {
    62  			rFieldValue.Set(reflect.ValueOf(boolVal))
    63  		}
    64  	case reflect.Int:
    65  		intVal, err := strconv.ParseInt(value, 10, 0)
    66  		if err == nil {
    67  			rFieldValue.Set(reflect.ValueOf(int(intVal)))
    68  		}
    69  	case reflect.Int64:
    70  		intVal, err := strconv.ParseInt(value, 10, 0)
    71  		if err == nil {
    72  			rFieldValue.Set(reflect.ValueOf(intVal))
    73  		}
    74  	case reflect.SliceOf(reflect.TypeOf("")).Kind():
    75  		rFieldValue.Set(reflect.ValueOf(strings.Split(value, " ")))
    76  	}
    77  }
    78  
    79  func applyEnvironmentMap(inputConfig *model.Config, env map[string]string) *model.Config {
    80  	appliedConfig := inputConfig.Clone()
    81  
    82  	rvalConfig := reflect.ValueOf(appliedConfig).Elem()
    83  	for envKey, envValue := range env {
    84  		applyEnvKey(strings.TrimPrefix(envKey, "MM_"), envValue, rvalConfig)
    85  	}
    86  
    87  	return appliedConfig
    88  }
    89  
    90  // generateEnvironmentMap creates a map[string]interface{} containing true at the leaves mirroring the
    91  // configuration structure so the client can know which env variables are overridden
    92  func generateEnvironmentMap(env map[string]string) map[string]interface{} {
    93  	rType := reflect.TypeOf(model.Config{})
    94  	return generateEnvironmentMapWithBaseKey(env, rType, "MM")
    95  }
    96  
    97  func generateEnvironmentMapWithBaseKey(env map[string]string, rType reflect.Type, base string) map[string]interface{} {
    98  	if rType.Kind() != reflect.Struct {
    99  		return nil
   100  	}
   101  
   102  	mapRepresentation := make(map[string]interface{})
   103  	for i := 0; i < rType.NumField(); i++ {
   104  		rField := rType.Field(i)
   105  		if rField.Type.Kind() == reflect.Struct {
   106  			if val := generateEnvironmentMapWithBaseKey(env, rField.Type, base+"_"+rField.Name); val != nil {
   107  				mapRepresentation[rField.Name] = val
   108  			}
   109  		} else {
   110  			if _, ok := env[strings.ToUpper(base+"_"+rField.Name)]; ok {
   111  				mapRepresentation[rField.Name] = true
   112  			}
   113  		}
   114  	}
   115  
   116  	if len(mapRepresentation) == 0 {
   117  		return nil
   118  	}
   119  
   120  	return mapRepresentation
   121  }
   122  
   123  // removeEnvOverrides returns a new config without the given environment overrides.
   124  // If a config variable has an environment override, that variable is set to the value that was
   125  // read from the store.
   126  func removeEnvOverrides(cfg, cfgWithoutEnv *model.Config, envOverrides map[string]interface{}) *model.Config {
   127  	paths := getPaths(envOverrides)
   128  	newCfg := cfg.Clone()
   129  	for _, path := range paths {
   130  		originalVal := getVal(cfgWithoutEnv, path)
   131  		newVal := getVal(newCfg, path)
   132  		if newVal.CanSet() {
   133  			newVal.Set(originalVal)
   134  		}
   135  	}
   136  	return newCfg
   137  }
   138  
   139  // getPaths turns a nested map into a slice of paths describing the keys of the map. Eg:
   140  // map[string]map[string]map[string]bool{"this":{"is first":{"path":true}, "is second":{"path":true}))) is turned into:
   141  // [][]string{{"this", "is first", "path"}, {"this", "is second", "path"}}
   142  func getPaths(m map[string]interface{}) [][]string {
   143  	return getPathsRec(m, nil)
   144  }
   145  
   146  // getPathsRec assembles the paths (see `getPaths` above)
   147  func getPathsRec(src interface{}, curPath []string) [][]string {
   148  	if srcMap, ok := src.(map[string]interface{}); ok {
   149  		paths := [][]string{}
   150  		for k, v := range srcMap {
   151  			paths = append(paths, getPathsRec(v, append(curPath, k))...)
   152  		}
   153  		return paths
   154  	}
   155  
   156  	return [][]string{curPath}
   157  }
   158  
   159  // getVal walks `src` (here it starts with a model.Config, then recurses into its leaves)
   160  // and returns the reflect.Value of the leaf at the end `path`
   161  func getVal(src interface{}, path []string) reflect.Value {
   162  	var val reflect.Value
   163  
   164  	// If we recursed on a Value, we already have it. If we're calling on an interface{}, get the Value.
   165  	switch v := src.(type) {
   166  	case reflect.Value:
   167  		val = v
   168  	default:
   169  		val = reflect.ValueOf(src)
   170  	}
   171  
   172  	// Move into the struct
   173  	if val.Kind() == reflect.Ptr {
   174  		val = val.Elem().FieldByName(path[0])
   175  	} else {
   176  		val = val.FieldByName(path[0])
   177  	}
   178  	if val.Kind() == reflect.Ptr {
   179  		val = val.Elem()
   180  	}
   181  	if val.Kind() == reflect.Struct {
   182  		return getVal(val, path[1:])
   183  	}
   184  	return val
   185  }