config

import "github.com/infrapi/lib/pkg/config"

Reads a dotenv file once, then hands out typed and validated values.

Loading#

vars, err := config.NewConfig()

NewConfig() reads the file named by INFRAPI_CONFIG_DOTENV_FILE, or .env when that variable is unset, and returns an error if the file cannot be read.

Note

The process environment is not a fallback

Values are taken from the dotenv file only. An INFRAPI_APP_NAME exported in the shell is invisible to GetString; put it in the dotenv file, or point INFRAPI_CONFIG_DOTENV_FILE at a file that contains it.

The returned Variables exposes the raw map, the file path and the default rule set:

type Variables struct {
	Values        map[string]string
	Path          string
	DefaultConfig *DefaultConfig
}

Reading a single value#

Every getter takes a *VariableOpts:

type VariableOpts struct {
	Key       string // dotenv key
	Default   string // used when the key is absent or empty
	Validator string // go-playground/validator rule
	Sensible  bool   // mask the value in error messages
}
port, err := vars.GetInt(&config.VariableOpts{
	Key:       "MY_APP_PORT",
	Default:   "8080",
	Validator: "number",
})
GetterReturnsParsing
GetStringstringraw value
GetBoolboolstrconv.ParseBool
GetIntintstrconv.Atoi
GetInt64int64strconv.ParseInt base 10
GetSliceString[]stringjson.Unmarshal, so the value must be a JSON array
GetDurationtime.Durationtime.ParseDuration

Rules that apply to all of them:

  • A missing or empty key with no Default is an error.
  • A missing or empty key with a Default returns that default without running the validator. Defaults are trusted.
  • Validator accepts any go-playground/validator rule string, applied to the raw string value.
  • Sensible: true replaces the value with [masked] in validation errors. Use it for tokens and passwords.
  • Every error message names the key and the dotenv path, so a bad deployment is easy to trace.

Application configuration#

GetAppConfig() resolves the whole INFRAPI_APP_* set in one call and returns a typed struct:

cfg, err := vars.GetAppConfig()
type AppConfig struct {
	AppName                 string
	AppLogLevel             logrus.Level
	AppLogFormat            string
	AppListenType           string
	AppListenAddress        string
	AppPlatform             string
	AppRegion               string
	AppFqdn                 string
	AppLocation             string
	AppUrl                  string
	AppTrustedProxies       []string
	AppCorsAllowOrigins     []string
	AppCorsAllowMethods     []string
	AppCorsAllowHeaders     []string
	AppCorsExposeHeaders    []string
	AppCorsAllowCredentials bool
	AppCorsMaxAge           int64
}

The first failing key aborts the call, so the error tells you exactly what to fix. Individual getters (GetAppName, GetAppLogLevel, GetAppCorsMaxAge, …) are exported too when only one value is needed.

Default variables#

NewDefaultConfig() builds the rules below. A key with no default is mandatory.

KeyDefaultValidator
INFRAPI_APP_NAME-required
INFRAPI_APP_LOG_LEVELinfooneof=trace debug info error
INFRAPI_APP_LOG_FORMATtextoneof=text json
INFRAPI_APP_LISTEN_TYPEtcponeof=tcp systemd
INFRAPI_APP_LISTEN_ADDRESS127.0.0.1:8080hostname_port
INFRAPI_APP_FQDNos.Hostname()fqdn
INFRAPI_APP_PLATFORM-oneof=sandbox preprod prod
INFRAPI_APP_REGION-oneof=eu-west-1 eu-west-2 au-southeast-1
INFRAPI_APP_LOCATION-oneof=dc1 dc2 dc3
INFRAPI_APP_URL-http_url
INFRAPI_APP_TRUSTED_PROXIES[]-
INFRAPI_APP_CORS_ALLOW_ORIGINS["*"]-
INFRAPI_APP_CORS_ALLOW_METHODS["PUT", "PATCH", "HEAD"]-
INFRAPI_APP_CORS_ALLOW_HEADERS["Origin"]-
INFRAPI_APP_CORS_EXPOSE_HEADERS["Content-Length"]-
INFRAPI_APP_CORS_ALLOW_CREDENTIALStrue-
INFRAPI_APP_CORS_MAX_AGE12-
INFRAPI_TRACING_ENABLEDfalse-
INFRAPI_TRACING_ENDPOINT-omitempty,url; required when tracing is enabled
INFRAPI_TRACING_PROTOCOLhttponeof=http grpc
INFRAPI_TRACING_SAMPLE_RATIO1a number from 0 to 1
INFRAPI_TRACING_HEADERS[]JSON list of Name=value, masked in errors
INFRAPI_TRACING_CA_CERT_PATH-PEM file; https:// endpoint only
INFRAPI_TRACING_CLIENT_CERT_PATH-PEM file, with the key; https:// endpoint only
INFRAPI_TRACING_CLIENT_KEY_PATH-PEM file, with the certificate; https:// endpoint only

INFRAPI_APP_CORS_MAX_AGE is a number of hours. The *_PROXIES, *_ORIGINS, *_METHODS and *_HEADERS values are JSON arrays.

GetTracingConfig() resolves the INFRAPI_TRACING_* set into a TracingConfig, which tracing sets the OTLP export up from; enabled without an endpoint is an error.

Overriding the rules#

DefaultConfig is a plain struct of *VariableOpts, so a service can tighten or relax a rule before reading anything:

vars, err := config.NewConfig()
if err != nil {
	return err
}

vars.DefaultConfig.AppRegion.Validator = "oneof=emea apac"
vars.DefaultConfig.AppPlatform.Validator = "oneof=development staging production"

cfg, err := vars.GetAppConfig()

examples/config walks through defaults, custom validators and the error messages produced by an invalid file.