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