konfig

package module
v0.8.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 4, 2019 License: MIT Imports: 16 Imported by: 0

README

Build Status codecov Go Report Card Go doc

Konfig

Composable, observable and performant config handling for Go. Written for larger distributed systems where you may have plenty of configuration sources - it allows you to compose configurations from multiple sources with reload hooks making it simple to build apps that live in a highly dynamic environment.

What's up with the name?

The name is Swedish for "config". We have a lot of nationalities here at Lalamove and to celebrate cultural diversity most of our open source packages will carry a name derived from a non-English language that is perhaps spoken by at least one of our employees(?).

Why another config package?

Most config packages for Golang are not very extensible and rarely expose interfaces. This makes it complex to build apps which can reload their state dynamically and difficult to mock. Fewer still come with sources such as Vault, Etcd and multiple encoding formats. In short, we didn't find a package that satisfied all of our requirements when we started out.

konfig is built around 4 small interfaces:

  • Loader
  • Watcher
  • Parser
  • Closer

Konfig features include:

  • Dynamic configuration loading
  • Composable load configs from multiple sources, such as vault, files and etcd
  • Polyglot load configs from multiple format. Konfig supports JSON, YAML, TOML, Key=Value
  • Fast, Lock-free, Thread safe Read Reads are up to 10x faster than Viper
  • Observable config - Hot Reload mechanism and tooling to manage state
  • Typed Read get typed values from config or bind a struct
  • Metrics exposed prometheus metrics telling you how many times a config is reloaded, if it failed, and how long it takes to reload

Get started

go get github.com/lalamove/konfig

Load and watch a json formatted config file.

var configFiles = []klfile.File{
	{
		Path:   "./config.json",
		Parser: kpjson.Parser,
	},
}

func init() {
	konfig.Init(konfig.DefaultConfig())
}

func main() {
	// load from json file
	konfig.RegisterLoaderWatcher(
		klfile.New(&klfile.Config{
			Files: configFiles,
			Watch: true,
		}),
		// optionally you can pass config hooks to run when a file is changed
		func(c konfig.Store) error {
			return nil
		},
	)

	if err := konfig.LoadWatch(); err != nil {
		log.Fatal(err)
	}

	// retrieve value from config file
	konfig.Bool("debug")
}

Store

The Store is the base of the config package. It holds and gives access to values stored by keys.

Creating a Store

You can create a global Store by calling konfig.Init(*konfig.Config):

konfig.Init(konfig.DefaultConfig())

The global store is accessible directly from the package:

konfig.Get("foo") // calls store.Get("foo")

You can create a new store by calling konfig.New(*konfig.Config):

s := konfig.New(konfig.DefaultConfig())

Loading and Watching a Store

After registering Loaders and Watchers in the konfig.Store, you must load and watch the store.

You can do both by calling LoadWatch:

if err := konfig.LoadWatch(); err != nil {
	log.Fatal(err)
}

You can call Load only, it will load all loaders and return:

if err := konfig.Load(); err != nil {
	log.Fatal(err)
}

And finally you can call Watch only, it will start all watchers and return:

if err := konfig.Watch(); err != nil {
	log.Fatal(err)
}

Loaders

Loaders load config values into the store. A loader is an implementation of the loader interface.

type Loader interface {
	// Name return the name of the load, it is used to create labeled vectors metrics per loader
	Name() string
	// StopOnFailure indicates if a failure of the loader should trigger a stop
	StopOnFailure() bool
	// Loads the config and add it to the Store
	Load(Store) error
	// MaxRetry returns the maximum number of times to allow retrying on load failure
	MaxRetry() int
	// RetryDelay returns the delay to wait before retrying
	RetryDelay() time.Duration
}

You can register loaders in the config individually or with a watcher.

Register a loader by itself:
configLoader := konfig.RegisterLoader(
	klfile.New(
		&klfile.Config{
			Files: []klfile.File{
				{
					Parser: kpjson.Parser,
					Path:   "./konfig.json",
				},
			},
		},
	),
)
Register a loader with a watcher:

To register a loader and a watcher together, you must register a LoaderWatcher which is an interface that implements both the Loader and the Watcher interface.

configLoader := konfig.RegisterLoaderWatcher(
	klfile.New(
		&klfile.Config{
			Files: []klfile.File{
				{
					Parser: kpjson.Parser,
					Path:   "./konfig.json",
				},
			},
			Watch: true,
		},
	),
)

You can also compose a loader and a watcher to create a LoaderWatcher:

configLoader := konfig.RegisterLoaderWatcher(
	// it creates a LoaderWatcher from a loader and a watcher
	konfig.NewLoaderWatcher(
		someLoader,
		someWatcher,
	),
)
Built in loaders

Konfig already has the following loaders, they all have a built in watcher:

Loads configs from files which can be watched. Files can have different parsers to load different formats. It has a built in file watcher which triggers a config reload (running hooks) when files are modified.

Loads configs from vault secrets. It has a built in Poll Watcher which triggers a config reload (running hooks) before the secret and the token from the auth provider expires.

Loads configs from HTTP sources. Sources can have different parsers to load different formats. It has a built in Poll Diff Watcher which triggers a config reload (running hooks) if data is different.

Loads configs from Etcd keys. Keys can have different parser to load different formats. It has a built in Poll Diff Watcher which triggers a config reload (running hooks) if data is different.

