use reflection to set config values from environment

This commit is contained in:
A.Unger
2021-11-03 11:34:56 +01:00
parent d54fc2f5b9
commit 09195ffc8c
4 changed files with 104 additions and 5 deletions

View File

@@ -68,7 +68,12 @@ func NewLogger(cfg *config.Config) log.Logger {
// ParseConfig loads accounts configuration from known paths.
func ParseConfig(c *cli.Context, cfg *config.Config) error {
return ociscfg.BindSourcesToStructs("accounts", cfg)
_, err := ociscfg.BindSourcesToStructs("accounts", cfg)
if err != nil {
return err
}
return nil
}
// SutureService allows for the accounts command to be embedded and supervised by a suture supervisor tree.

View File

@@ -65,7 +65,7 @@ func sanitizeExtensions(set []string, ext []string, f func(a, b string) bool) []
// BindSourcesToStructs assigns any config value from a config file / env variable to struct `dst`. Its only purpose
// is to solely modify `dst`, not dealing with the config structs; and do so in a thread safe manner.
func BindSourcesToStructs(extension string, dst interface{}) error {
func BindSourcesToStructs(extension string, dst interface{}) (*gofig.Config, error) {
sources := DefaultConfigSources(extension, supportedExtensions)
cnf := gofig.NewWithOptions("proxy", gofig.ParseEnv)
cnf.AddDriver(gooyaml.Driver)
@@ -73,8 +73,8 @@ func BindSourcesToStructs(extension string, dst interface{}) error {
err := cnf.BindStruct("", &dst)
if err != nil {
return err
return nil, err
}
return nil
return cnf, nil
}

View File

@@ -67,7 +67,19 @@ func NewLogger(cfg *config.Config) log.Logger {
// ParseConfig loads proxy configuration from known paths.
func ParseConfig(c *cli.Context, cfg *config.Config) error {
return ociscfg.BindSourcesToStructs("proxy", cfg)
conf, err := ociscfg.BindSourcesToStructs("proxy", cfg)
if err != nil {
return err
}
// load all env variables relevant to the config in the current context.
conf.LoadOSEnv(config.GetEnv(), false)
if err = config.UnmapEnv(conf, cfg); err != nil {
return err
}
return nil
}
// SutureService allows for the proxy command to be embedded and supervised by a suture supervisor tree.

View File

@@ -0,0 +1,82 @@
package config
import (
"fmt"
"reflect"
"strings"
gofig "github.com/gookit/config/v2"
)
var mappings = []struct {
gType string // expected type, used for decoding. It is the type expected from gookit.
envName string // name of the env var
tagName string // name of the tag to select the value from. Tag names are to be unique.
}{
{
gType: "bool",
envName: "PROXY_ENABLE_BASIC_AUTH",
tagName: "enable_basic_auth",
},
}
// GetEnv fetches a list of known env variables for this extension.
func GetEnv() []string {
var r []string
for i := range mappings {
r = append(r, mappings[i].envName)
}
return r
}
func UnmapEnv(gooconf *gofig.Config, cfg *Config) error {
for i := range mappings {
switch mappings[i].gType {
case "bool":
v := gooconf.Bool(mappings[i].envName)
if err := setField(cfg, mappings[i].tagName, v); err != nil {
return err
}
case "string":
v := gooconf.String(mappings[i].envName)
if err := setField(cfg, mappings[i].tagName, v); err != nil {
return err
}
default:
return fmt.Errorf("invalid type for env var: `%v`", mappings[i].envName)
}
}
return nil
}
// setField allows us to set a value on a struct selecting by its `mapstructure` tag.
func setField(item interface{}, fieldName string, value interface{}) error {
v := reflect.ValueOf(item).Elem()
if !v.CanAddr() {
return fmt.Errorf("cannot assign to the item passed, item must be a pointer in order to assign")
}
fName := func(t reflect.StructTag) (string, error) {
if jt, ok := t.Lookup("mapstructure"); ok {
return strings.Split(jt, ",")[0], nil
}
return "", fmt.Errorf("tag %s provided does not define a json tag", fieldName)
}
fieldNames := map[string]int{}
for i := 0; i < v.NumField(); i++ {
typeField := v.Type().Field(i)
tag := typeField.Tag
jName, _ := fName(tag)
fieldNames[jName] = i
}
fieldNum, ok := fieldNames[fieldName]
if !ok {
return fmt.Errorf("field does not exist within the provided item")
}
fieldVal := v.Field(fieldNum)
fieldVal.Set(reflect.ValueOf(value))
return nil
}