Stært is a Go library for loading and merging a program configuration structure from many sources.
Stært was born in order to merge two sources of configuration (Flæg, TOML). Now it also supports Key-Value Store.
We developed Flæg and Stært in order to simplify configuration maintenance on Træfik.
- Load your configuration structure from many sources
- Keep your configuration structure values unchanged if no overwriting (support defaults values)
- Three native sources :
- Command line arguments using Flæg package
- TOML config file using TOML package
- Key-Value Store using libkv and mapstructure packages
- An interface to add your own sources
- Handle pointers field :
- You can give a structure of default values for pointers
- Same comportment as Flæg
- Stært is command oriented
- It use
flaeg.Command
- Same comportment as Flæg commands
- Stært supports only one command (the root-command)
- Flæg allows you to use many commands
- Only Flæg will be used if a sub-command is called. (because the configuration type could be different from one command to another)
- You can add meta-data
"parseAllSources" -> "true"
to a sub-command if you want to parse all sources (it requires the same configuration type on the sub-command and the root-command)
- It use
It works on your own configuration structure, like this one :
package example
import (
"fmt"
"github.com/containous/flaeg"
"github.com/containous/staert"
"os"
)
// Configuration is a struct which contains all different type to field
type Configuration struct {
IntField int `description:"An integer field"`
StringField string `description:"A string field"`
PointerField *PointerSubConfiguration `description:"A pointer field"`
}
// PointerSubConfiguration is a SubStructure Configuration
type PointerSubConfiguration struct {
BoolField bool `description:"A boolean field"`
FloatField float64 `description:"A float field"`
}
Let's initialize it:
func main() {
// Init with default value
config := &Configuration{
IntField: 1,
StringField: "init",
PointerField: &PointerSubConfiguration{
FloatField: 1.1,
},
}
// Set default pointers value
defaultPointersConfig := &Configuration{
PointerField: &PointerSubConfiguration{
BoolField: true,
FloatField: 99.99,
},
}
//...
}
Stært uses flaeg.Command
structure, like this:
// Create command
command := &flaeg.Command{
Name:"example",
Description:"This is an example of description",
Config:config,
DefaultPointersConfig:defaultPointersConfig,
Run: func() error {
fmt.Printf("Run example with the config :\n%+v\n", config)
fmt.Printf("PointerField contains:%+v\n", config.PointerField)
return nil
}
}
Initialize Stært:
s := staert.NewStaert(command)
Initialize TOML source:
toml := staert.NewTomlSource("example", []string{"./toml/", "/any/other/path"})
Initialize Flæg source:
f := flaeg.New(command, os.Args[1:])
Add TOML and Flæg sources:
s.AddSource(toml)
s.AddSource(f)
NB: You can change order, so that, Flæg configuration will overwrite TOML one.
Just call LoadConfig
function:
loadedConfig, err := s.LoadConfig();
if err != nil {
// oops
}
// do what you want with `loadedConfig`
// or call run function
Run function will call run()
from the command:
if err := s.Run(); err != nil {
//OOPS
}
NB: If you didn't call LoadConfig()
before, your function run()
will use your original configuration.
TOML file ./toml/example.toml
:
IntField = 2
[PointerField]
We can run the example program using following CLI arguments:
$ ./example --stringfield=owerwrittenFromFlag --pointerfield.floatfield=55.55
Run example with the config :
&{IntField:2 StringField:owerwrittenFromFlag PointerField:0xc82000ec80}
PointerField contains:&{BoolField:true FloatField:55.55}
Tagoæl is a trivial example which shows how Stært can be use. This funny GoLang program takes its configuration from both TOML and Flæg sources to display messages.
$ ./tagoael -h
tagoæl is an enhanced Hello World program to display messages with
an advanced configuration mechanism provided by Flæg & Stært.
flæg: https://github.com/containous/flaeg
stært: https://github.com/containous/staert
tagoæl: https://github.com/debovema/tagoael
Usage: tagoael [--flag=flag_argument] [-f[flag_argument]] ... set flag_argument to flag(s)
or: tagoael [--flag[=true|false| ]] [-f[true|false| ]] ... set true/false to boolean flag(s)
Flags:
-c, --commandlineoverridesconfigfile Whether configuration from command line overrides configuration from configuration file or not. (default "true")
--configfile Configuration file to use (TOML). (default "tagoael")
-i, --displayindex Whether to display index of each message (default "false")
-m, --messagetodisplay Message to display (default "HELLO WOLRD")
-n, --numbertodisplay Number of messages to display (default "1000")
-h, --help Print Help (this message) and exit
Thank you @debovema for this work :)
As with Flæg and TOML sources, the configuration structure can be loaded from a Key-Value Store.
The package libkv provides connection to many KV Store like Consul
, Etcd
or Zookeeper
.
The whole configuration structure is stored, using architecture like this pattern:
- Key:
<prefix1>/<prefix2>/.../<fieldNameLevel1>/<fieldNameLevel2>/.../<fieldName>
- Value:
<value>
It handles:
- All mapstructure features(
bool
,int
, ... , Squashed Embedded Substruct
, Pointer). - Maps with pattern :
.../<MapFieldName>/<mapKey>
-><mapValue>
(Struct as key not supported) - Slices (and Arrays) with pattern :
.../<SliceFieldName>/<SliceIndex>
-><value>
Note: Hopefully, we provide the function StoreConfig
to store your configuration structure ;)
KvSource implements Source:
type KvSource struct {
store.Store
Prefix string // like this "prefix" (without the /)
}
It can be initialized like this:
kv, err := staert.NewKvSource(backend store.Backend, addrs []string, options *store.Config, prefix string)
You can directly load data from the KV Store into the config structure (given by reference)
config := &ConfigStruct{} // Here your configuration structure by reference
err := kv.Parse(config)
// do what you want with `config`
You can add this source to Stært, as with other sources:
s.AddSource(kv)
You can also store your whole configuration structure into the KV Store:
// We assume that `config` is initialized
err := kv.StoreConfig(config)
- Fork it!
- Create your feature branch:
git checkout -b my-new-feature
- Commit your changes:
git commit -am 'Add some feature'
- Push to the branch:
git push origin my-new-feature
- Submit a pull request :D