Loads configs from Consul KV. Keys can have different parser to load different formats. It has built in Poll Diff Watcher which triggers a config reload (running hooks) if data is different.

Loads configs from environment variables.

Loads configs from command line flags.

Loads configs from an io.Reader.

Parsers

Parsers parse an io.Reader into a konfig.Store. These are used by some loaders to parse the data they fetch into the config store. The File Loader, Etcd Loader and HTTP Loader use Parsers.

Config already has the following parsers:

Watchers

Watchers trigger a call on a Loader on events. A watcher is an implementation of the Watcher interface.

type Watcher interface {
	// Start starts the watcher, it must not be blocking.
	Start() error
	// Done indicate whether the watcher is done or not
	Done() <-chan struct{}
	// Watch should block until an event unlocks it
	Watch() <-chan struct{}
	// Close closes the watcher, it returns a non nil error if it is already closed
	// or something prevents it from closing properly.
	Close() error
	// Err returns the error attached to the watcher
	Err() error
}
Built in watchers

Konfig already has the following watchers:

Watches files for changes.

Sends events at a given rate, or if diff is enabled. It takes a Getter and fetches the data at a given rate. If data is different, it sends an event.

Hooks

Hooks are functions ran after a successful loader Load() call. They are used to reload the state of the application on a config change.

Registering a loader with some hooks

You can register a loader or a loader watcher with hooks.

configLoader := konfig.RegisterLoaderWatcher(
	klfile.New(
		&klfile.Config{
			Files: []klfile.File{
				{
					Parser: kpyaml.Parser,
					Path:   "./konfig.yaml",
				},
			},
			Watch: true,
		},
	),
	func(s konfig.Store) error {
		// Here you should reload the state of your app
		return nil
	},
)
Adding hooks to an existing loader

You can register a Loader or a LoaderWatcher with hooks.

configLoader.AddHooks(
	func(s konfig.Store) error {
		// Here you should reload the state of your app
		return nil
	},
	func(s konfig.Store) error {
		// Here you should reload the state of your app
		return nil
	},
)
Adding hooks on keys

Alternatively, you can add hooks on keys. Hooks on keys will match for prefix in order to run a hook when any key with a given prefix is updated. A hook can only be run once per load event, even if multiple keys match that hook.

konfig.RegisterKeyHook(
	"db.",
	func(s konfig.Store) error {
		return nil
	},
)

Closers

Closers can be added to konfig so that if konfig fails to load, it will execute Close() on the registered Closers.

type Closer interface {
	Close() error
}

Register a Closer

konfig.RegisterCloser(closer)

Config Groups

You can namespace your configs using config Groups.

konfig.Group("db").RegisterLoaderWatcher(
	klfile.New(
		&klfile.Config{
			Files: []klfile.File{
				{
					Parser: kpyaml.Parser,
					Path:   "./db.yaml",
				},
			},
			Watch: true,
		},
	),
)

// accessing grouped config
dbHost := konfig.Group("db").MustString("credentials.host")

Binding a Type to a Store

You can bind a type to the konfig store if you want your config values to be unmarshaled to a struct or a map[string]interface{}. Then you can access an instance of that type in a thread safe manner (in order to be safe for dynamic config updates).

Let's see with an example of a json config file:

{
    "addr": ":8080",
    "debug": true,
    "db": {
        "username": "foo"
    },
    "redis": {
        "host": "127.0.0.1"
    }
}
type DBConfig struct {
	Username string
}
type Config struct {
	Addr      string
	Debug     string
	DB        DBConfig `konfig:"db"`
	RedisHost string   `konfig:"redis.host"`
}

// we init the root konfig store
konfig.Init(konfig.DefaultConfig())

// we bind the Config struct to the konfig.Store
konfig.Bind(Config{})

// we register our config file
konfig.RegisterLoaderWatcher(
	klfile.New(
		&klfile.Config{
			Files: []klfile.File{
				{
					Parser: kpjson.Parser,
					Path:   "./config.json",
				},
			},
			Watch: true,
		},
	),
)

// we load our config and start watching
if err := konfig.LoadWatch(); err != nil {
	log.Fatal(err)
}

// Get our config value
c := konfig.Value().(Config)

fmt.Println(c.Addr) // :8080

Note that you can compose your config sources. For example, have your credentials come from Vault and be renewed often and have the rest of your config loaded from a file and be updated on file change.

It is important to understand how Konfig unmarshals your config values into your struct. When a Loader calls konfig.Set(), if the konfig store has a value bound to it, it will try to unmarshal the key to the bound value.

  • First, it will look for field tags in the struct, if a tag matches exactly the key, it will unmarshal the key to the struct field.
  • Then, it will do a EqualFold on the field name and the key, if they match, it will unmarshal the key to the struct field.
  • Then, if the key has a dot, it will check if the tag or the field name (to lowercase) is a prefix of the key, if yes, it will check if the type of the field is a struct of pointer, if yes, it will check the struct using what's after the prefix as the key.

Read from config

Apart from reading from the bound config value, konfig provides several methods to read values.

Every method to retrieve config values come in 2 flavours:

  • Get reads a value at the given key. If key is not present it returns the zero value of the type.
  • MustGet reads a value at the given key. If key is not present it panics.

All methods to read values from a Store:

// Exists checks whether the key k is set in the store.
Exists(k string) bool

// Get gets the value with the key k from the store. If the key is not set, Get returns nil. To check whether a value is really set, use Exists.
Get(k string) interface{}
// MustGet tries to get the value with the key k from the store. If the key k does not exist in the store, MustGet panics.
MustGet(k string) interface{}

// MustString tries to get the value with the key k from the store and casts it to a string. If the key k does not exist in the store, MustString panics.
MustString(k string) string
// String tries to get the value with the key k from the store and casts it to a string. If the key k does not exist it returns the Zero value.
String(k string) string

// MustInt tries to get the value with the key k from the store and casts it to a int. If the key k does not exist in the store, MustInt panics.
MustInt(k string) int
// Int tries to get the value with the key k from the store and casts it to a int. If the key k does not exist it returns the Zero value.
Int(k string) int

// MustFloat tries to get the value with the key k from the store and casts it to a float. If the key k does not exist in the store, MustFloat panics.
MustFloat(k string) float64
// Float tries to get the value with the key k from the store and casts it to a float. If the key k does not exist it returns the Zero value.
Float(k string) float64

// MustBool tries to get the value with the key k from the store and casts it to a bool. If the key k does not exist in the store, MustBool panics.
MustBool(k string) bool
// Bool tries to get the value with the key k from the store and casts it to a bool. If the key k does not exist it returns the Zero value.
Bool(k string) bool

// MustDuration tries to get the value with the key k from the store and casts it to a time.Duration. If the key k does not exist in the store, MustDuration panics.
MustDuration(k string) time.Duration
// Duration tries to get the value with the key k from the store and casts it to a time.Duration. If the key k does not exist it returns the Zero value.
Duration(k string) time.Duration

// MustTime tries to get the value with the key k from the store and casts it to a time.Time. If the key k does not exist in the store, MustTime panics.
MustTime(k string) time.Time
// Time tries to get the value with the key k from the store and casts it to a time.Time. If the key k does not exist it returns the Zero value.
Time(k string) time.Time

// MustStringSlice tries to get the value with the key k from the store and casts it to a []string. If the key k does not exist in the store, MustStringSlice panics.
MustStringSlice(k string) []string
// StringSlice tries to get the value with the key k from the store and casts it to a []string. If the key k does not exist it returns the Zero value.
StringSlice(k string) []string

// MustIntSlice tries to get the value with the key k from the store and casts it to a []int. If the key k does not exist in the store, MustIntSlice panics.
MustIntSlice(k string) []int
// IntSlice tries to get the value with the key k from the store and casts it to a []int. If the key k does not exist it returns the Zero value.
IntSlice(k string) []int

// MustStringMap tries to get the value with the key k from the store and casts it to a map[string]interface{}. If the key k does not exist in the store, MustStringMap panics.
MustStringMap(k string) map[string]interface{}
// StringMap tries to get the value with the key k from the store and casts it to a map[string]interface{}. If the key k does not exist it returns the Zero value.
StringMap(k string) map[string]interface{}

// MustStringMapString tries to get the value with the key k from the store and casts it to a map[string]string. If the key k does not exist in the store, MustStringMapString panics.
MustStringMapString(k string) map[string]string
// StringMapString tries to get the value with the key k from the store and casts it to a map[string]string. If the key k does not exist it returns the Zero value.
StringMapString(k string) map[string]string

Strict Keys

You can define required keys on the konfig.Store by calling the Strict method. When calling strict method, konfig will set required keys on the store and during the first Load call on the store it will check if the keys are present, if not, Load will return a non nil error. Then, after every Load on a loader, konfig will check again if the keys are still present, if not, the loader Load will be considered a failure.

Usage:

// We init the root konfig store
konfig.Init(konfig.DefaultConfig()).Strict("debug", "username")

// Register our loaders
...

// We load our config and start watching.
// If strict keys are not found after the load operation, LoadWatch will return a non nil error.
if err := konfig.LoadWatch(); err != nil {
	log.Fatal(err)
}

Alternatively, BindStructStrict can be used to strictly bind config. Usage:

type DBConfig struct {
	Username string
}
type Config struct {
	Addr      string    `konfig:"-"` // this key will be non-strict
	DB        DBConfig  `konfig:"db"`
	RedisHost string    `konfig:"redis.host"`
}

// we init the root konfig store
konfig.Init(konfig.DefaultConfig())

// we bind the Config struct to the konfig.Store
konfig.BindStructStrict(Config{})

// Register our loaders
...

// We load our config and start watching.
// If any strict key is not found after the load operation, LoadWatch will return a non nil error.
if err := konfig.LoadWatch(); err != nil {
	log.Fatal(err)
}

Getter

To easily build services which can use dynamically loaded configs you can create getters for specific keys. A getter implements ngetter.GetterTyped from nui package. It is useful when building apps in larger distributed environments.

Example with a config value set for the debug key:

debug := konfig.Getter("debug")

debug.Bool() // true

Metrics

Konfig comes with prometheus metrics.

Two metrics are exposed:

  • Config reloads counter vector with labels
  • Config reload duration summary vector with labels

Example of metrics:

# HELP konfig_loader_reload Number of config loader reload
# TYPE konfig_loader_reload counter
konfig_loader_reload{loader="config-files",result="failure",store="root"} 0.0
konfig_loader_reload{loader="config-files",result="success",store="root"} 1.0

