forked from mirror/viper
Make the remote features optional
This commit is contained in:
parent
d62d4bb4c6
commit
be5ff3e484
|
@ -201,6 +201,11 @@ Example:
|
||||||
|
|
||||||
|
|
||||||
### Remote Key/Value Store Support
|
### Remote Key/Value Store Support
|
||||||
|
|
||||||
|
To enable remote support in Viper, do a blank import of the `viper/remote` package:
|
||||||
|
|
||||||
|
`import _ github.com/spf13/viper/remote`
|
||||||
|
|
||||||
Viper will read a config string (as JSON, TOML, or YAML) retrieved from a
|
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
|
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,
|
over default values, but are overriden by configuration values retrieved from disk,
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
// Copyright © 2015 Steve Francia <spf@spf13.com>.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by an MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package remote integrates the remote features of Viper.
|
||||||
|
package remote
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
crypt "github.com/xordataexchange/crypt/config"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type remoteConfigProvider struct{}
|
||||||
|
|
||||||
|
func (rc remoteConfigProvider) Get(rp viper.RemoteProvider) (io.Reader, error) {
|
||||||
|
cm, err := getConfigManager(rp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b, err := cm.Get(rp.Path())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return bytes.NewReader(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc remoteConfigProvider) Watch(rp viper.RemoteProvider) (io.Reader, error) {
|
||||||
|
cm, err := getConfigManager(rp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp := <-cm.Watch(rp.Path(), nil)
|
||||||
|
err = resp.Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes.NewReader(resp.Value), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) {
|
||||||
|
|
||||||
|
var cm crypt.ConfigManager
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if rp.SecretKeyring() != "" {
|
||||||
|
kr, err := os.Open(rp.SecretKeyring())
|
||||||
|
defer kr.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if rp.Provider() == "etcd" {
|
||||||
|
cm, err = crypt.NewEtcdConfigManager([]string{rp.Endpoint()}, kr)
|
||||||
|
} else {
|
||||||
|
cm, err = crypt.NewConsulConfigManager([]string{rp.Endpoint()}, kr)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if rp.Provider() == "etcd" {
|
||||||
|
cm, err = crypt.NewStandardEtcdConfigManager([]string{rp.Endpoint()})
|
||||||
|
} else {
|
||||||
|
cm, err = crypt.NewStandardConsulConfigManager([]string{rp.Endpoint()})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return cm, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
viper.RemoteConfig = &remoteConfigProvider{}
|
||||||
|
}
|
114
viper.go
114
viper.go
|
@ -35,7 +35,6 @@ import (
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
jww "github.com/spf13/jwalterweatherman"
|
jww "github.com/spf13/jwalterweatherman"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
crypt "github.com/xordataexchange/crypt/config"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var v *Viper
|
var v *Viper
|
||||||
|
@ -44,6 +43,14 @@ func init() {
|
||||||
v = New()
|
v = New()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type remoteConfigFactory interface {
|
||||||
|
Get(rp RemoteProvider) (io.Reader, error)
|
||||||
|
Watch(rp RemoteProvider) (io.Reader, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoteConfig is optional, see the remote package
|
||||||
|
var RemoteConfig remoteConfigFactory
|
||||||
|
|
||||||
// Denotes encountering an unsupported
|
// Denotes encountering an unsupported
|
||||||
// configuration filetype.
|
// configuration filetype.
|
||||||
type UnsupportedConfigError string
|
type UnsupportedConfigError string
|
||||||
|
@ -115,7 +122,7 @@ type Viper struct {
|
||||||
configPaths []string
|
configPaths []string
|
||||||
|
|
||||||
// A set of remote providers to search for the configuration
|
// A set of remote providers to search for the configuration
|
||||||
remoteProviders []*remoteProvider
|
remoteProviders []*defaultRemoteProvider
|
||||||
|
|
||||||
// Name of file to look for inside the path
|
// Name of file to look for inside the path
|
||||||
configName string
|
configName string
|
||||||
|
@ -160,17 +167,40 @@ func Reset() {
|
||||||
SupportedRemoteProviders = []string{"etcd", "consul"}
|
SupportedRemoteProviders = []string{"etcd", "consul"}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remoteProvider stores the configuration necessary
|
type defaultRemoteProvider struct {
|
||||||
// to connect to a remote key/value store.
|
|
||||||
// Optional secretKeyring to unencrypt encrypted values
|
|
||||||
// can be provided.
|
|
||||||
type remoteProvider struct {
|
|
||||||
provider string
|
provider string
|
||||||
endpoint string
|
endpoint string
|
||||||
path string
|
path string
|
||||||
secretKeyring string
|
secretKeyring string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rp defaultRemoteProvider) Provider() string {
|
||||||
|
return rp.provider
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rp defaultRemoteProvider) Endpoint() string {
|
||||||
|
return rp.endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rp defaultRemoteProvider) Path() string {
|
||||||
|
return rp.path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rp defaultRemoteProvider) SecretKeyring() string {
|
||||||
|
return rp.secretKeyring
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoteProvider stores the configuration necessary
|
||||||
|
// to connect to a remote key/value store.
|
||||||
|
// Optional secretKeyring to unencrypt encrypted values
|
||||||
|
// can be provided.
|
||||||
|
type RemoteProvider interface {
|
||||||
|
Provider() string
|
||||||
|
Endpoint() string
|
||||||
|
Path() string
|
||||||
|
SecretKeyring() string
|
||||||
|
}
|
||||||
|
|
||||||
// Universally supported extensions.
|
// Universally supported extensions.
|
||||||
var SupportedExts []string = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop"}
|
var SupportedExts []string = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop"}
|
||||||
|
|
||||||
|
@ -252,7 +282,7 @@ func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error {
|
||||||
}
|
}
|
||||||
if provider != "" && endpoint != "" {
|
if provider != "" && endpoint != "" {
|
||||||
jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint)
|
jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint)
|
||||||
rp := &remoteProvider{
|
rp := &defaultRemoteProvider{
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
provider: provider,
|
provider: provider,
|
||||||
path: path,
|
path: path,
|
||||||
|
@ -284,7 +314,7 @@ func (v *Viper) AddSecureRemoteProvider(provider, endpoint, path, secretkeyring
|
||||||
}
|
}
|
||||||
if provider != "" && endpoint != "" {
|
if provider != "" && endpoint != "" {
|
||||||
jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint)
|
jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint)
|
||||||
rp := &remoteProvider{
|
rp := &defaultRemoteProvider{
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
provider: provider,
|
provider: provider,
|
||||||
path: path,
|
path: path,
|
||||||
|
@ -296,7 +326,7 @@ func (v *Viper) AddSecureRemoteProvider(provider, endpoint, path, secretkeyring
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Viper) providerPathExists(p *remoteProvider) bool {
|
func (v *Viper) providerPathExists(p *defaultRemoteProvider) bool {
|
||||||
for _, y := range v.remoteProviders {
|
for _, y := range v.remoteProviders {
|
||||||
if reflect.DeepEqual(y, p) {
|
if reflect.DeepEqual(y, p) {
|
||||||
return true
|
return true
|
||||||
|
@ -759,6 +789,10 @@ func (v *Viper) insensitiviseMaps() {
|
||||||
|
|
||||||
// retrieve the first found remote configuration
|
// retrieve the first found remote configuration
|
||||||
func (v *Viper) getKeyValueConfig() error {
|
func (v *Viper) getKeyValueConfig() error {
|
||||||
|
if RemoteConfig == nil {
|
||||||
|
return RemoteConfigError("Enable the remote features by doing a blank import of the viper/remote package: '_ github.com/spf13/viper/remote'")
|
||||||
|
}
|
||||||
|
|
||||||
for _, rp := range v.remoteProviders {
|
for _, rp := range v.remoteProviders {
|
||||||
val, err := v.getRemoteConfig(rp)
|
val, err := v.getRemoteConfig(rp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -770,36 +804,12 @@ func (v *Viper) getKeyValueConfig() error {
|
||||||
return RemoteConfigError("No Files Found")
|
return RemoteConfigError("No Files Found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Viper) getRemoteConfig(provider *remoteProvider) (map[string]interface{}, error) {
|
func (v *Viper) getRemoteConfig(provider *defaultRemoteProvider) (map[string]interface{}, error) {
|
||||||
var cm crypt.ConfigManager
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if provider.secretKeyring != "" {
|
reader, err := RemoteConfig.Get(provider)
|
||||||
kr, err := os.Open(provider.secretKeyring)
|
|
||||||
defer kr.Close()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if provider.provider == "etcd" {
|
|
||||||
cm, err = crypt.NewEtcdConfigManager([]string{provider.endpoint}, kr)
|
|
||||||
} else {
|
|
||||||
cm, err = crypt.NewConsulConfigManager([]string{provider.endpoint}, kr)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if provider.provider == "etcd" {
|
|
||||||
cm, err = crypt.NewStandardEtcdConfigManager([]string{provider.endpoint})
|
|
||||||
} else {
|
|
||||||
cm, err = crypt.NewStandardConsulConfigManager([]string{provider.endpoint})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
b, err := cm.Get(provider.path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
reader := bytes.NewReader(b)
|
|
||||||
v.marshalReader(reader, v.kvstore)
|
v.marshalReader(reader, v.kvstore)
|
||||||
return v.kvstore, err
|
return v.kvstore, err
|
||||||
}
|
}
|
||||||
|
@ -817,39 +827,11 @@ func (v *Viper) watchKeyValueConfig() error {
|
||||||
return RemoteConfigError("No Files Found")
|
return RemoteConfigError("No Files Found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Viper) watchRemoteConfig(provider *remoteProvider) (map[string]interface{}, error) {
|
func (v *Viper) watchRemoteConfig(provider *defaultRemoteProvider) (map[string]interface{}, error) {
|
||||||
var cm crypt.ConfigManager
|
reader, err := RemoteConfig.Watch(provider)
|
||||||
var err error
|
|
||||||
|
|
||||||
if provider.secretKeyring != "" {
|
|
||||||
kr, err := os.Open(provider.secretKeyring)
|
|
||||||
defer kr.Close()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if provider.provider == "etcd" {
|
|
||||||
cm, err = crypt.NewEtcdConfigManager([]string{provider.endpoint}, kr)
|
|
||||||
} else {
|
|
||||||
cm, err = crypt.NewConsulConfigManager([]string{provider.endpoint}, kr)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if provider.provider == "etcd" {
|
|
||||||
cm, err = crypt.NewStandardEtcdConfigManager([]string{provider.endpoint})
|
|
||||||
} else {
|
|
||||||
cm, err = crypt.NewStandardConsulConfigManager([]string{provider.endpoint})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
resp := <-cm.Watch(provider.path, nil)
|
|
||||||
// b, err := cm.Watch(provider.path, nil)
|
|
||||||
err = resp.Error
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
reader := bytes.NewReader(resp.Value)
|
|
||||||
v.marshalReader(reader, v.kvstore)
|
v.marshalReader(reader, v.kvstore)
|
||||||
return v.kvstore, err
|
return v.kvstore, err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue