MM-28859 Add feature flag managment system using split.io and remove viper. (#15954)

* Add feature flag managment system using split.io and remove viper.

* Fixing tests.

* Attempt to fix postgres tests.

* Fix watch filepath for advanced logging.

* Review fixes.

* Some error wrapping.

* Remove unessisary store interface.

* Desanitize SplitKey

* Simplify.

* Review feedback.

* Rename split mlog adatper to split logger.

* fsInner

* Style.

* Restore oldcfg test.

* Downgrading non-actionable feature flag errors to warnings.

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
Christopher Speller
2020-10-29 15:54:39 -07:00
коммит произвёл GitHub
родитель 8bb772638c
Коммит 1aadd36644
423 изменённых файлов: 37646 добавлений и 20257 удалений

Просмотреть файл

@@ -4,11 +4,122 @@
package config
import (
"os"
"reflect"
"strconv"
"strings"
"github.com/mattermost/mattermost-server/v5/model"
)
func GetEnvironment() map[string]string {
mmenv := make(map[string]string)
for _, env := range os.Environ() {
kv := strings.SplitN(env, "=", 2)
key := strings.ToUpper(kv[0])
if strings.HasPrefix(key, "MM") {
mmenv[key] = kv[1]
}
}
return mmenv
}
func applyEnvKey(key, value string, rValueSubject reflect.Value) {
keyParts := strings.SplitN(key, "_", 2)
if len(keyParts) < 1 {
return
}
rFieldValue := rValueSubject.FieldByNameFunc(func(candidate string) bool {
candidateUpper := strings.ToUpper(candidate)
return candidateUpper == keyParts[0]
})
if !rFieldValue.IsValid() {
return
}
if rFieldValue.Kind() == reflect.Ptr {
rFieldValue = rFieldValue.Elem()
if !rFieldValue.IsValid() {
return
}
}
switch rFieldValue.Kind() {
case reflect.Struct:
// If we have only one part left, we can't deal with a struct
// the env var is incomplete so give up.
if len(keyParts) < 2 {
return
}
applyEnvKey(keyParts[1], value, rFieldValue)
case reflect.String:
rFieldValue.Set(reflect.ValueOf(value))
case reflect.Bool:
boolVal, err := strconv.ParseBool(value)
if err == nil {
rFieldValue.Set(reflect.ValueOf(boolVal))
}
case reflect.Int:
intVal, err := strconv.ParseInt(value, 10, 0)
if err == nil {
rFieldValue.Set(reflect.ValueOf(int(intVal)))
}
case reflect.Int64:
intVal, err := strconv.ParseInt(value, 10, 0)
if err == nil {
rFieldValue.Set(reflect.ValueOf(intVal))
}
case reflect.SliceOf(reflect.TypeOf("")).Kind():
rFieldValue.Set(reflect.ValueOf(strings.Split(value, " ")))
}
}
func applyEnvironmentMap(inputConfig *model.Config, env map[string]string) *model.Config {
appliedConfig := inputConfig.Clone()
rvalConfig := reflect.ValueOf(appliedConfig).Elem()
for envKey, envValue := range env {
applyEnvKey(strings.TrimPrefix(envKey, "MM_"), envValue, rvalConfig)
}
return appliedConfig
}
// generateEnvironmentMap creates a map[string]interface{} containing true at the leaves mirroring the
// configuration structure so the client can know which env variables are overridden
func generateEnvironmentMap(env map[string]string) map[string]interface{} {
rType := reflect.TypeOf(model.Config{})
return generateEnvironmentMapWithBaseKey(env, rType, "MM")
}
func generateEnvironmentMapWithBaseKey(env map[string]string, rType reflect.Type, base string) map[string]interface{} {
if rType.Kind() != reflect.Struct {
return nil
}
mapRepresentation := make(map[string]interface{})
for i := 0; i < rType.NumField(); i++ {
rField := rType.Field(i)
if rField.Type.Kind() == reflect.Struct {
if val := generateEnvironmentMapWithBaseKey(env, rField.Type, base+"_"+rField.Name); val != nil {
mapRepresentation[rField.Name] = val
}
} else {
if _, ok := env[strings.ToUpper(base+"_"+rField.Name)]; ok {
mapRepresentation[rField.Name] = true
}
}
}
if len(mapRepresentation) == 0 {
return nil
}
return mapRepresentation
}
// removeEnvOverrides returns a new config without the given environment overrides.
// If a config variable has an environment override, that variable is set to the value that was
// read from the store.