onion

package module
v0.1.4 Latest Latest
Warning

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

Go to latest
Published: Aug 17, 2020 License: MIT Imports: 13 Imported by: 0

README

onion

Build Status Coverage Status GoDoc Go Report Card

import "github.com/goraz/onion"

Package onion is a layer based, pluggable config manager for golang.

The current version in develop branch is work in progress (see the milestone), for older versions check the v2 and v3 branches and use the gopkg.in/goraz/onion.v1 and gopkg.in/goraz/onion.v2 For the next release we use the go module and tagging using semantic version.

Shrek: For your information, there's a lot more to ogres than people think.
Donkey: Example?
Shrek: Example... uh... ogres are like onions! 
[holds up an onion, which Donkey sniffs] 
Donkey: They stink? 
Shrek: Yes... No! 
Donkey: Oh, they make you cry? 
Shrek: No! 
Donkey: Oh, you leave 'em out in the sun, they get all brown, start sproutin' little white hairs...
Shrek: [peels an onion] NO! Layers. Onions have layers. Ogres have layers... You get it? We both have layers.
[walks off]
Donkey: Oh, you both have LAYERS. Oh. You know, not everybody like onions. CAKE! Everybody loves cake! Cakes have layers!
Shrek: I don't care what everyone likes! Ogres are not like cakes.
Donkey: You know what ELSE everybody likes? Parfaits! Have you ever met a person, you say, "Let's get some parfait," they say, "Hell no, I don't like no parfait."? Parfaits are delicious!
Shrek: NO! You dense, irritating, miniature beast of burden! Ogres are like onions! End of story! Bye-bye! See ya later.
Donkey: Parfait's gotta be the most delicious thing on the whole damn planet! 

Goals

The main goal is to have minimal dependency based on usage. if you need normal config files in the file system, there should be no dependency to etcd or consul, if you have only yaml files, including toml or any other format is just not right.

Usage

Choose the layer first. normal file layer and json are built-in but for any other type you need to import the package for that layer.

Example json file layer
package main

import (
	"fmt"

	"github.com/goraz/onion"
)

func main() {
	// Create a file layer to load data from json file. onion loads the file based on the extension.
	// so the json file should have `.json` ext.
	l1, err := onion.NewFileLayer("/etc/shared.json", nil)
	if err != nil {
		panic(err)
	}

	// Create a layer based on the environment. it loads every environment with APP_ prefix
	// for example APP_TEST_STRING is available as o.Get("test.string")
	l2 := onion.NewEnvLayerPrefix("_", "APP")

	// Create the onion, the final result is union of l1 and l2 but l2 overwrite l1.
	o := onion.New(l1, l2)
	str := o.GetStringDefault("test.string", "empty")
	fmt.Println(str)
	// Now str is the string in this order
	// 1- if the APP_TEST_STRING is available in the env
	// 2- if the shared.json had key like this { "test" : { "string" : "value" }} then the str is "value"
	// 3- the provided default, "empty"
}
Loading other file format

Currently onion support json format out-of-the-box, while you need to blank import the loader package of others formats to use them:

  • toml (for 0.4.0 version)
  • toml-0.5.0 (for 0.5.0 version)
  • yaml
  • properties

For example:

import (
    _ "github.com/goraz/onion/loaders/toml" // Needed to load TOML format
)
Watch file and etcd

Also there is other layers, (like etcd and filewatchlayer) that watches for change.

package main

import (
	"fmt"

	"github.com/goraz/onion"
	"github.com/goraz/onion/layers/etcdlayer"
	"github.com/goraz/onion/layers/filewatchlayer"
)

func main() {
	// Create a file layer to load data from json file. also it watches for change in the file
	l1, err := filewatchlayer.NewFileWatchLayer("/etc/shared.json", nil)
	if err != nil {
		panic(err)
	}

	l2, err := etcdlayer.NewEtcdLayer("/app/config", "json", []string{"http://127.0.0.1:2379"}, nil)
	if err != nil {
		panic(err)
	}

	// Create the onion, the final result is union of l1 and l2 but l2 overwrite l1.
	o := onion.New(l1, l2)
	// Get the latest version of the key 
	str := o.GetStringDefault("test.string", "empty")
	fmt.Println(str)
}
Encrypted config

