github.com/mattermost/mattermost-server/v5@v5.39.3/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/mattermost/mattermost-server/v5/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, filter func(reflect.StructField) bool) map[string]interface{} {
    93  	rType := reflect.TypeOf(model.Config{})
    94  	return generateEnvironmentMapWithBaseKey(env, rType, "MM", filter)
    95  }
    96  
    97  func generateEnvironmentMapWithBaseKey(env map[string]string, rType reflect.Type, base string, filter func(reflect.StructField) bool) 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 filter != nil && !filter(rField) {
   106  			continue
   107  		}
   108  		if rField.Type.Kind() == reflect.Struct {
   109  			if val := generateEnvironmentMapWithBaseKey(env, rField.Type, base+"_"+rField.Name, filter); val != nil {
   110  				mapRepresentation[rField.Name] = val
   111  			}
   112  		} else {
   113  			if _, ok := env[strings.ToUpper(base+"_"+rField.Name)]; ok {
   114  				mapRepresentation[rField.Name] = true
   115  			}
   116  		}
   117  	}
   118  
   119  	if len(mapRepresentation) == 0 {
   120  		return nil
   121  	}
   122  
   123  	return mapRepresentation
   124  }
   125  
   126  // removeEnvOverrides returns a new config without the given environment overrides.
   127  // If a config variable has an environment override, that variable is set to the value that was
   128  // read from the store.
   129  func removeEnvOverrides(cfg, cfgWithoutEnv *model.Config, envOverrides map[string]interface{}) *model.Config {
   130  	paths := getPaths(envOverrides)
   131  	newCfg := cfg.Clone()
   132  	for _, path := range paths {
   133  		originalVal := getVal(cfgWithoutEnv, path)
   134  		newVal := getVal(newCfg, path)
   135  		if newVal.CanSet() {
   136  			newVal.Set(originalVal)
   137  		}
   138  	}
   139  	return newCfg
   140  }
   141  
   142  // getPaths turns a nested map into a slice of paths describing the keys of the map. Eg:
   143  // map[string]map[string]map[string]bool{"this":{"is first":{"path":true}, "is second":{"path":true}))) is turned into:
   144  // [][]string{{"this", "is first", "path"}, {"this", "is second", "path"}}
   145  func getPaths(m map[string]interface{}) [][]string {
   146  	return getPathsRec(m, nil)
   147  }
   148  
   149  // getPathsRec assembles the paths (see `getPaths` above)
   150  func getPathsRec(src interface{}, curPath []string) [][]string {
   151  	if srcMap, ok := src.(map[string]interface{}); ok {
   152  		paths := [][]string{}
   153  		for k, v := range srcMap {
   154  			paths = append(paths, getPathsRec(v, append(curPath, k))...)
   155  		}
   156  		return paths
   157  	}
   158  
   159  	return [][]string{curPath}
   160  }
   161  
   162  // getVal walks `src` (here it starts with a model.Config, then recurses into its leaves)
   163  // and returns the reflect.Value of the leaf at the end `path`
   164  func getVal(src interface{}, path []string) reflect.Value {
   165  	var val reflect.Value
   166  
   167  	// If we recursed on a Value, we already have it. If we're calling on an interface{}, get the Value.
   168  	switch v := src.(type) {
   169  	case reflect.Value:
   170  		val = v
   171  	default:
   172  		val = reflect.ValueOf(src)
   173  	}
   174  
   175  	// Move into the struct
   176  	if val.Kind() == reflect.Ptr {
   177  		val = val.Elem().FieldByName(path[0])
   178  	} else {
   179  		val = val.FieldByName(path[0])
   180  	}
   181  	if val.Kind() == reflect.Ptr {
   182  		val = val.Elem()
   183  	}
   184  	if val.Kind() == reflect.Struct {
   185  		return getVal(val, path[1:])
   186  	}
   187  	return val
   188  }