config

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: May 8, 2022 License: Apache-2.0 Imports: 10 Imported by: 0

README

Config GoDoc

Go Config which is extracted from go-micro is a pluggable dynamic config package.

Most config in applications are statically configured or include complex logic to load from multiple sources. Go Config makes this easy, pluggable and mergeable. You'll never have to deal with config in the same way again.

Features

  • Dynamic Loading - Load configuration from multiple source as and when needed. Go Config manages watching config sources in the background and automatically merges and updates an in memory view.

  • Pluggable Sources - Choose from any number of sources to load and merge config. The backend source is abstracted away into a standard format consumed internally and decoded via encoders. Sources can be env vars, flags, file, etcd, k8s configmap, etc.

  • Mergeable Config - If you specify multiple sources of config, regardless of format, they will be merged and presented in a single view. This massively simplifies priority order loading and changes based on environment.

  • Observe Changes - Optionally watch the config for changes to specific values. Hot reload your app using Go Config's watcher. You don't have to handle ad-hoc hup reloading or whatever else, just keep reading the config and watch for changes if you need to be notified.

  • Sane Defaults - In case config loads badly or is completely wiped away for some unknown reason, you can specify fallback values when accessing any config values directly. This ensures you'll always be reading some sane default in the event of a problem.

Getting Started

Go Config has the benefit of supporting multiple backend sources and config encoding formats out of the box.

Here’s the top level interface which encapsulates all the features mentioned.

// Config is an interface abstraction for dynamic configuration
type Config interface {
	// provide the reader.Values interface
	reader.Values
	// Init the config
	Init(opts ...Option) error
	// Options in the config
	Options() Options
	// Stop the config loader/watcher
	Close() error
	// Load config sources
	Load(source ...source.Source) error
	// Force a source changeset sync
	Sync() error
	// Watch a value for changes
	Watch(path ...string) (Watcher, error)
}

Ok so let’s break it down and discuss the various concerns in the framework starting with the backend sources.

Source

A source is a backend from which config is loaded. This could be command line flags, environment variables, a key-value store or any other number of places.

Go Config provides a simple abstraction over all these sources as a simple interface from which we read data or what we call a ChangeSet.

// Source is the source from which config is loaded
type Source interface {
	Read() (*ChangeSet, error)
	Write(*ChangeSet) error
	Watch() (Watcher, error)
	String() string
}

// ChangeSet represents a set of changes from a source
type ChangeSet struct {
	Data      []byte
	Checksum  string
	Format    string
	Source    string
	Timestamp time.Time
}

The ChangeSet includes the raw data, it’s format, timestamp of creation or last update and the source from which it was loaded. There’s also an optional md5 checksum which can be recalculated using the Sum() method.

The simplicity of this interface allows us to easily create a source for any backend, read it’s values at any given time or watch for changes where possible.

Encoding

Config is rarely available in just a single format and people usually have varying preferences on whether it should be stored in json, yaml, toml or something else. We make sure to deal with this in the framework so almost any encoding format can be dealt with.

The encoder is a very simply interface for handling encoding and decoding different formats. Why wouldn’t we reuse existing libraries for this? We do beneath the covers but to ensure we could deal with encoding in an abstract way it made sense to define an interface for it.

// Encoder handles encoding and decoding of a variety of config formats
type Encoder interface {
	Encode(interface{}) ([]byte, error)
	Decode([]byte, interface{}) error
	String() string
}

The current supported formats are json, yaml, toml and xml.

Reader

Once we’ve loaded backend sources and developed a way to decode the variety of config formats we need some way of actually internally representing and reading it. For this we’ve created a reader.

The reader manages decoding and merging multiple changesets into a single source of truth. It then provides a value interface which allows you to retrieve native Go types or scan the config into a type of your choosing.

// Reader manages merging multiple changesets into a single source of truth
type Reader interface {
    Merge(...*source.ChangeSet) (*source.ChangeSet, error)
    Values(*source.ChangeSet) (Values, error)
    String() string
}

// Values is returned by the reader
type Values interface {
	Bytes() []byte
	Get(path ...string) Value
	Set(val interface{}, path ...string)
	Del(path ...string)
	Map() map[string]interface{}
	Scan(v interface{}) error
}

// Value represents a value of any type
type Value interface {
	Bool(def bool) bool
	Int(def int) int
	String(def string) string
	Float64(def float64) float64
	Duration(def time.Duration) time.Duration
	StringSlice(def []string) []string
	StringMap(def map[string]string) map[string]string
	Scan(val interface{}) error
	Bytes() []byte
}

Our default internal representation for the merged source is json.

Example

Let’s look at how Go Config actually works in code. Starting with a simple example, let’s read config from a file.

Read Config

Step 1. Define a config.json file

