diff --git a/proxy/pkg/command/root.go b/proxy/pkg/command/root.go index 0eaa5260a..ae5ce38b3 100644 --- a/proxy/pkg/command/root.go +++ b/proxy/pkg/command/root.go @@ -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 } diff --git a/proxy/pkg/config/config.go b/proxy/pkg/config/config.go index 13cbb0eef..5e9c1fb94 100644 --- a/proxy/pkg/config/config.go +++ b/proxy/pkg/config/config.go @@ -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 diff --git a/proxy/pkg/config/env.go b/proxy/pkg/config/env.go new file mode 100644 index 000000000..d75650ff9 --- /dev/null +++ b/proxy/pkg/config/env.go @@ -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 +} diff --git a/proxy/pkg/config/env_mapping.go b/proxy/pkg/config/env_mapping.go deleted file mode 100644 index 6e34a3a2d..000000000 --- a/proxy/pkg/config/env_mapping.go +++ /dev/null @@ -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 -} diff --git a/proxy/pkg/config/mappings.go b/proxy/pkg/config/mappings.go new file mode 100644 index 000000000..8bab88fe2 --- /dev/null +++ b/proxy/pkg/config/mappings.go @@ -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, + }, + } +}