# HELP konfig_loader_reload_duration Histogram for the config reload duration
# TYPE konfig_loader_reload_duration summary
konfig_loader_reload_duration{loader="config-files",store="root",quantile="0.5"} 0.001227641
konfig_loader_reload_duration{loader="config-files",store="root",quantile="0.9"} 0.001227641
konfig_loader_reload_duration{loader="config-files",store="root",quantile="0.99"} 0.001227641
konfig_loader_reload_duration_sum{loader="config-files",store=""} 0.001227641
konfig_loader_reload_duration_count{loader="config-files",store=""} 1.0

To enable metrics, you must pass a custom config when creating a config store:

konfig.Init(&konfig.Config{
	Metrics: true,
	Name: "root",
})

Benchmark

Benchmarks are run on viper, go-config and konfig. Benchmark are done on reading ops and show that Konfig is 0 allocs on read and at leat 3x faster than Viper:

cd benchmarks && go test -bench . && cd ../
goos: linux
goarch: amd64
pkg: github.com/lalamove/konfig/benchmarks
BenchmarkGetKonfig-4            200000000                7.75 ns/op            0 B/op          0 allocs/op
BenchmarkStringKonfig-4         30000000                49.9 ns/op             0 B/op          0 allocs/op
BenchmarkGetViper-4             20000000               101 ns/op              32 B/op          2 allocs/op
BenchmarkStringViper-4          10000000               152 ns/op              32 B/op          2 allocs/op
BenchmarkGetGoConfig-4          10000000               118 ns/op              40 B/op          3 allocs/op
BenchmarkStringGoConfig-4       10000000               125 ns/op              40 B/op          3 allocs/op
PASS

Contributing

Contributions are welcome. To make contributions, fork the repository, create a branch and submit a Pull Request to the master branch.

Documentation

Overview

Package konfig provides a composable, observable and performant config handling for Go. Written for larger distributed systems where you may have plenty of configuration sources - it allows you to compose configurations from multiple sources with reload hooks making it simple to build apps that live in a highly dynamic environment.

Konfig is built around 4 small interfaces: Loader, Watcher, Parser, Closer

Get started:

var configFiles = []klfile.File{
      {
          Path:   "./config.json",
          Parser: kpjson.Parser,
      },
}

func init() {
	konfig.Init(konfig.DefaultConfig())
}

func main() {
	// load from json file with a file wacher
	konfig.RegisterLoaderWatcher(
		klfile.New(&klfile.Config{
			Files: configFiles,
			Watch: true,
		}),
		// optionally you can pass config hooks to run when a file is changed
		func(c konfig.Store) error {
			return nil
		},
	)

    // Load and start watching
	if err := konfig.LoadWatch(); err != nil {
		log.Fatal(err)
	}

	// retrieve value from config file
	konfig.Bool("debug")
}

Index

Constants

View Source
const (
	// TagKey is the tag key to unmarshal config values to bound value
	TagKey = "konfig"
	// KeySep is the separator for config keys
	KeySep = "."
)

Variables

View Source
var (
	// ErrInvalidConfigFileFormat is the error returned when a problem is encountered when parsing the
	// config file
	ErrInvalidConfigFileFormat = errors.New("Err invalid file format")

	// ErrLoaderNotFound is the error thrown when the loader with the given name cannot be found in the config store
	ErrLoaderNotFound = errors.New("Err loader not found")
	// ErrConfigNotFoundMsg is the error message thrown when a config key is not set
	ErrConfigNotFoundMsg = "Err config '%s' not found"
	// ErrStrictKeyNotFoundMsg is the error returned when a strict key is not found in the konfig store
	ErrStrictKeyNotFoundMsg = "Err strict key '%s' not found"
)
View Source
var (
	// MetricsConfigReload is the label for the prometheus counter for loader reload
	MetricsConfigReload = "konfig_loader_reload"
	// MetricsConfigReloadDuration is the label for the prometheus summary vector for loader reload duration
	MetricsConfigReloadDuration = "konfig_loader_reload_duration"
)
View Source
var (
	// ErrIncorrectValue is the error thrown when trying to bind an invalid type to a config store
	ErrIncorrectValue = errors.New("Bind takes a map[string]interface{} or a struct")
	// ErrIncorrectStructValue is the error thrown when trying to bind a non struct value with the BindStructStrict method
	ErrIncorrectStructValue = errors.New("BindStructStrict takes a struct")
)
View Source
var ErrNoLoaders = errors.New("No loaders in config")

ErrNoLoaders is the error returned when no loaders are set in the config and Load is called

Functions

func Bind

func Bind(v interface{})

Bind binds a value to the root config store

func BindStructStrict

func BindStructStrict(v interface{})

BindStructStrict binds a value to the root config store and adds the exposed keys as strict keys

func Bool

func Bool(k string) bool

Bool gets the config k and converts it to a bool. It returns the zero value if it doesn't find the config.

func Duration

func Duration(k string) time.Duration

Duration gets the config k and converts it to a duration. It returns the zero value if it doesn't find the config.

func Exists

func Exists(k string) bool

Exists checks if a config key k is set in the Store

func Float

func Float(k string) float64

Float gets the config k and tries to convert it to float64 It returns the zero value if it doesn't find the config.

