forked from mirror/viper
Added basic documentation, pointers to crypt repository, and tests for precedence
This commit is contained in:
parent
5e1d5e7207
commit
51da30f655
46
README.md
46
README.md
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue