distconf

package module
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Apr 8, 2023 License: Apache-2.0 Imports: 13 Imported by: 0

README

distconf

Framework for configuration variables and an abbreviation of distributed configuration.

Consider using this framework if you:

  • Want to configure your application from multiple sources
  • Want access to configuration to be quick (atomic reads)
  • Want to know when configuration changes, without restarting your application

The library comes with basic configuration Readers, but most advanced environments will want to read from something like Redis, Consul, or Zookeeper as well.

Core Concepts

Distconf is made up of an array of Readers that are in priority of configuration preference. When users want to get a configuration value, the readers are searched for the value in order and the first one is returned. The returned value has a Get() function that atomically returns the previous value and will return a new configuration value whenever one changes. It is ok to call Get() inside hot path code. Even if the original config value was fetched from a remote service, like Redis, the call to Get() will be an atomic memory access each time.

Example use case

The example I currently use is to have an array of Readers{ Environment, config file, consul (defaults), consul (production) } This allows me to override configuration from the env or a config file during development, use consul for dynamic configuration, have a default consul location for configuration shared across deployments, and have a specific consul location for production, canary, or internal development configuration.

Example for a user

As a user of distconf, a distconf.Distconf object will already be created and passed to you. You can get string, int64, time.Duration, boolean, or float objects from this Distconf. When you ask for them, you specify a default value that is used if for some reason all backings are down. You should ask for this value once, then get the configuration value from this distconf holder in your app. Generally, you create a config object that is Loaded in main and used in your program.

    // app.go
    type Config struct {
        QueueSize *distconf.Int
    }

    func (c *Config) Load(dconf *distconf.Distconf) {
        c.QueueSize = dconf.Int("app.queue_size", 100)
    }
  
    type App struct {
        Config Config
    }
    
    func (a *App) Start() {
        for {
            x := sqs.Load(a.Config.QueueSize.Get())
            // ...
        }
    }

    // main.go
    func main() {
        dconf := loadDistConf()
        c := app.Config{}
        c.Load(dconf)
        q := app.App {
            Config: c,
        }
        q.Start()
    }

Example creating a distconf

    jconf := JSONConfig {}
    jconf.RefreshFile("config.json")
    dconf := Distconf {
        Readers: []Reader {
            &Env{}, &jconf,
        },
    }
    // The variable should only be fetched a single time.  Then, store qsize somewhere and call Get() to fetch the
    // current value of queue.size
    qsize := dconf.Int("queue.size", 10)
    for {
        fmt.Println(qsize.Get())
    }

Readers

The included readers only depend upon the core libraries. If you want to implement your own Reader, it should satisfy the following interface.

    // Reader can get a []byte value for a config key
    type Reader interface {
        // Get should return the given config value.  If the value does not exist, it should return nil, nil.
        Get(key string) ([]byte, error)
        Close()
    }

If your reader is dynamic (the values contained can change while the application is running), then it should also implement the Dynamic interface.

    // A Dynamic config can change what it thinks a value is over time.
    type Dynamic interface {
    	// Watch should execute callback function whenever the key changes.  The parameter to callback should be the
    	// key's name.
    	Watch(key string, callback func(string)) error
}

Debugging

You can expose an expvar of all the configuration keys and values with Var() and just the keys with Keys().

LICENSE

Apache 2.0

Originally forked from https://github.com/signalfx/golib/tree/master/distconf at 3b7d7c75a7219b2f7b8ca5dd119251254f6a2a06

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ReaderCacheNotify

func ReaderCacheNotify(s *ReaderCache, key string, newVal []byte)

ReaderCacheNotify notifies a readercache that a value changed. This function isn't public on the ReaderCache so structs can embed a ReaderCache directly without exposing notifyWatchers.

func ReaderCacheRefresh

func ReaderCacheRefresh(s *ReaderCache, r Reader, OnFetchErr func(err error, key string))

ReaderCacheRefresh calls a Get on every value watched by the ReaderCache

Types

type Bool

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

Bool is a Boolean type config inside a Config. It uses strconv.ParseBool to parse the conf contents as either true for false

func (*Bool) Get

func (s *Bool) Get() bool

Get the boolean in this config variable

func (*Bool) MarshalJSON

func (s *Bool) MarshalJSON() ([]byte, error)

MarshalJSON converts the boolean into the JSON encoding of the boolean

func (*Bool) Watch

func (w *Bool) Watch(watch func())

Watch adds a watch for changes to this structure

type CachedReader

type CachedReader struct {
	Fallback DynamicReader
	// contains filtered or unexported fields
}

CachedReader allows bulk loading values and caching the result

func (*CachedReader) Close

func (m *CachedReader) Close()

Close ends the fallback endpoint