func Get

func Get(k string) interface{}

Get will return the value in config with given key k If not value is found, Get it returns nil

func Getter

func Getter(k string) ngetter.GetterTyped

Getter returns a mgetter.Getter for the key k

func Init

func Init(cfg *Config)

Init initiates the global config store with the given Config cfg

func Int

func Int(k string) int

Int gets the config k and tries to convert it to an int It returns the zero value if it doesn't find the config.

func IntSlice

func IntSlice(k string) []int

IntSlice gets the config k and converts it to a []int. it returns the zero value if it doesn't find the config.

func Load

func Load() error

Load is a function running load on the global config instance

func LoadWatch

func LoadWatch() error

LoadWatch loads the config then starts watching it

func MustBool

func MustBool(k string) bool

MustBool gets the config k and tries to convert it to a bool it panics if it fails.

func MustDuration

func MustDuration(k string) time.Duration

MustDuration gets the config k and tries to convert it to a duration it panics if it fails.

func MustFloat

func MustFloat(k string) float64

MustFloat gets the config k and tries to convert it to a float64 it panics if it fails.

func MustGet

func MustGet(k string) interface{}

MustGet returns the value in config with given key k If not found it panics

func MustInt

func MustInt(k string) int

MustInt gets the config k and tries to convert it to an int it panics if the config does not exist or it fails to convert it to an int.

func MustIntSlice

func MustIntSlice(k string) []int

MustIntSlice gets the config k and tries to convert it to a []int it panics if it fails.

func MustString

func MustString(k string) string

MustString gets the config k and tries to convert it to a string it panics if it fails.

func MustStringMap

func MustStringMap(k string) map[string]interface{}

MustStringMap gets the config k and tries to convert it to a map[string]interface{} it panics if it fails.

func MustStringMapString

func MustStringMapString(k string) map[string]string

MustStringMapString gets the config k and tries to convert it to a map[string]string it panics if it fails.

func MustStringSlice

func MustStringSlice(k string) []string

MustStringSlice gets the config k and tries to convert it to a []string it panics if it fails.

func MustTime

func MustTime(k string) time.Time

MustTime gets the config k and tries to convert it to a time.Time it panics if it fails.

func RunHooks

func RunHooks() error

RunHooks runs all hooks and child groups hooks

func Set

func Set(k string, v interface{})

Set will set the key value to the sync.Map

func SetLogger

func SetLogger(l nlogger.Structured)

SetLogger sets the logger used in the global store

func String

func String(k string) string

String gets the config k and tries to convert it to a string It returns the zero value if it doesn't find the config.

func StringMap

func StringMap(k string) map[string]interface{}

StringMap gets the config k and converts it to a map[string]interface{}. it returns the zero value if it doesn't find the config.

func StringMapString

func StringMapString(k string) map[string]string

StringMapString gets the config k and converts it to a map[string]string. it returns the zero value if it doesn't find the config.

func StringSlice

func StringSlice(k string) []string

StringSlice gets the config k and converts it to a []string. It returns the zero value if it doesn't find the config.

func Time

func Time(k string) time.Time

Time gets the config k and converts it to a time.Time. It returns the zero value if it doesn't find the config.

func Value

func Value() interface{}

Value returns the value bound to the root config store

func Watch

func Watch() error

Watch starts the watchers on loaders

Types

type Closers

type Closers []io.Closer

Closers is a multi closer

func (Closers) Close

func (cs Closers) Close() error

Close closes all closers in the multi closer and returns an error if an error was encountered. Error returned is multierror.Error. https://github.com/hashicorp/go-multierror

type Config

type Config struct {
	// Name is the name of the config store, it is used in metrics as a label. When creating a config group, the name of the group becomes the name of the store
	Name string
	// ExitCode is the code to exit when errors are encountered in loaders
	ExitCode int
	// Disables exiting the program (os.Exit) when errors on loaders
	NoExitOnError bool
	// NoStopOnFailure if false the store closes all registered Watchers and Closers and exit the process unless NoExitOnError is true
	// when a Loader fails to load or a Loader Hook fails. If true, nothing happens when a Loader fails.
	NoStopOnFailure bool
	// Logger is the logger used internally
	Logger nlogger.Provider
	// Metrics sets whether a konfig.Store should record metrics for config loaders
	Metrics bool
	// MaxWatcherPanics is the maximum number of times to restart a watcher when it panics, default is 0.
	MaxWatcherPanics int
}

Config is the config to init a config store

func DefaultConfig

func DefaultConfig() *Config

DefaultConfig returns a default Config

type ConfigLoader

type ConfigLoader struct {
	// contains filtered or unexported fields
}

ConfigLoader is a wrapper of Loader with methods to add hooks

func RegisterLoader

func RegisterLoader(l Loader, loaderHooks ...func(Store) error) *ConfigLoader

RegisterLoader registers a Loader l with a given Watcher w.

func RegisterLoaderWatcher

func RegisterLoaderWatcher(lw LoaderWatcher, loaderHooks ...func(Store) error) *ConfigLoader

RegisterLoaderWatcher registers a WatcherLoader to the config.

func (*ConfigLoader) AddHooks

func (cl *ConfigLoader) AddHooks(loaderHooks ...func(Store) error) *ConfigLoader

AddHooks adds hooks to the loader

type Loader

