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 byp2
overridingp1
B
:Fizz
- Set byp3
overridingp1
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
Notifier
interface - (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
omitempty
for pointer values which should not be initialised to their zero value. - Add support for:
- ETCD Parser / Notifier
- Consul Parser / Notifier