go-app/pkg/config/config.go

115 lines
2.5 KiB
Go

package config
import (
"context"
"errors"
"flag"
"fmt"
"os"
"runtime/debug"
"time"
"github.com/caarlos0/env/v11"
"gopkg.in/yaml.v3"
)
// To be set by ldflags in go build command or
// retrieved from build meta below
var Version = "(devel)"
// Calling this will try to load from config if -config is
// provided as a file, and will apply any environment overrides
// on-top of configuration defaults.
// Config is stored in returned context, and can be retrieved
// using config.FromCtx(ctx)
func LoadConfig(ctx context.Context) (context.Context, error) {
configPath := flag.String("config", "", "Path to the configuration file")
flag.Parse()
// Start with defaults
// Load from config if provided
// Layer on environment
cfg, err := loadConfig(*configPath)
if err != nil {
return ctx, err
}
// Add config to context, and return
// an updated context
ctx = cfg.AddToCtx(ctx)
return ctx, nil
}
func loadConfig(configPath string) (*AppConfig, error) {
cfg := *DefaultConfig
if configPath != "" {
file, err := os.Open(configPath)
if err != nil {
return nil, fmt.Errorf("could not open config file: %w", err)
}
defer file.Close()
decoder := yaml.NewDecoder(file)
if err := decoder.Decode(&cfg); err != nil {
return nil, fmt.Errorf("could not decode config file: %w", err)
}
}
if err := env.Parse(&cfg); err != nil {
return nil, fmt.Errorf("could not parse environment variables: %w", err)
}
// Perform updates / enrichments
err := prepareConfig(&cfg)
if err != nil {
return nil, err
}
return &cfg, nil
}
func prepareConfig(cfg *AppConfig) error {
var errs error
// Set timeouts
if cfg.HTTP.ReadTimeout != "" {
if rT, err := time.ParseDuration(cfg.HTTP.ReadTimeout); err == nil {
cfg.HTTP.rT = &rT
} else {
errs = errors.Join(errs, err)
}
}
if cfg.HTTP.ReadTimeout != "" {
if wT, err := time.ParseDuration(cfg.HTTP.WriteTimeout); err == nil {
cfg.HTTP.wT = &wT
} else {
errs = errors.Join(errs, err)
}
}
if cfg.HTTP.IdleTimeout != "" {
if iT, err := time.ParseDuration(cfg.HTTP.IdleTimeout); err == nil {
cfg.HTTP.iT = &iT
} else {
errs = errors.Join(errs, err)
}
}
return errs
}
// Returns read timeout, write timeout, and idle timeout, in that order
// nil if unset
func (h *HTTPConfig) Timeouts() (*time.Duration, *time.Duration, *time.Duration) {
return h.rT, h.wT, h.iT
}
func getVersion() string {
if info, ok := debug.ReadBuildInfo(); ok && info.Main.Version != "(devel)" {
return info.Main.Version
}
return Version
}