Add poc to parse config from env. Should refactor (famous last words)
This commit is contained in:
parent
8ae7f2cf5b
commit
0177d349df
115
config/config.go
Normal file
115
config/config.go
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
var ConfigFetchMethod = os.Environ
|
||||||
|
|
||||||
|
|
||||||
|
func Build(prefix string, configStruct interface{}) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if e := recover(); e != nil {
|
||||||
|
switch r := e.(type) {
|
||||||
|
case string:
|
||||||
|
err = errors.New(r)
|
||||||
|
case error:
|
||||||
|
err = r
|
||||||
|
default:
|
||||||
|
err = errors.New("Undefined panic")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
assertIsPtr(configStruct)
|
||||||
|
|
||||||
|
fieldNames := getConfigStructFieldNames(configStruct)
|
||||||
|
config := loadConfig(prefix)
|
||||||
|
|
||||||
|
structRef := reflect.ValueOf(configStruct)
|
||||||
|
for _, fieldName := range fieldNames {
|
||||||
|
value, ok := config[fieldName]
|
||||||
|
if !ok {
|
||||||
|
// no such field loaded, avoid overwriting default
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
field := reflect.Indirect(structRef).FieldByName(fieldName)
|
||||||
|
typeRef := field.Type()
|
||||||
|
ret, err := tryParseString(value, typeRef.Kind())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
field.Set(reflect.ValueOf(ret).Convert(typeRef))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func tryParseString(what string, toType reflect.Kind) (interface{}, error) {
|
||||||
|
switch toType {
|
||||||
|
case reflect.String:
|
||||||
|
return what, nil
|
||||||
|
case reflect.Int:
|
||||||
|
i, err := strconv.Atoi(what)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
case reflect.Bool:
|
||||||
|
b, err := strconv.ParseBool(what)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Failed to parse value %v", what)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertIsPtr(what interface{}) {
|
||||||
|
r := reflect.ValueOf(what)
|
||||||
|
if r.Kind() != reflect.Ptr {
|
||||||
|
panic(fmt.Errorf("Supplied value is not a pointer to a struct"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConfigStructFieldNames(configStruct interface{}) []string {
|
||||||
|
configFields := []string{}
|
||||||
|
|
||||||
|
val := reflect.ValueOf(configStruct).Elem()
|
||||||
|
for i := 0; i < val.NumField(); i++ {
|
||||||
|
configFields = append(configFields, val.Type().Field(i).Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return configFields
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadConfig(prefix string) map[string]string {
|
||||||
|
prefix = fmt.Sprintf("%s_", prefix)
|
||||||
|
|
||||||
|
config := map[string]string{}
|
||||||
|
for _, env := range ConfigFetchMethod() {
|
||||||
|
parts := strings.SplitN(env, "=", 2)
|
||||||
|
key, value := parts[0], parts[1]
|
||||||
|
if strings.HasPrefix(key, prefix) {
|
||||||
|
key = strings.TrimPrefix(key, prefix)
|
||||||
|
config[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckKeysExist returns an error if not all keys are present in config map
|
||||||
|
func CheckKeysExist(config map[string]string, keys ...string) error {
|
||||||
|
for _, key := range keys {
|
||||||
|
if _, ok := config[key]; ok {
|
||||||
|
return fmt.Errorf("Config key '%s' is not set", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
49
config/config_test.go
Normal file
49
config/config_test.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package config_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"kdelsd/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
type TestConfig struct {
|
||||||
|
INT int
|
||||||
|
BOOL bool
|
||||||
|
STR string
|
||||||
|
OTHER_STR string
|
||||||
|
}
|
||||||
|
|
||||||
|
var testConfigInstance = TestConfig{
|
||||||
|
INT: 42,
|
||||||
|
BOOL: true,
|
||||||
|
STR: "sajtok",
|
||||||
|
OTHER_STR: "default-value",
|
||||||
|
}
|
||||||
|
|
||||||
|
var testEnv = []string {
|
||||||
|
"TEST_INT=42",
|
||||||
|
"TEST_BOOL=true",
|
||||||
|
"TEST_STR=sajtok",
|
||||||
|
}
|
||||||
|
|
||||||
|
func setup() {
|
||||||
|
config.ConfigFetchMethod = func() []string {
|
||||||
|
return testEnv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildConfig(t *testing.T) {
|
||||||
|
setup()
|
||||||
|
c := TestConfig{}
|
||||||
|
c.OTHER_STR = testConfigInstance.OTHER_STR
|
||||||
|
|
||||||
|
err := config.Build("TEST", &c)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c != testConfigInstance {
|
||||||
|
t.Errorf("%v != %v", c, testConfigInstance)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user