Also if you want to store data in encrypted content. currently only secconf (based on the crypt project) is supported. also the onioncli helps you to manage this keys.

package main

import (
	"bytes"
	"fmt"

	"github.com/goraz/onion"
	"github.com/goraz/onion/ciphers/secconf"
	"github.com/goraz/onion/layers/etcdlayer"
	"github.com/goraz/onion/layers/filewatchlayer"
)

// Normally this should be in a safe place, not here
const privateKey = `PRIVATE KEY`

func main() {
	// The private key should be in the safe place. this is just a demo, also there is a cli tool
	// to create this `go get -u github.com/goraz/onion/cli/onioncli`
	cipher, err := secconf.NewCipher(bytes.NewReader([]byte(privateKey)))
	if err != nil {
		panic(err)
	}

	// Create a file layer to load data from json file. also it watches for change in the file
	// passing the cipher to this make means the file in base64 and pgp encrypted
	l1, err := filewatchlayer.NewFileWatchLayer("/etc/shared.json", cipher)
	if err != nil {
		panic(err)
	}

	// Create a etcd layer. it watches the /app/config key and it should be json file encoded with
	// base64 and pgp
	l2, err := etcdlayer.NewEtcdLayer("/app/config", "json", []string{"http://127.0.0.1:2379"}, cipher)
	if err != nil {
		panic(err)
	}

	// Create the onion, the final result is union of l1 and l2 but l2 overwrites l1.
	o := onion.New(l1, l2)
	// Get the latest version of the key
	str := o.GetStringDefault("test.string", "empty")
	fmt.Println(str)
}

Documentation

Overview

Package onion is a layer based, pluggable config manager for golang. The goal is to have multiple layer and load value base on this layers. the first layer with the data is used to provide the response.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AddLayers

func AddLayers(l ...Layer)

AddLayers add a new layer to global config

func AddLayersContext

func AddLayersContext(ctx context.Context, l ...Layer)

AddLayersContext add a new layer to global config

func Get

func Get(key string) (interface{}, bool)

Get try to get the key from config layers

func GetBool

func GetBool(key string) bool

GetBool is used to get a boolean value fro config, with false as default

func GetBoolDefault

func GetBoolDefault(key string, def bool) bool

GetBoolDefault return bool value from Onion. if the value is not exists or if tha value is not boolean, return the default

func GetDelimiter

func GetDelimiter() string

GetDelimiter return the delimiter for nested key

func GetDuration

func GetDuration(key string) time.Duration

GetDuration is for getting duration from config, it cast both int and string to duration

func GetDurationDefault

func GetDurationDefault(key string, def time.Duration) time.Duration

GetDurationDefault is a function to get duration from config. it support both string duration (like 1h3m2s) and integer duration

func GetFloat32

func GetFloat32(key string) float32

GetFloat32 return an float32 value, if the value is not there, then it returns zero value

func GetFloat32Default

func GetFloat32Default(key string, def float32) float32

GetFloat32Default return an float32 value from Onion, if the value is not exists or its not a float32, default is returned

func GetFloat64

func GetFloat64(key string) float64

GetFloat64 return the float64 value from config, if its not there, return zero

func GetFloat64Default

func GetFloat64Default(key string, def float64) float64

GetFloat64Default return an float64 value from Onion, if the value is not exists or if the value is not float64 then return the default

func GetInt

func GetInt(key string) int

GetInt return an int value, if the value is not there, then it return zero value

func GetInt64

func GetInt64(key string) int64

GetInt64 return the int64 value from config, if its not there, return zero

func GetInt64Default

func GetInt64Default(key string, def int64) int64

GetInt64Default return an int64 value from Onion, if the value is not exists or if the value is not int64 then return the default

func GetIntDefault

func GetIntDefault(key string, def int) int

GetIntDefault return an int value from Onion, if the value is not exists or its not an integer , default is returned

func GetString

func GetString(key string) string

GetString is for getting an string from conig. if the key is not

