Add poc to parse config from env. Should refactor (famous last words)
This commit is contained in:
		
							
								
								
									
										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)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user