func (*CachedReader) Get

func (m *CachedReader) Get(key string) ([]byte, error)

Get will try fetching it from source. If you can, update in memory. If you can't, try to find it in memory and return that instead.

func (*CachedReader) ListConfig

func (m *CachedReader) ListConfig() map[string][]byte

ListConfig returns all the cached values

func (*CachedReader) ReadFrom

func (m *CachedReader) ReadFrom(r io.Reader) (n int64, err error)

ReadFrom loads a stored cache

func (*CachedReader) StoreConfig

func (m *CachedReader) StoreConfig(toStore map[string][]byte)

StoreConfig overwrites the stored config

func (*CachedReader) Watch

func (m *CachedReader) Watch(key string, callback func(string)) error

Watch forwards to the fallback

func (*CachedReader) WriteTo

func (m *CachedReader) WriteTo(w io.Writer) (int64, error)

WriteTo updates the cache

type ComboRefresher

type ComboRefresher []Refreshable

ComboRefresher can refresh from multiple sources

func (ComboRefresher) Refresh

func (c ComboRefresher) Refresh()

Refresh calls all refreshes at once

type CommandLine

type CommandLine struct {
	Prefix string
	Source []string
}

CommandLine gets distconf values from the command line

func (*CommandLine) Close

func (p *CommandLine) Close()

Close does nothing

func (*CommandLine) Get

func (p *CommandLine) Get(key string) ([]byte, error)

Get looks for "prefix+key=xyz"

type Distconf

type Distconf struct {
	Logger  Logger
	Readers []Reader
	// contains filtered or unexported fields
}

Distconf gets configuration data from the first backing that has it

func (*Distconf) Bool

func (c *Distconf) Bool(key string, defaultVal bool) *Bool

Bool object that can be referenced to get boolean values from a backing config

func (*Distconf) Close

func (c *Distconf) Close()

Close this config framework's readers. Config variable results are undefined after this call.

func (*Distconf) Duration

func (c *Distconf) Duration(key string, defaultVal time.Duration) *Duration

Duration returns a duration object that calls ParseDuration() on the given key

func (*Distconf) Float

func (c *Distconf) Float(key string, defaultVal float64) *Float

Float object that can be referenced to get float values from a backing config

func (*Distconf) Int

func (c *Distconf) Int(key string, defaultVal int64) *Int

Int object that can be referenced to get integer values from a backing config

func (*Distconf) Keys

func (c *Distconf) Keys() []string

Keys returns an array of all keys watched by distconf

func (*Distconf) Str

func (c *Distconf) Str(key string, defaultVal string) *Str

Str object that can be referenced to get string values from a backing config

func (*Distconf) Struct

func (c *Distconf) Struct(key string, defaultVal interface{}) *Struct

Struct object that can be referenced to decode a json representation of an instance of the type that is provided in the defaultVal. The DefaultValue provided sets the type that the backend josn will be unmarshelled into.

func (*Distconf) Var

func (c *Distconf) Var() expvar.Var

Var returns an expvar variable that shows all the current configuration variables and their current value

type Duration

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

Duration is a duration type config inside a Config.

func (*Duration) Get

func (s *Duration) Get() time.Duration

Get the string in this config variable

func (*Duration) MarshalJSON

func (s *Duration) MarshalJSON() ([]byte, error)

MarshalJSON converts the duration into the JSON encoding of the duration

func (*Duration) Watch

func (w *Duration) Watch(watch func())

Watch adds a watch for changes to this structure

type Dynamic

type Dynamic interface {
	// Watch should execute callback function whenever the key changes.  The parameter to callback should be the
	// key's name.
	Watch(key string, callback func(string)) error
}

A Dynamic config can change what it thinks a value is over time.

type DynamicReader

type DynamicReader interface {
	Reader
	Dynamic
}

DynamicReader is a reader that can also change

type Env

type Env struct {
	// Prefix is a forced prefix in front of config variables
	Prefix string
	// OsGetenv is a stub for os.Getenv()
	OsGetenv func(string) string
}

Env allows fetching configuration from the env

func (*Env) Close

func (p *Env) Close()

Close does nothing and exists just to satisfy an interface

func (*Env) Get

func (p *Env) Get(key string) ([]byte, error)

Get prefix + key from env if it exists.

type Float

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

Float is an float type config inside a Config.

func (*Float) Get

func (c *Float) Get() float64

Get the float in this config variable

func (*Float) MarshalJSON

func (c *Float) MarshalJSON() ([]byte, error)

MarshalJSON converts the float into the JSON encoding of the float

func (*Float) Watch

func (w *Float) Watch(watch func())

Watch adds a watch for changes to this structure

type InMemory

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

InMemory stores configuration in memory

func (*InMemory) Close

