Added basic documentation, pointers to crypt repository, and tests for precedence

This commit is contained in:
Brian Ketelsen 2014-10-27 15:32:46 -04:00
parent 5e1d5e7207
commit 51da30f655
2 changed files with 66 additions and 6 deletions

View File

@ -8,6 +8,8 @@ Go configuration with fangs
Viper is a complete configuration solution. Designed to work within an Viper is a complete configuration solution. Designed to work within an
application to handle file based configuration and seamlessly marry that with application to handle file based configuration and seamlessly marry that with
command line flags which can also be used to control application behavior. command line flags which can also be used to control application behavior.
Viper also supports retrieving configuration values from remote key/value stores.
Etcd is currently supported, and Consul is coming soon.
## Why Viper? ## Why Viper?
@ -26,10 +28,8 @@ Viper does the following for you:
Viper believes that: Viper believes that:
1. command line flags take precedence over options set in config files 1. command line flags take precedence over options set in config files
2. config files take precedence over defaults 2. config files take precedence over options set in remote key/value stores
3. remote key/value stores take precedence over defaults
Config files often can be found in multiple locations. Viper allows you to set
multiple paths to search for the config file in.
Viper configuration keys are case insensitive. Viper configuration keys are case insensitive.
@ -70,6 +70,44 @@ Viper configuration keys are case insensitive.
fmt.Println("verbose enabled") fmt.Println("verbose enabled")
} }
### Remote Key/Value Store Support
Viper will read a config string (as JSON, TOML, or YAML) retrieved from a
path in a Key/Value store such as Etcd or Consul. These values take precedence
over default values, but are overriden by configuration values retrieved from disk,
flags, or environment variables.
Viper uses [crypt](https://github.com/xordataexchange/crypt) to retrieve configuration
from the k/v store, which means that you can store your configuration values
encrypted and have them automatically decrypted if you have the correct
gpg keyring. Encryption is optional.
You can use remote configuration in conjunction with local configuration, or
independently of it.
`crypt` has a command-line helper that you can use to put configurations
in your k/v store. `crypt` defaults to etcd on http://127.0.0.1:4001.
go get github.com/xordataexchange/crypt/bin/crypt
crypt set -plaintext /config/hugo.json /Users/hugo/settings/config.json
Confirm that your value was set:
crypt get -plaintext /config/hugo.json
See the `crypt` documentation for examples of how to set encrypted values, or how
to use Consul.
### Remote Key/Value Store Example - Unencrypted
viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001","/config/hugo.json")
err := viper.ReadRemoteConfig()
### Remote Key/Value Store Example - Encrypted
viper.AddSecureRemoteProvier("etcd","http://127.0.0.1:4001","/config/hugo.json","/etc/secrets/mykeyring.gpg")
err := viper.ReadRemoteConfig()
## Q & A ## Q & A

View File

@ -51,6 +51,12 @@ var jsonExample = []byte(`{
} }
}`) }`)
var remoteExample = []byte(`{
"id":"0002",
"type":"cronut",
"newkey":"remote"
}`)
func TestBasics(t *testing.T) { func TestBasics(t *testing.T) {
SetConfigFile("/tmp/config.yaml") SetConfigFile("/tmp/config.yaml")
assert.Equal(t, "/tmp/config.yaml", getConfigFile()) assert.Equal(t, "/tmp/config.yaml", getConfigFile())
@ -126,6 +132,22 @@ func TestTOML(t *testing.T) {
assert.Equal(t, "TOML Example", Get("title")) assert.Equal(t, "TOML Example", Get("title"))
} }
func TestRemotePrecedence(t *testing.T) {
SetConfigType("json")
r := bytes.NewReader(jsonExample)
MarshallReader(r, config)
remote := bytes.NewReader(remoteExample)
assert.Equal(t, "0001", Get("id"))
MarshallReader(remote, kvstore)
assert.Equal(t, "0001", Get("id"))
assert.NotEqual(t, "cronut", Get("type"))
assert.Equal(t, "remote", Get("newkey"))
Set("newkey", "newvalue")
assert.NotEqual(t, "remote", Get("newkey"))
assert.Equal(t, "newvalue", Get("newkey"))
Set("newkey", "remote")
}
func TestEnv(t *testing.T) { func TestEnv(t *testing.T) {
SetConfigType("json") SetConfigType("json")
r := bytes.NewReader(jsonExample) r := bytes.NewReader(jsonExample)
@ -147,9 +169,9 @@ func TestEnv(t *testing.T) {
} }
func TestAllKeys(t *testing.T) { func TestAllKeys(t *testing.T) {
ks := sort.StringSlice{"title", "owner", "name", "beard", "ppu", "batters", "hobbies", "clothing", "age", "hacker", "id", "type"} ks := sort.StringSlice{"title", "newkey", "owner", "name", "beard", "ppu", "batters", "hobbies", "clothing", "age", "hacker", "id", "type"}
dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z") dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
all := map[string]interface{}{"hacker": true, "beard": true, "batters": map[string]interface{}{"batter": []interface{}{map[string]interface{}{"type": "Regular"}, map[string]interface{}{"type": "Chocolate"}, map[string]interface{}{"type": "Blueberry"}, map[string]interface{}{"type": "Devil's Food"}}}, "hobbies": []interface{}{"skateboarding", "snowboarding", "go"}, "ppu": 0.55, "clothing": map[interface{}]interface{}{"jacket": "leather", "trousers": "denim"}, "name": "crunk", "owner": map[string]interface{}{"organization": "MongoDB", "Bio": "MongoDB Chief Developer Advocate & Hacker at Large", "dob": dob}, "id": "13", "title": "TOML Example", "age": 35, "type": "donut"} all := map[string]interface{}{"hacker": true, "beard": true, "newkey": "remote", "batters": map[string]interface{}{"batter": []interface{}{map[string]interface{}{"type": "Regular"}, map[string]interface{}{"type": "Chocolate"}, map[string]interface{}{"type": "Blueberry"}, map[string]interface{}{"type": "Devil's Food"}}}, "hobbies": []interface{}{"skateboarding", "snowboarding", "go"}, "ppu": 0.55, "clothing": map[interface{}]interface{}{"jacket": "leather", "trousers": "denim"}, "name": "crunk", "owner": map[string]interface{}{"organization": "MongoDB", "Bio": "MongoDB Chief Developer Advocate & Hacker at Large", "dob": dob}, "id": "13", "title": "TOML Example", "age": 35, "type": "donut"}
var allkeys sort.StringSlice var allkeys sort.StringSlice
allkeys = AllKeys() allkeys = AllKeys()