type Loader interface {
	// StopOnFailure tells whether a loader failure should lead to closing config and the registered closers.
	StopOnFailure() bool
	// Name returns the name of the loader
	Name() string
	// Load loads config values in a Values
	Load(Values) error
	// MaxRetry returns the max number of times to retry when Load fails
	MaxRetry() int
	// RetryDelay returns the delay between each retry
	RetryDelay() time.Duration
}

Loader is the interface a config loader must implement to be used withint the package

type LoaderHooks

type LoaderHooks []func(Store) error

LoaderHooks are functions ran when a config load has been performed

func (LoaderHooks) Run

func (l LoaderHooks) Run(cfg Store) error

Run runs all loader and stops when it encounters an error

type LoaderWatcher

type LoaderWatcher interface {
	Loader
	Watcher
}

LoaderWatcher is an interface that implements both loader and watcher

func NewLoaderWatcher

func NewLoaderWatcher(l Loader, w Watcher) LoaderWatcher

NewLoaderWatcher creates a new LoaderWatcher from a Loader and a Watcher

type NopWatcher

type NopWatcher struct{}

NopWatcher is a nil watcher

func (NopWatcher) Close

func (NopWatcher) Close() error

Close is a noop that returns nil

func (NopWatcher) Done

func (NopWatcher) Done() <-chan struct{}

Done returns an already closed channel

func (NopWatcher) Err

func (NopWatcher) Err() error

Err returns nil, because nothing can go wrong when you do nothing

func (NopWatcher) Start

func (NopWatcher) Start() error

Start implements watcher interface and always returns a nil error

func (NopWatcher) Watch

func (NopWatcher) Watch() <-chan struct{}

Watch implements a basic watch that waits forever

type S

type S struct {
	WatcherLoaders []*loaderWatcher
	WatcherClosers Closers
	Closers        Closers
	// contains filtered or unexported fields
}

S is the concrete implementation of the Store

func New

func New(cfg *Config) *S

New returns a new Store with the given config

func (*S) Bind

func (c *S) Bind(v interface{})

Bind binds a value (either a map[string]interface{} or a struct) to the config store. When config values are set on the config store, they are also set on the bound value.

func (*S) BindStructStrict

func (c *S) BindStructStrict(v interface{})

BindStructStrict binds a value (must a struct) to the config store and adds the exposed fields as strick keys.

func (*S) Bool

func (c *S) Bool(k string) bool

Bool gets the config k and converts it to a bool. It returns the zero value if it doesn't find the config.

func (*S) Duration

func (c *S) Duration(k string) time.Duration

Duration gets the config k and converts it to a duration. It returns the zero value if it doesn't find the config.

func (*S) Exists

func (c *S) Exists(k string) bool

Exists checks if a config key k is set in the Store

func (*S) Float

func (c *S) Float(k string) float64

Float gets the config k and tries to convert it to float64 It returns the zero value if it doesn't find the config.

func (*S) Get

func (c *S) Get(k string) interface{}

Get gets a value from config

func (*S) Getter

func (c *S) Getter(k string) ngetter.GetterTyped

Getter returns a mgetter.Getter for the key k

func (*S) Group

func (c *S) Group(groupName string) Store

Group gets a group of configs

func (*S) Int

func (c *S) Int(k string) int

Int gets the config k and tries to convert it to an int It returns the zero value if it doesn't find the config.

func (*S) IntSlice

func (c *S) IntSlice(k string) []int

IntSlice gets the config k and converts it to a []int. it returns the zero value if it doesn't find the config.

func (*S) Load

func (c *S) Load() error

Load is a function running load on the global config instance

func (*S) LoadWatch

func (c *S) LoadWatch() error

LoadWatch loads the config then starts watching it

func (*S) MustBool

func (c *S) MustBool(k string) bool

MustBool gets the config k and tries to convert it to a bool it panics if it fails.

func (*S) MustDuration

func (c *S) MustDuration(k string) time.Duration

MustDuration gets the config k and tries to convert it to a duration it panics if it fails.

func (*S) MustFloat

func (c *S) MustFloat(k string) float64

MustFloat gets the config k and tries to convert it to a float64 it panics if it fails.

func (*S) MustGet

func (c *S) MustGet(k string) interface{}

MustGet gets a value from config and panics if the value does not exist

func (*S) MustInt

func (c *S) MustInt(k string) int

MustInt gets the config k and tries to convert it to an int it panics if the config does not exist or it fails to convert it to an int.

func (*S) MustIntSlice

func (c *S) MustIntSlice(k string) []int

MustIntSlice gets the config k and tries to convert it to a []int it panics if it fails.

func (*S) MustString

func (c *S) MustString(k string) string

MustString gets the config k and tries to convert it to a string it panics if it fails.

func (*S) MustStringMap

func (c *S) MustStringMap(k string) map[string]interface{}

MustStringMap gets the config k and tries to convert it to a map[string]interface{} it panics if it fails.

func (*S) MustStringMapString

func (c *S) MustStringMapString(k string) map[string]string

MustStringMapString gets the config k and tries to convert it to a map[string]string it panics if it fails.

func (*S) MustStringSlice

func (c *S) MustStringSlice(k string) []string

MustStringSlice gets the config k and tries to convert it to a []string it panics if it fails.

func (*S) MustTime

