1// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2// See LICENSE.txt for license information. 3 4package config 5 6import ( 7 "os" 8 "reflect" 9 "strconv" 10 "strings" 11 12 "github.com/mattermost/mattermost-server/v6/model" 13) 14 15func 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 28func 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 79func 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 92func 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 97func 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. 129func 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"}} 145func getPaths(m map[string]interface{}) [][]string { 146 return getPathsRec(m, nil) 147} 148 149// getPathsRec assembles the paths (see `getPaths` above) 150func 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` 164func 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} 189