📚 configuration - Awesome Go Library for Configuration
Library for initializing configuration structs from env variables, files, flags and 'default' tag.
Detailed Description of configuration
Configuration
is a library for injecting values recursively into structs - a convenient way of setting up a configuration object. Available features:
- setting default values for struct fields -
NewDefaultProvider()
- setting values from environment variables -
NewEnvProvider()
- setting values from command line flags -
NewFlagProvider(&cfg)
- setting values from a JSON file -
NewJSONFileProvider("./testdata/input.json")
Supported types:
string
,*string
,[]string
,[]*string
bool
,*bool
,[]bool
,[]*bool
int
,int8
,int16
,int32
,int64
+ slices of these types*int
,*int8
,*int16
,*int32
,*int64
+ slices of these typesuint
,uint8
,uint16
,uint32
,uint64
+ slices of these types*uint
,*uint8
,*uint16
,*uint32
,*uint64
+ slices of these typesfloat32
,float64
+ slices of these types*float32
,*float64
+ slices of these typestime.Duration
from strings like12ms
,2s
etc.- embedded structs and pointers to structs
- any custom type which satisfies
FieldSetter
interface
Why?
- your entire configuration can be defined in one model
- all metadata is in your model (defined with
tags
) - easy to set/change a source of data for your configuration
- easy to set a priority of sources to fetch data from (e.g., 1.
flags
, 2.env
, 3.default
or another order) - you can implement your custom provider
- no external dependencies
- complies with
12-factor app
Quick start
Import path github.com/BoRuDar/configuration/v4
// defining a struct
cfg := struct {
Name string `flag:"name"`
LastName string `default:"defaultLastName"`
Age byte `env:"AGE_ENV" default:"-1"`
BoolPtr *bool `default:"false"`
ObjPtr *struct {
F32 float32 `default:"32"`
StrPtr *string `default:"str_ptr_test"`
HundredMS time.Duration `default:"100ms"`
}
Obj struct {
IntPtr *int16 `default:"123"`
Beta int `file_json:"inside.beta" default:"24"`
StrSlice []string `default:"one;two"`
IntSlice []int64 `default:"3; 4"`
unexported string `xml:"ignored"`
}
}{}
configurator := configuration.New(
&cfg,
// order of execution will be preserved:
configuration.NewFlagProvider(), // 1st
configuration.NewEnvProvider(), // 2nd
configuration.NewJSONFileProvider(fileName), // 3rd
configuration.NewDefaultProvider(), // 4th
)
if err := configurator.InitValues(); err != nil {
log.Fatalf("unexpected error: ", err)
}
If you need only ENV variables and default values you can use a shorter form:
err := configuration.FromEnvAndDefault(&cfg)
Providers
You can specify one or more providers. They will be executed in order of definition:
[]Provider{
NewFlagProvider(&cfg), // 1
NewEnvProvider(), // 2
NewDefaultProvider(), // 3
}
If provider set value successfully next ones will not be executed (if flag provider from the sample above found a value env and default providers are skipped).
The value of first successfully executed provider will be set.
If none of providers found value - an application will be terminated.
This behavior can be changed with configurator.OnFailFnOpt
option:
err := configuration.New(
&cfg,
configuration.NewEnvProvider(),
configuration.NewDefaultProvider()).
SetOptions(
configuration.OnFailFnOpt(func(err error) {
log.Println(err)
}),
).InitValues()
Custom provider
You can define a custom provider which should satisfy next interface:
type Provider interface {
Name() string
Init(ptr any) error
Provide(field reflect.StructField, v reflect.Value) error
}
Default provider
Looks for default
tag and set value from it:
struct {
// ...
Name string `default:"defaultName"`
// ...
}
Env provider
Looks for env
tag and tries to find an ENV variable with the name from the tag (AGE
in this example):
struct {
// ...
Age byte `env:"AGE"`
// ...
}
Name inside tag env:"<name>"
must be unique for each field. Only UPPER register for ENV vars is accepted:
bad_env_var_name=bad
GOOD_ENV_VAR_NAME=good
Flag provider
Looks for flag
tag and tries to set value from the command line flag -first_name
struct {
// ...
Name string `flag:"first_name|default_value|Description"`
// ...
}
Name inside tag flag:"<name>"
must be unique for each field. default_value
and description
sections are optional
and can be omitted.
NewFlagProvider(&cfg)
expects a pointer to the same object for initialization.
Note: if program is executed with -help
or -h
flag you will see all available flags with description:
Flags:
-first_name "Description (default: default_value)"
And program execution will be terminated.
Options for NewFlagProvider
- WithFlagSet - sets a custom FlagSet
JSON File provider
Requires file_json:"<path_to_json_field>"
tag.
NewJSONFileProvider("./testdata/input.json")
For example, tag file_json:"cache.retention"
will assume that you have this structure of your JSON file:
{
"cache": {
"retention": 1
}
}
Additional providers
FieldSetter interface
You can define how to set fields with any custom types:
type FieldSetter interface {
SetField(field reflect.StructField, val reflect.Value, valStr string) error
}
Example:
type ipTest net.IP
func (it *ipTest) SetField(_ reflect.StructField, val reflect.Value, valStr string) error {
i := ipTest(net.ParseIP(valStr))
if val.Kind() == reflect.Pointer {
val.Set(reflect.ValueOf(&i))
} else {
val.Set(reflect.ValueOf(i))
}
return nil
}
Contribution
- Open a feature request or a bug report in issues
- Fork and create a PR into
dev
branch