func (c *S) MustTime(k string) time.Time

MustTime gets the config k and tries to convert it to a time.Time it panics if it fails.

func (*S) Name

func (c *S) Name() string

Name returns the name of the store. The name for the root store is "root".

func (*S) RegisterCloser

func (c *S) RegisterCloser(closer io.Closer) Store

RegisterCloser adds a closer to the list of closers. Closers are closed when an error occurred while reloading a config and the ExitOnError config is set to true

func (*S) RegisterKeyHook

func (c *S) RegisterKeyHook(k string, f func(Store) error) Store

RegisterKeyHook adds a hook to be run on the given key k and all subkeys of k

func (*S) RegisterLoader

func (c *S) RegisterLoader(l Loader, loaderHooks ...func(Store) error) *ConfigLoader

RegisterLoader registers a Loader l with a given Watcher w.

func (*S) RegisterLoaderWatcher

func (c *S) RegisterLoaderWatcher(lw LoaderWatcher, loaderHooks ...func(Store) error) *ConfigLoader

RegisterLoaderWatcher registers a WatcherLoader to the config.

func (*S) RunHooks

func (c *S) RunHooks() error

RunHooks runs all hooks and child groups hooks

func (*S) Set

func (c *S) Set(k string, v interface{})

Set sets a value in config

func (*S) SetLogger

func (c *S) SetLogger(l nlogger.Structured)

SetLogger sets the logger used in the store

func (*S) Strict

func (c *S) Strict(keys ...string) Store

Strict specifies mandatory keys on the konfig. After strict is called, konfig will wait for the first config Load to happen and will check if the specified strict keys are present, if not, Load will return a non nil error. Then, after every following `Load` of a loader, it will check if the strict keys are still present in the konfig and consider the load a failure if a key is not present anymore.

func (*S) String

func (c *S) String(k string) string

String gets the config k and tries to convert it to a string It returns the zero value if it doesn't find the config.

func (*S) StringMap

func (c *S) StringMap(k string) map[string]interface{}

StringMap gets the config k and converts it to a map[string]interface{}. it returns the zero value if it doesn't find the config.

func (*S) StringMapString

func (c *S) StringMapString(k string) map[string]string

StringMapString gets the config k and converts it to a map[string]string. it returns the zero value if it doesn't find the config.

func (*S) StringSlice

func (c *S) StringSlice(k string) []string

StringSlice gets the config k and converts it to a []string. It returns the zero value if it doesn't find the config.

func (*S) Time

func (c *S) Time(k string) time.Time

Time gets the config k and converts it to a time.Time. It returns the zero value if it doesn't find the config.

func (*S) Value

func (c *S) Value() interface{}

Value returns the value bound to the config store

func (*S) Watch

func (c *S) Watch() error

Watch starts the watchers on loaders

type Store

