first draft of config loading exclusively using env vars

This commit is contained in:
A.Unger
2021-11-03 14:46:38 +01:00
parent ac373dd004
commit 90844c5c84
5 changed files with 119 additions and 136 deletions
+1 -1
View File
@@ -75,7 +75,7 @@ func ParseConfig(c *cli.Context, cfg *config.Config) error {
// 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 {
if err = cfg.UnmapEnv(conf); err != nil {
return err
}
+36 -36
View File
@@ -9,56 +9,56 @@ import (
// Log defines the available logging configuration.
type Log struct {
Level string `mapstructure:"log_level"`
Pretty bool `mapstructure:"log_pretty"`
Color bool `mapstructure:"log_color"`
File string `mapstructure:"log_file"`
Level string `mapstructure:"level"`
Pretty bool `mapstructure:"pretty"`
Color bool `mapstructure:"color"`
File string `mapstructure:"file"`
}
// Debug defines the available debug configuration.
type Debug struct {
Addr string `mapstructure:"debug_addr"`
Token string `mapstructure:"debug_token"`
Pprof bool `mapstructure:"debug_pprof"`
Zpages bool `mapstructure:"debug_zpages"`
Addr string `mapstructure:"addr"`
Token string `mapstructure:"token"`
Pprof bool `mapstructure:"pprof"`
Zpages bool `mapstructure:"zpages"`
}
// HTTP defines the available http configuration.
type HTTP struct {
Addr string `mapstructure:"http_addr"`
Root string `mapstructure:"http_root"`
TLSCert string `mapstructure:"http_tls_cert"`
TLSKey string `mapstructure:"http_tls_key"`
TLS bool `mapstructure:"http_tls"`
Addr string `mapstructure:"addr"`
Root string `mapstructure:"root"`
TLSCert string `mapstructure:"tls_cert"`
TLSKey string `mapstructure:"tls_key"`
TLS bool `mapstructure:"tls"`
}
// Service defines the available service configuration.
type Service struct {
Name string `mapstructure:"service_name"`
Namespace string `mapstructure:"service_namespace"`
Version string `mapstructure:"service_version"`
Name string `mapstructure:"name"`
Namespace string `mapstructure:"namespace"`
Version string `mapstructure:"version"`
}
// Tracing defines the available tracing configuration.
type Tracing struct {
Enabled bool `mapstructure:"tracing_enabled"`
Type string `mapstructure:"tracing_type"`
Endpoint string `mapstructure:"tracing_endpoint"`
Collector string `mapstructure:"tracing_collector"`
Service string `mapstructure:"tracing_service"`
Enabled bool `mapstructure:"enabled"`
Type string `mapstructure:"type"`
Endpoint string `mapstructure:"endpoint"`
Collector string `mapstructure:"collector"`
Service string `mapstructure:"service"`
}
// Policy enables us to use multiple directors.
type Policy struct {
Name string `mapstructure:"policy_name"`
Routes []Route `mapstructure:"policy_routes"`
Name string `mapstructure:"name"`
Routes []Route `mapstructure:"routes"`
}
// Route define forwarding routes
type Route struct {
Type RouteType `mapstructure:"route_type"`
Endpoint string `mapstructure:"route_endpoint"`
Backend string `mapstructure:"route_backend"`
Type RouteType `mapstructure:"type"`
Endpoint string `mapstructure:"endpoint"`
Backend string `mapstructure:"backend"`
ApacheVHost bool `mapstructure:"apache-vhost"`
}
@@ -78,18 +78,18 @@ const (
var (
// RouteTypes is an array of the available route types
RouteTypes []RouteType = []RouteType{QueryRoute, RegexRoute, PrefixRoute}
RouteTypes = []RouteType{QueryRoute, RegexRoute, PrefixRoute}
)
// Reva defines all available REVA configuration.
type Reva struct {
Address string `mapstructure:"reva_address"`
Middleware Middleware `mapstructure:"reva_middleware"`
Address string `mapstructure:"address"`
Middleware Middleware `mapstructure:"middleware"`
}
// Middleware configures proxy middlewares.
type Middleware struct {
Auth Auth `mapstructure:""`
Auth Auth `mapstructure:"middleware"`
}
// Auth configures proxy http auth middleware.
@@ -99,8 +99,8 @@ type Auth struct {
// Cache is a TTL cache configuration.
type Cache struct {
Size int `mapstructure:"cache_size"`
TTL int `mapstructure:"cache_ttl"`
Size int `mapstructure:"size"`
TTL int `mapstructure:"ttl"`
}
// Config combines all available configuration parts.
@@ -132,9 +132,9 @@ type Config struct {
// OIDC is the config for the OpenID-Connect middleware. If set the proxy will try to authenticate every request
// with the configured oidc-provider
type OIDC struct {
Issuer string `mapstructure:"oidc_issuer"`
Insecure bool `mapstructure:"oidc_insecure"`
UserinfoCache Cache `mapstructure:"oidc_user_info_cache"`
Issuer string `mapstructure:"issuer"`
Insecure bool `mapstructure:"insecure"`
UserinfoCache Cache `mapstructure:"user_info_cache"`
}
// PolicySelector is the toplevel-configuration for different selectors
@@ -147,7 +147,7 @@ type PolicySelector struct {
// StaticSelectorConf is the config for the static-policy-selector
type StaticSelectorConf struct {
Policy string `mapstructure:"static_selector_policy"`
Policy string `mapstructure:"policy"`
}
// TokenManager is the config for using the reva token manager
+55
View File
@@ -0,0 +1,55 @@
package config
import (
"fmt"
gofig "github.com/gookit/config/v2"
)
type mapping struct {
goType string // expected type, used for decoding. It is the field dynamic type.
env string // name of the env var.
destination interface{} // memory address of the original config value to modify.
}
// GetEnv fetches a list of known env variables for this extension. It is to be used by gookit, as it provides a list
// with all the environment variables an extension supports.
func GetEnv() []string {
var r = make([]string, len(structMappings(&Config{})))
for i := range structMappings(&Config{}) {
r = append(r, structMappings(&Config{})[i].env)
}
return r
}
// UnmapEnv loads values from the gooconf.Config argument and sets them in the expected destination.
func (c *Config) UnmapEnv(gooconf *gofig.Config) error {
vals := structMappings(c)
for i := range vals {
// we need to guard against v != "" because this is the condition that checks that the value is set from the environment.
// the `ok` guard is not enough, apparently.
if v, ok := gooconf.GetValue(vals[i].env); ok && v != "" {
switch vals[i].goType {
case "bool":
r := gooconf.Bool(vals[i].env)
*vals[i].destination.(*bool) = r
case "string":
r := gooconf.String(vals[i].env)
*vals[i].destination.(*string) = r
case "int":
r := gooconf.Int(vals[i].env)
*vals[i].destination.(*int) = r
case "float":
// defaults to float64
r := gooconf.Float(vals[i].env)
*vals[i].destination.(*float64) = r
default:
// it is unlikely we will ever get here. Let this serve more as a runtime check for when debugging.
return fmt.Errorf("invalid type for env var: `%v`", vals[i].env)
}
}
}
return nil
}
-99
View File
@@ -1,99 +0,0 @@
package config
import (
"fmt"
"reflect"
"strings"
gofig "github.com/gookit/config/v2"
)
// mappings holds a record of how to get an env variable's value onto a config.Config value. Field selectors are made via
// the `tagName` field. For instance having the following value:
// type example struct {
// enable `mapstructure:"enable"`
// }
//
// e := example{enable: false}
//
// we can link the field `e.enable` with the environment variable EXTENSION_ENABLE by adding an entry in this mappings:
// {
// gType: "bool",
// envName: "EXTENSION_ENABLE",
// tagName: "enable",
// }
//
// so when a config is parsed the value is read from the environment, parsed and loaded onto whatever destination
// has the tagName.
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
}
+27
View File
@@ -0,0 +1,27 @@
package config
// structMappings binds a set of environment variables to a destination on cfg.
func structMappings(cfg *Config) []mapping {
return []mapping{
{
goType: "bool",
env: "PROXY_ENABLE_BASIC_AUTH",
destination: &cfg.EnableBasicAuth,
},
{
goType: "string",
env: "PROXY_LOG_LEVEL",
destination: &cfg.Log.Level,
},
{
goType: "bool",
env: "PROXY_LOG_COLOR",
destination: &cfg.Log.Color,
},
{
goType: "bool",
env: "PROXY_LOG_PRETTY",
destination: &cfg.Log.Pretty,
},
}
}