package bootstrap import ( "os" "reflect" "strconv" flag "github.com/spf13/pflag" ) type Environment struct { Version string `env:"VERSION" default:"0.0.1" flag:"version" usage:"option to specify a custom version"` Codename string `env:"CODENAME" default:"Magical Anomaly" flag:"codename" usage:"option to change the release codename"` LogLevel string `env:"LOG_LEVEL" default:"debug" flag:"log-level" usage:"option to change the loglevel"` DataDirectory string `env:"DATA_DIR" default:"./data" flag:"data-dir" usage:"option to specify where the state data gets stored"` ContentDirectory string `env:"CONTENT_DIR" default:"./content" flag:"content-dir" usage:"option to specify where the content gets stored"` Hostname string `env:"HOSTNAME" default:"0.0.0.0" flag:"hostname" usage:"option to specify the address/hostname to bind the api server to"` Port int `env:"PORT" default:"8080" flag:"port" usage:"option to specify the port to bind the api server to"` Authentication bool `env:"AUTHENTICATION" default:"true" flag:"authentication" usage:"option to disable authentication"` Watchdog bool `env:"WATCHDOG" default:"true" flag:"watchdog" usage:"option to disable watchdog"` WatchdogInterval int `env:"WATCHDOG_INTERVAL" default:"60" flag:"watchdog-interval" usage:"option to specify the interval in second(s) on which watchdog runs"` WatchdogSyncMode string `env:"WATCHDOG_SYNC_MODE" default:"strict" flag:"watchdog-mode" usage:"option to specify the mode watchdog will run with: strict|sync|dry"` } func loadFromEnv(env *Environment) { v := reflect.ValueOf(env).Elem() t := v.Type() for i := 0; i < t.NumField(); i++ { field := t.Field(i) valueField := v.Field(i) // It is of great importance we set these fields above key := field.Tag.Get("env") fallback := field.Tag.Get("default") // If the variable is not found or is empty raw, ok := os.LookupEnv(key) if !ok || len(raw) == 0 { raw = fallback } switch valueField.Kind() { case reflect.String: valueField.SetString(raw) case reflect.Int: if parsed, err := strconv.Atoi(raw); err == nil { valueField.SetInt(int64(parsed)) } case reflect.Bool: if parsed, err := strconv.ParseBool(raw); err == nil { valueField.SetBool(parsed) } } } } func bindFlags(env *Environment) { v := reflect.ValueOf(env).Elem() t := v.Type() for i := 0; i < t.NumField(); i++ { field := t.Field(i) valueField := v.Field(i) flagName := field.Tag.Get("flag") flagUsage := field.Tag.Get("usage") if len(flagName) == 0 { continue } switch valueField.Kind() { case reflect.String: flag.StringVar( valueField.Addr().Interface().(*string), flagName, valueField.String(), flagUsage, ) case reflect.Int: flag.IntVar( valueField.Addr().Interface().(*int), flagName, int(valueField.Int()), flagUsage, ) case reflect.Bool: flag.BoolVar( valueField.Addr().Interface().(*bool), flagName, valueField.Bool(), flagUsage, ) } } } func LoadConfig() Environment { var env Environment loadFromEnv(&env) bindFlags(&env) flag.Parse() return env }