GoFig is a configuration loading library for Go. It aims to provide a simple, flexible and decoupled API for all your configuration loading needs.
- Status: PoC (Proof of Concept)
package main
import (
"log"
"os"
"os/signal"
"syscall"
"go.krak3n.codes/gofig"
"go.krak3n.codes/gofig/notifiers/fsnotify"
"go.krak3n.codes/gofig/parsers/env"
"go.krak3n.codes/gofig/parsers/yaml"
)
// Config is our configuration structure.
type Config struct {
Foo struct {
Bar string `gofig:"bar"`
} `gofig:"foo"`
Fizz struct {
Buzz string `gofig:"buzz"`
} `gofig:"fizz"`
}
func main() {
var cfg Config
// Initialise gofig with the destination struct
gfg, err := gofig.New(&cfg, gofig.WithDebug())
gofig.Must(err)
// Setsup a yaml parser with file notification support
yml := gofig.FromFileAndNotify(yaml.New(), fsnotify.New("./config.yaml"))
// Parse the yaml file and then environment variables
gofig.Must(gfg.Parse(yml, env.New(env.WithPrefix("GOFIG"))))
// Setup gofig notification channel to send notification of configuration updates
notifyCh := make(chan error, 1)
gfg.Notify(notifyCh, yml)
// Setup OS signal notification
signalCh := make(chan os.Signal, 1)
signal.Notify(signalCh, syscall.SIGINT, syscall.SIGTERM)
// Wait for OS signal or configuration changes to reload your application
for {
log.Printf("configuration: %+v\n", cfg)
select {
case err := <-notifyCh:
if err != nil {
log.Println(err)
return
}
case sig := <-signalCh:
log.Println(sig)
return
}
}
// Check close errors.
// You only need to call Close if using gofig.Notify or gofig.NotifyWithContext.
gofig.Must(gfg.Close())
}GoFig implements it's parsers as sub modules. Currently it supports:
Note priority enforcement can be disabled by using the
SetEnforcePriority()option function.
By default parsers are parsed in the order in which they are given to Parse. This also applies to
Notify. Each field tracks which parser set it's values priority, this is then checked when each
parser attempts to set a value and is rejected if it's priority is not equal to or higher than the
priority set on the field.
For example:
cfg := struct{
A string `gofig:"a"`
B string `gofig:"b"`
}{}
gofig.New(&cfg)
p1 := gofig.NewInMemoryParser()
p1.Add("a", "Foo")
p1.Add("b", "Bar")
p2 := gofig.NewInMemoryParser()
p2.Add("a", "Fizz")
p3 := gofig.NewInMemoryParser()
p3.Add("b", "Buzz")
gofig.Must(gofig.Parse(p1, p2, p3))When parsed the cfg struct will hold these values:
A:Fizz- Set byp2overridingp1B:Fizz- Set byp3overridingp1
Since the InMemoryParser implements the Notifier interface we can also dynamically update values
via Notify. We will add p2 to Notify and change the b value.
ch := make(chan error)
gofig.Notify(ch, p3, p2)
p2.Add("b", "Fizz")
gofig.Must(<-ch)Here the values will be unchanged, even though p2 updated b. This is because p3 has higher
priority than p2.
New Parsers add will always have a higher priority than previously added parsers.
- (PoC) Support notification of config changes via
Notifierinterface - (PoC) Implement File notifier on changes to files via
fsnotify - (Poc) Parser Order Priority on Notify events, e.g file changes should not override env var config
- Test Suite / Code Coverage reporting
- Helpful errors
- Support pointer values
- Default Values via a struct tag, e.g:
gofig:"foo,default=bar" - Support
omitemptyfor pointer values which should not be initialised to their zero value. - Add support for:
- ETCD Parser / Notifier
- Consul Parser / Notifier