{
    "hosts": {
        "database": {
            "address": "10.0.0.1",
            "port": 3306
        },
        "cache": {
            "address": "10.0.0.2",
            "port": 6379
        }
    }
}

Step 2. Load the file into config

import "github.com/jiyeyuran/go-config"

config.Load(file.NewSource(
	file.WithPath("config.json"),
))

Step 3. Read the values from config

type Host struct {
	Address string `json:"address"`
	Port int `json:"port"`
}

var host Host

config.Get("hosts", "database").Scan(&host)

And that’s it! It’s really that simple.

Watch Config

If the config file changes, the next time you read the value it will be different. But what if you want to track that change? You can watch for changes. Let’s test it out.

w, err := config.Watch("hosts", "database")
if err != nil {
	// do something
}

// wait for next value
v, err := w.Next()
if err != nil {
	// do something
}

var host Host

v.Scan(&host)

In this example rather than getting a value, we watch it. The next time the value changes we’ll receive it and can update our Host struct.

Merge Config

Another useful feature is the ability to load config from multiple sources which are ordered, merged and overridden. A good example of this would be loading config from a file but overriding via environment variables or flags.

config.Load(
	// base config
	file.NewSource(),
	// override file with env vars
	env.NewSource(),
	// override env vars with flags
	flag.NewSource(),
)
Fallback Values
// Get address. Set default to localhost as fallback
address := config.Get("hosts", "database", "address").String("localhost")

// Get port. Set default to 3000 as fallback
port := config.Get("hosts", "database", "port").Int(3000)
Summary

The way in which config is managed and consumed needs to evolve. Go Config looks to do this by drastically simplifying use of dynamic configuration with a pluggable framework.

Go Config currently supports a number of configuration formats and backend sources but we’re always looking for more contributions. If you’re interested in contribution please feel free to do so by with a pull request.

Let Go Config managed the complexity of configuration for you so you can focus on what’s really important. Your code.

Documentation

Overview

Package config is an interface for dynamic configuration.

Index

Constants

This section is empty.

Variables

View Source
var (
	// Default Config Manager
	DefaultConfig, _ = NewConfig()
)

Functions

func Bytes

func Bytes() []byte

Return config as raw json

func Get

func Get(path ...string) reader.Value

Get a value from the config

func Load

func Load(source ...source.Source) error

Load config sources

func LoadFile

func LoadFile(path string) error

LoadFile is short hand for creating a file source and loading it

func Map

func Map() map[string]interface{}

Return config as a map

func Scan

func Scan(v interface{}) error

Scan values to a go type

func Sync

func Sync() error

Force a source changeset sync

Types

type Config

type Config interface {
	// provide the reader.Values interface
	reader.Values
	// Init the config
	Init(opts ...Option) error
	// Options in the config
	Options() Options
	// Stop the config loader/watcher
	Close() error
	// Load config sources
	Load(source ...source.Source) error
	// Force a source changeset sync
	Sync() error
	// Watch a value for changes
	Watch(path ...string) (Watcher, error)
}

Config is an interface abstraction for dynamic configuration

func NewConfig

func NewConfig(opts ...Option) (Config, error)

NewConfig returns new config

type Option

type Option func(o *Options)

func WithLoader

func WithLoader(l loader.Loader) Option

WithLoader sets the loader for manager config

func WithReader

func WithReader(r reader.Reader) Option

WithReader sets the config reader

func WithSource

func WithSource(s source.Source) Option

WithSource appends a source to list of sources

func WithWatcherDisabled

func WithWatcherDisabled() Option

type Options

type Options struct {
	Loader loader.Loader
	Reader reader.Reader
	Source []source.Source

	// for alternative data
	Context context.Context

	WithWatcherDisabled bool
}

type Watcher

type Watcher interface {
	Next() (reader.Value, error)
	Stop() error
}

Watcher is the config watcher

func Watch

func Watch(path ...string) (Watcher, error)

Watch a value for changes

Directories

Path Synopsis
Package encoder handles source encoding formats
Package encoder handles source encoding formats
xml
package loader manages loading from multiple sources
package loader manages loading from multiple sources
Package reader parses change sets and provides config values
Package reader parses change sets and provides config values
Package secrets is an interface for encrypting and decrypting secrets
Package secrets is an interface for encrypting and decrypting secrets
box
Package box is an asymmetric implementation of config/secrets using nacl/box
Package box is an asymmetric implementation of config/secrets using nacl/box
secretbox
Package secretbox is a config/secrets implementation that uses nacl/secretbox to do symmetric encryption / verification
Package secretbox is a config/secrets implementation that uses nacl/secretbox to do symmetric encryption / verification
Package source is the interface for sources
Package source is the interface for sources
cli
env
file
Package file is a file source.
Package file is a file source.
memory
Package memory is a memory source
Package memory is a memory source

Jump to

Keyboard shortcuts

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