func GetStringDefault

func GetStringDefault(key string, def string) string

GetStringDefault get a string from Onion. if the value is not exists or if tha value is not string, return the default

func GetStringSlice

func GetStringSlice(key string) []string

GetStringSlice try to get a slice from the config, also it support comma separated value if there is no array at the key.

func RegisterDecoder

func RegisterDecoder(dec Decoder, formats ...string)

RegisterDecoder add a new decoder to the system, json is registered out of the box

func ReloadWatch

func ReloadWatch() <-chan struct{}

ReloadWatch see onion.ReloadWatch

func SetDelimiter

func SetDelimiter(d string)

SetDelimiter set the current delimiter on global config

Types

type Cipher

type Cipher interface {
	Decrypt(io.Reader) ([]byte, error)
}

Cipher is used to decrypt data on loading

type Decoder

type Decoder interface {
	Decode(context.Context, io.Reader) (map[string]interface{}, error)
}

Decoder is a stream decoder to convert a stream into a map of config keys, json is supported out of the box

func GetDecoder

func GetDecoder(format string) Decoder

GetDecoder returns the decoder based on its name, it may returns nil if the decoder is not registered

type Layer

type Layer interface {
	// Load is called once to get the initial data, it can return nil if there is no initial data
	Load() map[string]interface{}
	// Watch is called as soon as the layer registered in the onion. if the layer is persistent
	// it can return nil or a closed channel
	// Also this function may called several time and should return the same channel each time
	// and should not block
	Watch() <-chan map[string]interface{}
}

Layer is an interface to handle the load phase.

func NewEnvLayer

func NewEnvLayer(separator string, whiteList ...string) Layer

NewEnvLayer create new layer using the whitelist of environment values.

func NewEnvLayerPrefix

func NewEnvLayerPrefix(separator string, prefix string) Layer

NewEnvLayerPrefix create new env layer, with all values with the same prefix TODO: No prefix loading

func NewFileLayer

func NewFileLayer(path string, c Cipher) (Layer, error)

NewFileLayer create a new file layer. it choose the format base on the extension

func NewFileLayerContext

func NewFileLayerContext(ctx context.Context, path string, c Cipher) (Layer, error)

NewFileLayerContext create a new file layer. it choose the format base on the extension

func NewFlatEnvLayerPrefix

func NewFlatEnvLayerPrefix(separator string, prefix string) Layer

NewFlatEnvLayerPrefix create new env layer, with all values with the same prefix however this does not seperate the value hierarchically. This can be used for layered configurations such as csv or ini instead of json or yaml.

func NewMapLayer

func NewMapLayer(data ...map[string]interface{}) Layer

NewMapLayer returns a basic map layer, this layer is simply holds a map of values

func NewStreamLayer

func NewStreamLayer(r io.Reader, format string, c Cipher) (Layer, error)

NewStreamLayer create new stream layer, see the NewStreamLayerContext

func NewStreamLayerContext

func NewStreamLayerContext(ctx context.Context, r io.Reader, format string, c Cipher) (Layer, error)

NewStreamLayerContext try to create a layer based on a stream, the format should be a registered format (see RegisterDecoder) and if the Cipher is not nil, it pass data to cipher first. A nil cipher is accepted as plain cipher

type Onion

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

Onion is a layer base configuration system

func New

func New(layers ...Layer) *Onion

New returns a new onion

func NewContext

func NewContext(ctx context.Context, layers ...Layer) *Onion

NewContext return a new Onion, context is used for watch

func (*Onion) AddLayers

func (o *Onion) AddLayers(l ...Layer)

AddLayers add new layers to onion

func (*Onion) AddLayersContext

func (o *Onion) AddLayersContext(ctx context.Context, l ...Layer)

AddLayersContext add new layers to the end of config layers. last layer is loaded after all other layer

func (*Onion) Get

func (o *Onion) Get(key string) (interface{}, bool)

Get try to get the key from config layers

func (*Onion) GetBool

func (o *Onion) GetBool(key string) bool

GetBool is used to get a boolean value fro config, with false as default

func (*Onion) GetBoolDefault

