126 lines
3.1 KiB
Go

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
}