new method of working with config
This commit is contained in:
55
internal/config/config.go
Normal file
55
internal/config/config.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/logutils"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
// time configuration
|
||||
TimeFormat string `env:"TIME_FORMAT" default:"2006-01-02 15:04:05"`
|
||||
TimeZoneLocal *time.Location
|
||||
TimeZoneUTC *time.Location
|
||||
|
||||
// logging
|
||||
LogLevel int `env:"LOG_LEVEL" default:"50"`
|
||||
Log *logutils.LevelFilter
|
||||
|
||||
// webserver
|
||||
WebServerPort int `env:"WEBSERVER_PORT" default:"8080"`
|
||||
WebServerIP string `env:"WEBSERVER_IP" default:"0.0.0.0"`
|
||||
WebServerReadTimeout int `env:"WEBSERVER_READ_TIMEOUT" default:"5"`
|
||||
WebServerWriteTimeout int `env:"WEBSERVER_WRITE_TIMEOUT" default:"1"`
|
||||
WebServerIdleTimeout int `env:"WEBSERVER_IDLE_TIMEOUT" default:"2"`
|
||||
}
|
||||
|
||||
func NewConfig() (*Config, error) {
|
||||
cfg := &Config{}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func (cfg *Config) Validate() error {
|
||||
checks := []struct {
|
||||
bad bool
|
||||
errMsg string
|
||||
}{
|
||||
{cfg.TimeFormat == "", "no TimeFormat specified"},
|
||||
{cfg.LogLevel == 0, "no LogLevel specified"},
|
||||
{cfg.WebServerPort == 0, "no WebServer.Port specified"},
|
||||
{cfg.WebServerIP == "", "no WebServer.IP specified"},
|
||||
{cfg.WebServerReadTimeout == 0, "no WebServer.ReadTimeout specified"},
|
||||
{cfg.WebServerWriteTimeout == 0, "no WebServer.WriteTimeout specified"},
|
||||
{cfg.WebServerIdleTimeout == 0, "no WebServer.IdleTimeout specified"},
|
||||
}
|
||||
|
||||
for _, check := range checks {
|
||||
if check.bad {
|
||||
return fmt.Errorf("invalid config: %s", check.errMsg)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
117
internal/envconfig/envconfig.go
Normal file
117
internal/envconfig/envconfig.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package envconfig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
/*
|
||||
ignored:"true"
|
||||
env:"ENVIRONMENT_VARIABLE"
|
||||
default:"default value"
|
||||
*/
|
||||
|
||||
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.")
|
||||
}
|
186
internal/initialize/initialize.go
Normal file
186
internal/initialize/initialize.go
Normal file
@@ -0,0 +1,186 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"mutating-webhook/internal/config"
|
||||
"mutating-webhook/internal/envconfig"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
/*
|
||||
func setLogLevel(l int) {
|
||||
switch {
|
||||
case l <= 20:
|
||||
config.Log.SetMinLevel(logutils.LogLevel("ERROR"))
|
||||
case l > 20 && l <= 40:
|
||||
config.Log.SetMinLevel(logutils.LogLevel("WARNING"))
|
||||
case l > 40 && l <= 60:
|
||||
config.Log.SetMinLevel(logutils.LogLevel("INFO"))
|
||||
case l > 60 && l <= 80:
|
||||
config.Log.SetMinLevel(logutils.LogLevel("DEBUG"))
|
||||
case l > 80:
|
||||
config.Log.SetMinLevel(logutils.LogLevel("TRACE"))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func Init() {
|
||||
/*
|
||||
var (
|
||||
tz string
|
||||
err error
|
||||
)
|
||||
*/
|
||||
|
||||
cfg, err := config.NewConfig()
|
||||
if err != nil {
|
||||
log.Fatalf("[FATAL] Unable to initialize.")
|
||||
}
|
||||
|
||||
cfgInfo, err := envconfig.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()
|
||||
|
||||
if err = cfg.Validate(); err != nil {
|
||||
log.Fatalf("[FATAL] %v", err)
|
||||
}
|
||||
|
||||
/*
|
||||
// log configuration
|
||||
flag.IntVar(&config.LogLevel,
|
||||
"log",
|
||||
getEnvInt("LOG_LEVEL", 50),
|
||||
"(LOG_LEVEL)\nlog level")
|
||||
// local webserver configuration
|
||||
flag.IntVar(&config.WebSrvPort,
|
||||
"http-port",
|
||||
getEnvInt("HTTP_PORT", 8080),
|
||||
"(HTTP_PORT)\nlisten port for internal webserver")
|
||||
flag.StringVar(&config.WebSrvIP,
|
||||
"http-ip",
|
||||
getEnvString("HTTP_IP", ""),
|
||||
"(HTTP_IP)\nlisten ip for internal webserver")
|
||||
flag.IntVar(&config.WebSrvReadTimeout,
|
||||
"http-read-timeout",
|
||||
getEnvInt("HTTP_READ_TIMEOUT", 5),
|
||||
"(HTTP_READ_TIMEOUT)\ninternal http server read timeout in seconds")
|
||||
flag.IntVar(&config.WebSrvWriteTimeout,
|
||||
"http-write-timeout",
|
||||
getEnvInt("HTTP_WRITE_TIMEOUT", 2),
|
||||
"(HTTP_WRITE_TIMEOUT\ninternal http server write timeout in seconds")
|
||||
flag.IntVar(&config.WebSrvIdleTimeout,
|
||||
"http-idle-timeout",
|
||||
getEnvInt("HTTP_IDLE_TIMEOUT", 2),
|
||||
"(HTTP_IDLE_TIMEOUT)\ninternal http server idle timeout in seconds")
|
||||
// timezone
|
||||
flag.StringVar(&tz,
|
||||
"timezone",
|
||||
getEnvString("TZ", "America/Chicago"),
|
||||
"(TZ)\ntimezone")
|
||||
// read command line options
|
||||
flag.Parse()
|
||||
|
||||
// logging level
|
||||
setLogLevel(config.LogLevel)
|
||||
log.SetOutput(config.Log)
|
||||
|
||||
// timezone configuration
|
||||
config.TimeZoneUTC, _ = time.LoadLocation("UTC")
|
||||
if config.TimeZoneLocal, err = time.LoadLocation(tz); 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")
|
||||
}
|
||||
|
||||
// print current configuration
|
||||
log.Printf("[DEBUG] configuration value set: LOG_LEVEL = %s\n", strconv.Itoa(config.LogLevel))
|
||||
log.Printf("[DEBUG] configuration value set: HTTP_PORT = %s\n", strconv.Itoa(config.WebSrvPort))
|
||||
log.Printf("[DEBUG] configuration value set: HTTP_IP = %s\n", config.WebSrvIP)
|
||||
log.Printf("[DEBUG] configuration value set: HTTP_READ_TIMEOUT = %s\n", strconv.Itoa(config.WebSrvReadTimeout))
|
||||
log.Printf("[DEBUG] configuration value set: HTTP_WRITE_TIMEOUT = %s\n", strconv.Itoa(config.WebSrvWriteTimeout))
|
||||
log.Printf("[DEBUG] configuration value set: HTTP_IDLE_TIMEOUT = %s\n", strconv.Itoa(config.WebSrvIdleTimeout))
|
||||
log.Printf("[DEBUG] configuration value set: TZ = %s\n", tz)
|
||||
|
||||
log.Println("[INFO] initialization complete")
|
||||
*/
|
||||
}
|
Reference in New Issue
Block a user