type Store interface {
	// Name returns the name of the store
	Name() string
	// SetLogger sets the logger within the store
	// it will propagate to all children groups
	SetLogger(l nlogger.Structured)
	// RegisterLoader registers a Loader in the store and adds the given loader hooks.
	RegisterLoader(l Loader, loaderHooks ...func(Store) error) *ConfigLoader
	// RegisterLoaderWatcher reigsters a LoaderWatcher in the store and adds the given loader hooks.
	RegisterLoaderWatcher(lw LoaderWatcher, loaderHooks ...func(Store) error) *ConfigLoader
	// RegisterCloser registers an io.Closer in the store. A closer closes when konfig fails to load configs.
	RegisterCloser(closer io.Closer) Store
	// RegisterKeyHook adds a hook to be run when the key changes.
	// If a key has the given key as path prefix, it runs the hook as well.
	RegisterKeyHook(k string, h func(Store) error) Store

	// Strict specifies mandatory keys on the konfig. When Strict is called, konfig will check that the specified keys are present, else it will return a non nil error.
	// Then, after every following `Load` of a loader, it will check if the strict keys are still present in the konfig and consider the load a failure if a key is not present anymore.
	Strict(...string) Store
	// RunHooks runs all hooks and child groups hooks
	RunHooks() error

	// Load loads all loaders registered in the store. If it faisl it returns a non nil error
	Load() error
	// Watch starts all watchers registered in the store. If it fails it returns a non nil error.
	Watch() error

	// LoadWatch loads all loaders registered in the store, then starts watching all
	// watchers. If loading or starting watchers fails, loadwatch stops and returns a non nil error.
	LoadWatch() error

	// Group lazyloads a child Store from the current store. If the group already exists, it just returns it, else it creates it and returns it. Groups are useful to namespace configs by domain.
	Group(g string) Store

	// Getter returns a GetterTyped for the given key k
	Getter(k string) ngetter.GetterTyped

	// Get gets the value with the key k fron the store. If the key is not set, Get returns nil. To check whether a value is really set, use Exists.
	Get(k string) interface{}
	// MustGet tries to get the value with the key k from the store. If the key k does not exist in the store, MustGet panics.
	MustGet(k string) interface{}
	// Set sets the key k with the value v in the store.
	Set(k string, v interface{})
	// Exists checks whether the key k is set in the store.
	Exists(k string) bool
	// MustString tries to get the value with the key k from the store and casts it to a string. If the key k does not exist in the store, MustGet panics.
	MustString(k string) string

	// String tries to get the value with the key k from the store and casts it to a string. If the key k does not exist it returns the Zero value.
	String(k string) string

	// MustInt tries to get the value with the key k from the store and casts it to a int. If the key k does not exist in the store, MustInt panics.
	MustInt(k string) int

	// Int tries to get the value with the key k from the store and casts it to a int. If the key k does not exist it returns the Zero value.
	Int(k string) int

	// MustFloat tries to get the value with the key k from the store and casts it to a float. If the key k does not exist in the store, MustFloat panics.
	MustFloat(k string) float64
	// Float tries to get the value with the key k from the store and casts it to a float. If the key k does not exist it returns the Zero value.
	Float(k string) float64

	// MustBool tries to get the value with the key k from the store and casts it to a bool. If the key k does not exist in the store, MustBool panics.
	MustBool(k string) bool
	// Bool tries to get the value with the key k from the store and casts it to a bool. If the key k does not exist it returns the Zero value.
	Bool(k string) bool

	// MustDuration tries to get the value with the key k from the store and casts it to a time.Duration. If the key k does not exist in the store, MustDuration panics.
	MustDuration(k string) time.Duration
	// Duration tries to get the value with the key k from the store and casts it to a time.Duration. If the key k does not exist it returns the Zero value.
	Duration(k string) time.Duration

	// MustTime tries to get the value with the key k from the store and casts it to a time.Time. If the key k does not exist in the store, MustTime panics.
	MustTime(k string) time.Time
	// Time tries to get the value with the key k from the store and casts it to a time.Time. If the key k does not exist it returns the Zero value.
	Time(k string) time.Time

	// MustStringSlice tries to get the value with the key k from the store and casts it to a []string. If the key k does not exist in the store, MustStringSlice panics.
	MustStringSlice(k string) []string
	// StringSlice tries to get the value with the key k from the store and casts it to a []string. If the key k does not exist it returns the Zero value.
	StringSlice(k string) []string

	// MustIntSlice tries to get the value with the key k from the store and casts it to a []int. If the key k does not exist in the store, MustIntSlice panics.
	MustIntSlice(k string) []int
	// IntSlice tries to get the value with the key k from the store and casts it to a []int. If the key k does not exist it returns the Zero value.
	IntSlice(k string) []int

	// MustStringMap tries to get the value with the key k from the store and casts it to a map[string]interface{}. If the key k does not exist in the store, MustStringMap panics.
	MustStringMap(k string) map[string]interface{}
	// StringMap tries to get the value with the key k from the store and casts it to a map[string]interface{}. If the key k does not exist it returns the Zero value.
	StringMap(k string) map[string]interface{}

	// MustStringMapString tries to get the value with the key k from the store and casts it to a map[string]string. If the key k does not exist in the store, MustStringMapString panics.
	MustStringMapString(k string) map[string]string
	// StringMapString tries to get the value with the key k from the store and casts it to a map[string]string. If the key k does not exist it returns the Zero value.
	StringMapString(k string) map[string]string

	// Bind binds a value (either a map[string]interface{} or a struct) to the config store. When config values are set on the config store, they are also set on the bound value.
	Bind(interface{})

	// BindStructStrict strictly binds a struct to the config store and add's the struct's exposed fields as strict keys.
	BindStructStrict(interface{})

	// Value returns the value bound to the config store.
	// It panics if no bound value has been set
	Value() interface{}
}

Store is the interface

func Group

func Group(groupName string) Store

Group gets a group of configs

func Instance

func Instance() Store

Instance returns the singleton global config store

func RegisterCloser

func RegisterCloser(closer io.Closer) Store

RegisterCloser adds a closer to the list of closers. Closers are closed when an error occurred while reloading a config and the ExitOnError config is set to true

func RegisterKeyHook

func RegisterKeyHook(k string, f func(Store) error) Store

RegisterKeyHook adds a hook to be run on the given key k and all subkeys of k

func Strict

func Strict(keys ...string) Store

Strict specifies mandatory keys on the konfig. After strict is called, konfig will wait for the first config Load to happen and will check if the specified strict keys are present, if not, Load will return a non nil error. Then, after every following `Load` of a loader, it will check if the strict keys are still present in the konfig and consider the load a failure if a key is not present anymore.

type Values

type Values map[string]interface{}

Values is the values attached to a loader

func (Values) Set

func (x Values) Set(k string, v interface{})

Set adds a key value to the Values

type Watcher

type Watcher interface {
	// Start starts the watcher, it must no be blocking.
	Start() error
	// Done indicate whether the watcher is done or not
	Done() <-chan struct{}
	// Watch should block until an event unlocks it
	Watch() <-chan struct{}
	// Close closes the watcher, it returns a non nil error if it is already closed
	// or something prevents it from closing properly.
	Close() error
	// Err returns the error attached to the watcher
	Err() error
}

Watcher is the interface implementing a config watcher. Config watcher trigger loaders. A file watcher or a simple Timer can be valid watchers.

Directories

Path Synopsis
loader
Package mocks is a generated GoMock package.
Package mocks is a generated GoMock package.
kpkeyval
Package kpkeyval provides a key value parser to parse an io.Reader's content of key/values with a configurable separator and add it into a konfig.Store.
Package kpkeyval provides a key value parser to parse an io.Reader's content of key/values with a configurable separator and add it into a konfig.Store.
watcher

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL