first draft of config loading exclusively using env vars
This commit is contained in:
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user