Layer based configuration for golang

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)
}
Comments
  • Merge layers

    Merge layers

    I added a feature to obtain a single map[string]interface{}, instead of the usual slice, containing all the layers. And I also added a method that will use this merge: a MergeAndDecode method that will decode our configuration directly in a struct using mapstructure, that is already a dependency of this project.

    I hope you'll be glad of my adds and you will merge this feature to your project! We would like to use onion in our, I liked the layer concept and how you have implemented it.

    Thanks in advance

  • support comma separated string as if it were a string slice

    support comma separated string as if it were a string slice

    I wanted to use a list of strings for a config param, but I wanted to be able to get that list from an environment variable.

    My solution is to modify GetStringSlice so that if it doesn't find a slice, it will then see if it has a string instead. If so, it splits that string on any commas, creating a []string.

  • Add the possibility to load the configurations from multiple file in the same directory

    Add the possibility to load the configurations from multiple file in the same directory

    I want to read all configurations file in the numeric (sort -n) order present in a certain directory. For example, in /etc/sysctl.d/ I have these files:

    10-console-messages.toml  
    10-link-restrictions.toml
    101-zeropage.conf
    README
    99-sysctl.toml
    

    I want to read all /etc/sysctl.d/*.toml files. e.g

    • 10-console-messages.toml
    • 10-link-restrictions.toml
    • 99-sysctl.toml
    • 101-zeropage.conf
  • Add support to TOML 0.5.0

    Add support to TOML 0.5.0

    https://github.com/BurntSushi/toml is outdated and only support TOML 0.4.0

    Maybe this package can be a good replacement: https://github.com/pelletier/go-toml

  • reload wrapper

    reload wrapper

    A library to wrap around the onion and watch the changes and after each change, update the values in its bind list. there is a prototype version in v3, for example, https://github.com/fzerorubigd/onion/blob/v3/onion.go#L489

  • directory watch layer

    directory watch layer

    Watches for changes in a whole directory, limited to extensions

    It is appropriate for conf.d directories

    Signed-off-by: Pouyan Heyratpour [email protected]

  • Added a FlatEnvLayer

    Added a FlatEnvLayer

    I copied and and configured NewFlatEnvLayerPrefix from NewEnvLayerPrefix to solve the issue of needing an environment variable that corresponds to a configuration that is not hierarchical. For example, if one wanted to use onion to parse fields from a layer that they had implemented, and that filetype was not hierarchical, then the env layer with prefix would not match the configuration file. With this function, the Prefix + Separator will be stripped off of the beginning, but the rest of the name will remain the same.

  • Golang 1.6 : cannot return value obtained from unexported field or method

    Golang 1.6 : cannot return value obtained from unexported field or method

    • /home/travis/gopath/src/github.com/fzerorubigd/onion/onion_test.go Line 184: - reflect.Value.Interface: cannot return value obtained from unexported field or method goroutine 9 [running]: reflect.valueInterface(0x5621c0, 0xc82009e170, 0x56, 0x1, 0x0, 0x0) /home/travis/.gimme/versions/go/src/reflect/value.go:919 +0xe7 reflect.Value.Interface(0x5621c0, 0xc82009e170, 0x56, 0x0, 0x0) /home/travis/.gimme/versions/go/src/reflect/value.go:908 +0x48 github.com/fzerorubigd/onion.iterateConfig(0xc82001e9b0, 0x5622e0, 0xc82009e140, 0x0, 0x0) /home/travis/gopath/src/github.com/fzerorubigd/onion/onion.go:438 +0xf1a github.com/fzerorubigd/onion.(_Onion).GetStruct(0xc82001e9b0, 0x0, 0x0, 0x5622e0, 0xc82009e140) /home/travis/gopath/src/github.com/fzerorubigd/onion/onion.go:381 +0x49 github.com/fzerorubigd/onion.TestOnion.func1.6() /home/travis/gopath/src/github.com/fzerorubigd/onion/onion_test.go:184 +0x1d6 github.com/jtolds/gls._m(0x0, 0xc8200cfb40) /home/travis/gopath/src/github.com/jtolds/gls/stack_tags.go:39 +0x2b github.com/jtolds/gls.mark1(0x0, 0xc8200cfb40) /home/travis/gopath/src/github.com/jtolds/gls/stack_tags.go:18 +0x2b github.com/jtolds/gls._m(0x1, 0xc8200cfb40) /home/travis/gopath/src/github.com/jtolds/gls/stack_tags.go:41 +0x5a github.com/jtolds/gls.markS(0x1, 0xc8200cfb40) /home/travis/gopath/src/github.com/jtolds/gls/stack_tags.go:16 +0x2b github.com/jtolds/gls.addStackTag(0x1, 0xc8200cfb40) /home/travis/gopath/src/github.com/jtolds/gls/stack_tags.go:13 +0x37 github.com/jtolds/gls.(_ContextManager).SetValues(0xc82000a5a0, 0xc8200f6510, 0xc8200cfb40) /home/travis/gopath/src/github.com/jtolds/gls/context.go:92 +0x4b3 github.com/fzerorubigd/onion.TestOnion.func1() /home/travis/gopath/src/github.com/fzerorubigd/onion/onion_test.go:211 +0x22ac github.com/jtolds/gls._m(0x0, 0xc8200ce780) /home/travis/gopath/src/github.com/jtolds/gls/stack_tags.go:39 +0x2b github.com/jtolds/gls.markS(0x0, 0xc8200ce780) /home/travis/gopath/src/github.com/jtolds/gls/stack_tags.go:16 +0x2b github.com/jtolds/gls.addStackTag(0x0, 0xc8200ce780) /home/travis/gopath/src/github.com/jtolds/gls/stack_tags.go:13 +0x37 github.com/jtolds/gls.(*ContextManager).SetValues(0xc82000a5a0, 0xc8200caa80, 0xc8200ce780) /home/travis/gopath/src/github.com/jtolds/gls/context.go:92 +0x4b3 github.com/fzerorubigd/onion.TestOnion(0xc82001c360) /home/travis/gopath/src/github.com/fzerorubigd/onion/onion_test.go:221 +0x186 testing.tRunner(0xc82001c360, 0x6edf20) /home/travis/.gimme/versions/go/src/testing/testing.go:473 +0x98 created by testing.RunTests /home/travis/.gimme/versions/go/src/testing/testing.go:582 +0x892
  • DisallowUnknownFields feature

    DisallowUnknownFields feature

    It would be great to have a feature like the DisallowUnknownFields to set in the onionwriter.DecodeOnion

    	var foo Bar
    	decoder := json.NewDecoder(r.Body)
    	decoder.DisallowUnknownFields()
    	if err := decoder.Decode(&foo); err != nil {
    		return err
    	}
        
    
  • New Version

    New Version

    • Remove the old toml dependency
    • upgrade etcd
    • add ci linter
    • remove old go versions from build matrix

    Things that block me to merge this right away

    • Flaky unit test in configwatch
    • etcd version is problematic, I am not sure why the new versions are not in the go proxy
  • Add support to quoted keys

    Add support to quoted keys

    Mm I found a little problem along the way: both toml and json should support keys like: toml

    site."google.com" = true
    

    json

      "site": {
        "google.com": true
      }
    

    But onion doesn't, it split it in site, google, com instead of site, "google.com" Should we support this?

Golang library for managing configuration data from environment variables

envconfig import "github.com/kelseyhightower/envconfig" Documentation See godoc Usage Set some environment variables: export MYAPP_DEBUG=false export

Dec 26, 2022
A golang package for parsing ini-style configuration files

Mini Mini is a simple ini configuration file parser. The ini syntax supported includes: The standard name=value Comments on new lines starting with #

Jan 7, 2023
Golang Configuration tool that support YAML, JSON, TOML, Shell Environment

Configor Golang Configuration tool that support YAML, JSON, TOML, Shell Environment (Supports Go 1.10+) Usage package main import ( "fmt" "github.c

Dec 29, 2022
🔥🔥 🌈 Golang configuration,use to Viper reading from remote Nacos config systems. Viper remote for Naocs.

Viper remote for Nacos Golang configuration,use to Viper reading from remote Nacos config systems. Viper remote for Naocs. runtime_viper := viper.New(

Dec 6, 2022
Nginx Configuration Golang Parser

Nginx Configuration Golang Parser

Oct 21, 2022
DanaConfig is a static configuration extractor implemented in Golang for the main component of DanaBot
DanaConfig is a static configuration extractor implemented in Golang for the main component of DanaBot

DanaConfig is a static configuration extractor implemented in Golang for the main component of DanaBot (targeting Microsoft Windows). By de

Mar 7, 2022
Cfginterpolator is an interpolate library in golang allowing to include data from external sources in your configuration

cfginterpolator cfginterpolator is an interpolate library in golang allowing to include data from external sources in your configuration cfginterpolat

Dec 14, 2021
Golang library for reading properties from configuration files in JSON and YAML format or from environment variables.

go-config Golang library for reading properties from configuration files in JSON and YAML format or from environment variables. Usage Create config in

Aug 22, 2022
12 factor configuration as a typesafe struct in as little as two function calls

Config Manage your application config as a typesafe struct in as little as two function calls. type MyConfig struct { DatabaseUrl string `config:"DAT

Dec 13, 2022
JSON or YAML configuration wrapper with convenient access methods.

Config Package config provides convenient access methods to configuration stored as JSON or YAML. This is a fork of the original version. This version

Dec 16, 2022
Configure is a Go package that gives you easy configuration of your project through redundancy

Configure Configure is a Go package that gives you easy configuration of your project through redundancy. It has an API inspired by negroni and the fl

Sep 26, 2022
An opinionated configuration loading framework for Containerized and Cloud-Native applications.
An opinionated configuration loading framework for Containerized and Cloud-Native applications.

Opinionated configuration loading framework for Containerized and 12-Factor compliant applications. Read configurations from Environment Variables, an

Dec 16, 2022
Load configuration in cascade from multiple backends into a struct
Load configuration in cascade from multiple backends into a struct

Confita is a library that loads configuration from multiple backends and stores it in a struct. Supported backends Environment variables JSON files Ya

Jan 1, 2023
Small library to read your configuration from environment variables

envconfig envconfig is a library which allows you to parse your configuration from environment variables and fill an arbitrary struct. See the example

Nov 3, 2022
A minimalist Go configuration library
A minimalist Go configuration library

fig fig is a tiny library for loading an application's config file and its environment into a Go struct. Individual fields can have default values def

Dec 23, 2022
go-up! A simple configuration library with recursive placeholders resolution and no magic.

go-up! A simple configuration library with placeholders resolution and no magic. go-up provides a simple way to configure an application from multiple

Nov 23, 2022
goconfig uses a struct as input and populates the fields of this struct with parameters from command line, environment variables and configuration file.

goconfig goconfig uses a struct as input and populates the fields of this struct with parameters from command line, environment variables and configur

Dec 15, 2022
Go configuration made easy!

gofigure Go configuration made easy! Just define a struct and call Gofigure Supports strings, ints/uints/floats, slices and nested structs Supports en

Sep 26, 2022
Harvest configuration, watch and notify subscriber

Harvester Harvester is a configuration library which helps setting up and monitoring configuration values in order to dynamically reconfigure your app

Dec 26, 2022