collapses config into one library

This commit is contained in:
2022-10-11 08:11:29 -05:00
parent 7468a6dcb4
commit cd14bc11fa
4 changed files with 20 additions and 25 deletions

View File

@@ -3,7 +3,6 @@ package config
import (
"fmt"
"log"
"mutating-webhook/internal/envconfig"
"os"
"reflect"
"strconv"
@@ -31,6 +30,7 @@ type Config struct {
WebServerIdleTimeout int `env:"WEBSERVER_IDLE_TIMEOUT" default:"2"`
}
// DefaultConfig initializes the config variable for use with a prepared set of defaults.
func DefaultConfig() *Config {
return &Config{
Log: &logutils.LevelFilter{
@@ -40,7 +40,7 @@ func DefaultConfig() *Config {
}
}
func (cfg *Config) Validate() error {
func (cfg *Config) validate() error {
checks := []struct {
bad bool
errMsg string
@@ -63,7 +63,7 @@ func (cfg *Config) Validate() error {
return nil
}
func (cfg *Config) SetLogLevel() {
func (cfg *Config) setLogLevel() {
switch {
case cfg.LogLevel <= 20:
cfg.Log.SetMinLevel(logutils.LogLevel("ERROR"))
@@ -79,7 +79,7 @@ func (cfg *Config) SetLogLevel() {
log.SetOutput(cfg.Log)
}
func (cfg *Config) PrintRunningConfig(cfgInfo []envconfig.StructInfo) {
func (cfg *Config) printRunningConfig(cfgInfo []StructInfo) {
log.Printf("[DEBUG] Current Running Configuration Values:")
for _, info := range cfgInfo {
switch info.Type.String() {

View File

@@ -0,0 +1,111 @@
package config
import (
"fmt"
"reflect"
"strconv"
"strings"
)
type StructInfo struct {
Name string
Alt string
Key string
Field reflect.Value
Tags reflect.StructTag
Type reflect.Type
DefaultValue interface{}
}
func getStructInfo(spec interface{}) ([]StructInfo, error) {
s := reflect.ValueOf(spec)
if s.Kind() != reflect.Pointer {
return []StructInfo{}, fmt.Errorf("getStructInfo() was sent a %s instead of a pointer to a struct.\n", s.Kind())
}
s = s.Elem()
if s.Kind() != reflect.Struct {
return []StructInfo{}, fmt.Errorf("getStructInfo() was sent a %s instead of a struct.\n", s.Kind())
}
typeOfSpec := s.Type()
infos := make([]StructInfo, 0, s.NumField())
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
ftype := typeOfSpec.Field(i)
ignored, _ := strconv.ParseBool(ftype.Tag.Get("ignored"))
if !f.CanSet() || ignored {
continue
}
for f.Kind() == reflect.Pointer {
if f.IsNil() {
if f.Type().Elem().Kind() != reflect.Struct {
break
}
f.Set(reflect.New(f.Type().Elem()))
}
f = f.Elem()
}
info := StructInfo{
Name: ftype.Name,
Alt: strings.ToUpper(ftype.Tag.Get("env")),
Key: ftype.Name,
Field: f,
Tags: ftype.Tag,
Type: ftype.Type,
}
if info.Alt != "" {
info.Key = info.Alt
}
info.Key = strings.ToUpper(info.Key)
if ftype.Tag.Get("default") != "" {
v, err := typeConversion(ftype.Type.String(), ftype.Tag.Get("default"))
if err != nil {
return []StructInfo{}, err
}
info.DefaultValue = v
}
infos = append(infos, info)
}
return infos, nil
}
func typeConversion(t, v string) (interface{}, error) {
switch t {
case "string":
return v, nil
case "int":
return strconv.ParseInt(v, 10, 0)
case "int8":
return strconv.ParseInt(v, 10, 8)
case "int16":
return strconv.ParseInt(v, 10, 16)
case "int32":
return strconv.ParseInt(v, 10, 32)
case "int64":
return strconv.ParseInt(v, 10, 64)
case "uint":
return strconv.ParseUint(v, 10, 0)
case "uint16":
return strconv.ParseUint(v, 10, 16)
case "uint32":
return strconv.ParseUint(v, 10, 32)
case "uint64":
return strconv.ParseUint(v, 10, 64)
case "float32":
return strconv.ParseFloat(v, 32)
case "float64":
return strconv.ParseFloat(v, 64)
case "complex64":
return strconv.ParseComplex(v, 64)
case "complex128":
return strconv.ParseComplex(v, 128)
case "bool":
return strconv.ParseBool(v)
}
return nil, fmt.Errorf("Unable to identify type.")
}

View File

@@ -0,0 +1,125 @@
package config
import (
"flag"
"log"
"os"
"reflect"
"strconv"
"time"
)
// getEnvString returns string from environment variable
func getEnvString(env, def string) (val string) { //nolint:deadcode
val = os.Getenv(env)
if val == "" {
return def
}
return
}
// getEnvInt returns int from environment variable
func getEnvInt(env string, def int) (ret int) {
val := os.Getenv(env)
if val == "" {
return def
}
ret, err := strconv.Atoi(val)
if err != nil {
log.Fatalf("[ERROR] Environment variable is not numeric: %v\n", env)
}
return
}
// getEnvBool returns boolean from environment variable
func getEnvBool(env string, def bool) bool {
var (
err error
retVal bool
val = os.Getenv(env)
)
if len(val) == 0 {
return def
} else {
retVal, err = strconv.ParseBool(val)
if err != nil {
log.Fatalf("[ERROR] Environment variable is not boolean: %v\n", env)
}
}
return retVal
}
// Init initializes the application configuration by reading default values from the struct's tags
// and environment variables. Tags processed by this process are as follows:
// `ignored:"true" env:"ENVIRONMENT_VARIABLE" default:"default value"`
func Init() *Config {
cfg := DefaultConfig()
cfgInfo, err := getStructInfo(cfg)
if err != nil {
log.Fatalf("[FATAL] %v", err)
}
for _, info := range cfgInfo {
switch info.Type.String() {
case "string":
var dv string
if info.DefaultValue != nil {
dv = info.DefaultValue.(string)
}
p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*string)
flag.StringVar(p, info.Name, getEnvString(info.Name, dv), "("+info.Key+")")
case "bool":
var dv bool
if info.DefaultValue != nil {
dv = info.DefaultValue.(bool)
}
p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*bool)
flag.BoolVar(p, info.Name, getEnvBool(info.Name, dv), "("+info.Key+")")
case "int":
var dv int
if info.DefaultValue != nil {
dv = int(info.DefaultValue.(int64))
}
p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*int)
flag.IntVar(p, info.Name, getEnvInt(info.Name, dv), "("+info.Key+")")
}
}
flag.Parse()
// set logging level
cfg.setLogLevel()
// validate some required values are defined.
// need to break this out to a required:"true" struct tag
if err = cfg.validate(); err != nil {
log.Fatalf("[FATAL] %v", err)
}
// timezone & format configuration
cfg.TZoneUTC, _ = time.LoadLocation("UTC")
if err != nil {
log.Fatalf("[ERROR] Unable to parse timezone string. Please use one of the timezone database values listed here: %s", "https://en.wikipedia.org/wiki/List_of_tz_database_time_zones")
}
cfg.TZoneLocal, err = time.LoadLocation(cfg.TimeZoneLocal)
if err != nil {
log.Fatalf("[ERROR] Unable to parse timezone string. Please use one of the timezone database values listed here: %s", "https://en.wikipedia.org/wiki/List_of_tz_database_time_zones")
}
time.Now().Format(cfg.TimeFormat)
// print running config
cfg.printRunningConfig(cfgInfo)
log.Println("[INFO] initialization complete")
return cfg
}