Config Access provides tools for loading, layering and accessing configuration files. It works with any configuration
format that can be parsed into a map[string]interface{}, which includes Go's built in JSON parser and the most
widely used YAML parsers.
This package provides a small subset of the functionality of larger configuration libraries like Viper, but has a fraction of the upstream dependencies. This package provides the configuration handling functionality for the Granitic microservices framework.
GoDoc for this package can be found here
Config Access is agnostic of how your configuration is stored and parsed, as long as the result of parsing is a
map[string]interface{}. For brevity, Config Access defines a type alias ConfigNode for
map[string]interface{}.
For example, a JSON file could be loaded and parsed as:
var config config_access.ConfigNode
f, _ := os.Open("/your/config.json")
b, _ := io.ReadAll(f)
json.Unmarshal(bytes, &config)Once loaded, Config Access provides a way of recovering individual configuration values while converting them to
various Go builtin types through an interface called a Selector. Individual items are accessed using
a dot delimited 'path' syntax.
selector := config_access.NewDefaultSelector(config, true, true)
if cs.PathExists("my.string") {
s := cs.StringVal("my.string")
}Methods exist to try and interpret configuration values as string, int, float64, bool, slices
[]interface{} and objects map[string]interface{}.
If you do not want to handle errors whenever you attempt to access a configuration value, you can use a QuietSelector
that instead of returning an error when encountering a problem or missing value, executes a function that you provide.
This can be used to implement 'fail fast' behaviour by exiting the application on an error or logging the problem. If
the function you provide does not exit the application, configuration value methods (e.g. StringVal()) will return
the zero value relevant to the type you asked for (nil, "", 0 etc")
quiet := config_access.NewDeferredErrorQuietSelector(config,
func(path string, err error){
fmt.printf("Error while trying to load config at %s: %s", path, err.Error())
})Configuration sources can be merged together. The most common use case is to combine some base common configuration with configuration that is specific to a deployment environment.
var base config_access.ConfigNode
var prod config_access.ConfigNode
f, _ := os.Open("/your/base-config.json")
b, _ := io.ReadAll(f)
json.Unmarshal(bytes, &base)
f, _ = os.Open("/your/prod-config.json")
b, _ = io.ReadAll(f)
json.Unmarshal(bytes, &prod)
combined := config_access.Merge(base, prod, false)Configuration loaded into a ConfigNode can be used to populate the fields of a struct in one call:
//Assume config previously loaded into variable config
type Name struct {
First string
Middle []string
Last string
}
var name Name
ca.Populate("orderDetails.recipient", &name, config)Selectors and QuietSelectors provide a StringOrEnv method where value at a config path will be treated as an environment
variable name, if it starts with a $. If the environment variable exists, the value of that environment variable will be
returned.
The symbol that defines the start of an environment variable can be overridden by supplying an Opts object as part of the
call to StringOrEnv