116 lines
3.9 KiB
Go
116 lines
3.9 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const defaultEnvPrefix = "GO_OMNI_"
|
|
|
|
// LoadFromEnv loads application settings from GO_OMNI_* variables. The
|
|
// database settings remain owned by database.ConfigFromEnv.
|
|
func LoadFromEnv(prefix string) (Config, error) {
|
|
return applyEnv(Default(), prefix)
|
|
}
|
|
|
|
// ApplyEnv overlays nonempty environment values on a caller's defaults or file
|
|
// settings, then validates the result. Caller-owned slices are not retained.
|
|
func ApplyEnv(defaults Config, prefix string) (Config, error) {
|
|
defaults.Errors = defaults.Errors.Clone()
|
|
defaults.TrustedProxies = append([]string(nil), defaults.TrustedProxies...)
|
|
defaults.Log.RedactKeys = append([]string(nil), defaults.Log.RedactKeys...)
|
|
return applyEnv(defaults, prefix)
|
|
}
|
|
|
|
func applyEnv(cfg Config, prefix string) (Config, error) {
|
|
prefix = strings.TrimSpace(prefix)
|
|
if prefix == "" {
|
|
prefix = defaultEnvPrefix
|
|
}
|
|
cfg.Address = envString(prefix+"HTTP_ADDR", cfg.Address)
|
|
cfg.Transport = envString(prefix+"TRANSPORT", cfg.Transport)
|
|
cfg.TLS.CertFile = envString(prefix+"TLS_CERT_FILE", cfg.TLS.CertFile)
|
|
cfg.TLS.KeyFile = envString(prefix+"TLS_KEY_FILE", cfg.TLS.KeyFile)
|
|
cfg.RequestIDHeader = envString(prefix+"REQUEST_ID_HEADER", cfg.RequestIDHeader)
|
|
if value := strings.TrimSpace(os.Getenv(prefix + "TRUSTED_PROXIES")); value != "" {
|
|
cfg.TrustedProxies = strings.Split(value, ",")
|
|
for i := range cfg.TrustedProxies {
|
|
cfg.TrustedProxies[i] = strings.TrimSpace(cfg.TrustedProxies[i])
|
|
}
|
|
}
|
|
cfg.Log.Level = envString(prefix+"LOG_LEVEL", cfg.Log.Level)
|
|
cfg.Log.Format = envString(prefix+"LOG_FORMAT", cfg.Log.Format)
|
|
cfg.Log.Output = envString(prefix+"LOG_OUTPUT", cfg.Log.Output)
|
|
cfg.Log.File = envString(prefix+"LOG_FILE", cfg.Log.File)
|
|
cfg.Log.Service = envString(prefix+"SERVICE", cfg.Log.Service)
|
|
cfg.Log.Environment = envString(prefix+"ENVIRONMENT", cfg.Log.Environment)
|
|
|
|
var err error
|
|
for _, field := range []struct {
|
|
name string
|
|
value *time.Duration
|
|
}{
|
|
{"HTTP_READ_HEADER_TIMEOUT", &cfg.HTTP.ReadHeaderTimeout},
|
|
{"HTTP_READ_TIMEOUT", &cfg.HTTP.ReadTimeout},
|
|
{"HTTP_WRITE_TIMEOUT", &cfg.HTTP.WriteTimeout},
|
|
{"HTTP_IDLE_TIMEOUT", &cfg.HTTP.IdleTimeout},
|
|
} {
|
|
if value := strings.TrimSpace(os.Getenv(prefix + field.name)); value != "" {
|
|
*field.value, err = time.ParseDuration(value)
|
|
if err != nil {
|
|
return Config{}, fmt.Errorf("%s%s must be a duration: %w", prefix, field.name, err)
|
|
}
|
|
}
|
|
}
|
|
if value := strings.TrimSpace(os.Getenv(prefix + "HTTP_MAX_HEADER_BYTES")); value != "" {
|
|
cfg.HTTP.MaxHeaderBytes, err = strconv.Atoi(value)
|
|
if err != nil {
|
|
return Config{}, fmt.Errorf("%sHTTP_MAX_HEADER_BYTES must be an integer: %w", prefix, err)
|
|
}
|
|
}
|
|
if cfg.EnableLogger, err = envBool(prefix+"ENABLE_LOGGER", cfg.EnableLogger); err != nil {
|
|
return Config{}, err
|
|
}
|
|
if cfg.Log.AddSource, err = envBool(prefix+"LOG_ADD_SOURCE", cfg.Log.AddSource); err != nil {
|
|
return Config{}, err
|
|
}
|
|
if value := strings.TrimSpace(os.Getenv(prefix + "SLOW_REQUEST_THRESHOLD")); value != "" {
|
|
cfg.SlowRequestThreshold, err = time.ParseDuration(value)
|
|
if err != nil {
|
|
return Config{}, fmt.Errorf("%sSLOW_REQUEST_THRESHOLD must be a duration: %w", prefix, err)
|
|
}
|
|
}
|
|
if value := strings.TrimSpace(os.Getenv(prefix + "SHUTDOWN_TIMEOUT")); value != "" {
|
|
cfg.ShutdownTimeout, err = time.ParseDuration(value)
|
|
if err != nil {
|
|
return Config{}, fmt.Errorf("%sSHUTDOWN_TIMEOUT must be a duration: %w", prefix, err)
|
|
}
|
|
}
|
|
if err := cfg.Validate(); err != nil {
|
|
return Config{}, err
|
|
}
|
|
return cfg, nil
|
|
}
|
|
|
|
func envString(key, fallback string) string {
|
|
if value := strings.TrimSpace(os.Getenv(key)); value != "" {
|
|
return value
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
func envBool(key string, fallback bool) (bool, error) {
|
|
value := strings.TrimSpace(os.Getenv(key))
|
|
if value == "" {
|
|
return fallback, nil
|
|
}
|
|
parsed, err := strconv.ParseBool(value)
|
|
if err != nil {
|
|
return false, fmt.Errorf("%s must be a boolean: %w", key, err)
|
|
}
|
|
return parsed, nil
|
|
}
|