func (m *InMemory) Close()

Close does nothing and exists to satisfy an interface

func (*InMemory) Get

func (m *InMemory) Get(key string) ([]byte, error)

Get returns the stored value

func (*InMemory) ListConfig

func (m *InMemory) ListConfig() map[string][]byte

ListConfig returns a copy of the currently stored config values

func (*InMemory) StoreConfig

func (m *InMemory) StoreConfig(toStore map[string][]byte)

StoreConfig stores into memory the given values, only if there currently is not a copy

func (*InMemory) Watch

func (m *InMemory) Watch(key string, callback func(string)) error

Watch calls callback when a write happens on the key

func (*InMemory) Write

func (m *InMemory) Write(key string, value []byte) error

Write updates the in memory value

type Int

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

Int is an integer type config inside a Config.

func (*Int) Get

func (c *Int) Get() int64

Get the integer in this config variable

func (*Int) MarshalJSON

func (c *Int) MarshalJSON() ([]byte, error)

MarshalJSON converts the integer into the JSON encoding of the integer

func (*Int) Watch

func (w *Int) Watch(watch func())

Watch adds a watch for changes to this structure

type JSONConfig

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

JSONConfig reads configuration from a JSON stream

func (*JSONConfig) Close

func (j *JSONConfig) Close()

Close does nothing and exists to satisfy an interface

func (*JSONConfig) Get

func (j *JSONConfig) Get(key string) ([]byte, error)

Get returns the key's value as read by JSON

func (*JSONConfig) Refresh

func (j *JSONConfig) Refresh(input io.Reader) error

Refresh loads the configuration from a Reader

func (*JSONConfig) RefreshFile

func (j *JSONConfig) RefreshFile(filename string) error

RefreshFile reloads the configuration from a file

func (*JSONConfig) Watch

func (j *JSONConfig) Watch(key string, callback func(string)) error

Watch updates callback when a value changes inside the file

type Logger

type Logger func(key string, err error, msg string)

Logger is used to log debug information during distconf operation

type Reader

type Reader interface {
	// Get should return the given config value.  If the value does not exist, it should return nil, nil.
	Get(key string) ([]byte, error)
	Close()
}

Reader can get a []byte value for a config key

type ReaderCache

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

ReaderCache is a type of distconf reader that simply caches previous Get() values and updates watchers when a value changes

func (*ReaderCache) Watch

func (s *ReaderCache) Watch(key string, callback func(string)) error

Watch registers a distconf watcher

func (*ReaderCache) WatchedKeys

func (s *ReaderCache) WatchedKeys() []string

WatchedKeys copies the internal watched keys map's keys

type ReaderWriter

type ReaderWriter interface {
	Reader
	Writer
}

A ReaderWriter can both read and write configuration information

type Refreshable

type Refreshable interface {
	Refresh()
}

Refreshable is any configuration that needs to be periodically refreshed

type Refresher

type Refresher struct {
	WaitTime  *Duration
	ToRefresh Refreshable
	// contains filtered or unexported fields
}

A Refresher can refresh the values inside any Refreshable object

func (*Refresher) Close

func (r *Refresher) Close() error

Close ends the Refresher

func (*Refresher) Done

func (r *Refresher) Done() <-chan struct{}

Done returns a channel that blocks until Close() is called or Start() is finished

func (*Refresher) Setup

func (r *Refresher) Setup() error

Setup inits the refresher

func (*Refresher) Start

func (r *Refresher) Start() error

Start executing the refresher, waiting waitTime between ending, and a new start of the Refresh call

type Str

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

Str is a string type config inside a Config.

func (*Str) Get

func (s *Str) Get() string

Get the string in this config variable

func (*Str) MarshalJSON

func (s *Str) MarshalJSON() ([]byte, error)

MarshalJSON converts the string into the JSON encoding of the string

func (*Str) Watch

func (w *Str) Watch(watch func())

Watch adds a watch for changes to this structure

type Struct

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

Struct allows distconf to atomically update sets of variables. The structure provided must be represented as a json object in the backend. The DefaultValue provided sets the type that the backend josn will be unmarshelled into.

func (*Struct) Get

func (s *Struct) Get() interface{}

Get the current value. For structs this *must* be cast to the same type as the DefaultValue provided. Not matching the type will result in a runtime panic.

func (*Struct) MarshalJSON

func (s *Struct) MarshalJSON() ([]byte, error)

MarshalJSON converts the struct into the JSON encoding of the struct

func (*Struct) Watch

func (w *Struct) Watch(watch func())

Watch adds a watch for changes to this structure

type Writer

type Writer interface {
	Write(key string, value []byte) error
}

Writer can modify Config properties

Jump to

Keyboard shortcuts

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