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 Config type could be different from one Command to another)
- You can add Metadata
"parseAllSources" -> "true"
to a Sub-Command if you want to parse all sources (it requires the same Config 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 differents 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
}
}
Init Stært
s:=staert.NewStaert(command)
Init TOML source
toml:=staert.NewTomlSource("example", []string{"./toml/", "/any/other/path"})
Init 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, flaeg 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 FUNC
Run function will call the func run()
from the command :
if err := s.Run(); err != nil {
//OOPS
}
}
NB : If you didn't call LoadConfig()
before, your func run()
will use your original configuration
TOML file ./toml/example.toml
:
IntField= 2
[PointerField]
We can run the example program using folowing 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 progam takes its configuration from both TOML and Flaeg 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" (witout 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
Or 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 Initialazed
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