func (o *Onion) GetBoolDefault(key string, def bool) bool

GetBoolDefault return bool value from Onion. if the value is not exists or if tha value is not boolean, return the default

func (*Onion) GetDelimiter

func (o *Onion) GetDelimiter() string

GetDelimiter return the delimiter for nested key

func (*Onion) GetDuration

func (o *Onion) GetDuration(key string) time.Duration

GetDuration is for getting duration from config, it cast both int and string to duration

func (*Onion) GetDurationDefault

func (o *Onion) GetDurationDefault(key string, def time.Duration) time.Duration

GetDurationDefault is a function to get duration from config. it support both string duration (like 1h3m2s) and integer duration

func (*Onion) GetFloat32

func (o *Onion) GetFloat32(key string) float32

GetFloat32 return an float32 value, if the value is not there, then it returns zero value

func (*Onion) GetFloat32Default

func (o *Onion) GetFloat32Default(key string, def float32) float32

GetFloat32Default return an float32 value from Onion, if the value is not exists or its not a float32, default is returned

func (*Onion) GetFloat64

func (o *Onion) GetFloat64(key string) float64

GetFloat64 return the float64 value from config, if its not there, return zero

func (*Onion) GetFloat64Default

func (o *Onion) GetFloat64Default(key string, def float64) float64

GetFloat64Default return an float64 value from Onion, if the value is not exists or if the value is not float64 then return the default

func (*Onion) GetInt

func (o *Onion) GetInt(key string) int

GetInt return an int value, if the value is not there, then it return zero value

func (*Onion) GetInt64

func (o *Onion) GetInt64(key string) int64

GetInt64 return the int64 value from config, if its not there, return zero

func (*Onion) GetInt64Default

func (o *Onion) GetInt64Default(key string, def int64) int64

GetInt64Default return an int64 value from Onion, if the value is not exists or if the value is not int64 then return the default

func (*Onion) GetIntDefault

func (o *Onion) GetIntDefault(key string, def int) int

GetIntDefault return an int value from Onion, if the value is not exists or its not an integer , default is returned

func (*Onion) GetString

func (o *Onion) GetString(key string) string

GetString is for getting an string from conig. if the key is not

func (*Onion) GetStringDefault

func (o *Onion) GetStringDefault(key string, def string) string

GetStringDefault get a string from Onion. if the value is not exists or if tha value is not string, return the default

func (*Onion) GetStringSlice

func (o *Onion) GetStringSlice(key string) []string

GetStringSlice try to get a slice from the config, also it support comma separated value if there is no array at the key.

func (*Onion) LayersData

func (o *Onion) LayersData() []map[string]interface{}

LayersData is used to get all layers data at once, useful for test and also used in the config writer

func (*Onion) ReloadWatch

func (o *Onion) ReloadWatch() <-chan struct{}

ReloadWatch returns a channel to watch new layer data change, it just work for once, after the first change the channel will be changed to a new channel (the old channel will be closed to signal all listeners)

func (*Onion) SetDelimiter

func (o *Onion) SetDelimiter(d string)

SetDelimiter set the current delimiter

Directories

Path Synopsis
ciphers
secconf
Package secconf implements secconf encoding as specified in the following format: base64(gpg(gzip(data)))
Package secconf implements secconf encoding as specified in the following format: base64(gpg(gzip(data)))
cli
layers
etcdlayer
Package etcdlayer is a layer to manage a configuration on a key inside the etcd, it watches the change on the key
Package etcdlayer is a layer to manage a configuration on a key inside the etcd, it watches the change on the key
filewatchlayer
Package filewatchlayer is the file loader with watch for the file.
Package filewatchlayer is the file loader with watch for the file.
loaders
properties
Package properties is used to handle properties file in Onion file layer.
Package properties is used to handle properties file in Onion file layer.
toml
Package tomlloader is used to handle toml file in Onion file layer.
Package tomlloader is used to handle toml file in Onion file layer.
yaml
Package yamlloader is used to handle yaml file in Onion stream layer.
Package yamlloader is used to handle yaml file in Onion stream layer.

Jump to

Keyboard shortcuts

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