mirror of
https://github.com/spf13/viper
synced 2024-12-22 11:37:02 +00:00
work in progress
This commit is contained in:
parent
83fd92627c
commit
3d8182460c
1 changed files with 137 additions and 3 deletions
140
viper.go
140
viper.go
|
@ -5,13 +5,15 @@
|
|||
|
||||
// Viper is a application configuration system.
|
||||
// It believes that applications can be configured a variety of ways
|
||||
// via flags, ENVIRONMENT variables, configuration files.
|
||||
// via flags, ENVIRONMENT variables, configuration files retrieved
|
||||
// from the file system, or a remote key/value store.
|
||||
|
||||
// Each item takes precedence over the item below it:
|
||||
|
||||
// flag
|
||||
// env
|
||||
// config
|
||||
// key/value store
|
||||
// default
|
||||
|
||||
package viper
|
||||
|
@ -25,6 +27,7 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -38,14 +41,29 @@ import (
|
|||
"gopkg.in/yaml.v1"
|
||||
)
|
||||
|
||||
// remoteProvider stores the configuration necessary
|
||||
// to connect to a remote key/value store.
|
||||
// Optional secretKeyring to unencrypt encrypted values
|
||||
// can be provided.
|
||||
type remoteProvider struct {
|
||||
provider string
|
||||
endpoint string
|
||||
path string
|
||||
secretKeyring string
|
||||
}
|
||||
|
||||
// A set of paths to look for the config file in
|
||||
var configPaths []string
|
||||
|
||||
// A set of remote providers to search for the configuration
|
||||
var remoteProviders []*remoteProvider
|
||||
|
||||
// Name of file to look for inside the path
|
||||
var configName string = "config"
|
||||
|
||||
// extensions Supported
|
||||
var SupportedExts []string = []string{"json", "toml", "yaml", "yml"}
|
||||
var SupportedRemoteProviders []string = []string{"etcd", "consul"}
|
||||
var configFile string
|
||||
var configType string
|
||||
|
||||
|
@ -53,6 +71,7 @@ var config map[string]interface{} = make(map[string]interface{})
|
|||
var override map[string]interface{} = make(map[string]interface{})
|
||||
var env map[string]string = make(map[string]string)
|
||||
var defaults map[string]interface{} = make(map[string]interface{})
|
||||
var kvstore map[string]interface{} = make(map[string]interface{})
|
||||
var pflags map[string]*pflag.Flag = make(map[string]*pflag.Flag)
|
||||
var aliases map[string]string = make(map[string]string)
|
||||
|
||||
|
@ -81,6 +100,74 @@ func AddConfigPath(in string) {
|
|||
}
|
||||
}
|
||||
|
||||
// AddRemoteProvider adds a remote configuration source.
|
||||
// Remote Providers are searched in the order they are added.
|
||||
// provider is a string value, "etcd" or "consul" are currently supported.
|
||||
// endpoint is the url. etcd requires http://ip:port consul requires ip:port
|
||||
// path is the path in the k/v store to retrieve configuration
|
||||
// To retrieve a config file called myapp.json from /configs/myapp.json
|
||||
// you should set path to /configs and set config name (SetConfigName()) to
|
||||
// "myapp"
|
||||
func AddRemoteProvider(provider, endpoint, path string) error {
|
||||
if !stringInSlice(provider, SupportedRemoteProviders) {
|
||||
return UnsupportedRemoteProviderError(provider)
|
||||
}
|
||||
if provider != "" && endpoint != "" {
|
||||
jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint)
|
||||
rp := &remoteProvider{
|
||||
endpoint: endpoint,
|
||||
provider: provider,
|
||||
}
|
||||
if !providerPathExists(rp) {
|
||||
remoteProviders = append(remoteProviders, rp)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddSecureRemoteProvider adds a remote configuration source.
|
||||
// Secure Remote Providers are searched in the order they are added.
|
||||
// provider is a string value, "etcd" or "consul" are currently supported.
|
||||
// endpoint is the url. etcd requires http://ip:port consul requires ip:port
|
||||
// secretkeyring is the filepath to your openpgp secret keyring. e.g. /etc/secrets/myring.gpg
|
||||
// path is the path in the k/v store to retrieve configuration
|
||||
// To retrieve a config file called myapp.json from /configs/myapp.json
|
||||
// you should set path to /configs and set config name (SetConfigName()) to
|
||||
// "myapp"
|
||||
// Secure Remote Providers are implemented with github.com/xordataexchange/crypt
|
||||
func AddSecureRemoteProvider(provider, endpoint, secretkeyring string) error {
|
||||
if !stringInSlice(provider, SupportedRemoteProviders) {
|
||||
return UnsupportedRemoteProviderError(provider)
|
||||
}
|
||||
if provider != "" && endpoint != "" {
|
||||
jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint)
|
||||
rp := &remoteProvider{
|
||||
endpoint: endpoint,
|
||||
provider: provider,
|
||||
}
|
||||
if !providerPathExists(rp) {
|
||||
remoteProviders = append(remoteProviders, rp)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func providerPathExists(p *remoteProvider) bool {
|
||||
|
||||
for _, y := range remoteProviders {
|
||||
if reflect.DeepEqual(y, p) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type UnsupportedRemoteProviderError string
|
||||
|
||||
func (str UnsupportedRemoteProviderError) Error() string {
|
||||
return fmt.Sprintf("Unsupported Remote Provider Type %q", string(str))
|
||||
}
|
||||
|
||||
func GetString(key string) string {
|
||||
return cast.ToString(Get(key))
|
||||
}
|
||||
|
@ -132,6 +219,10 @@ func Marshal(rawVal interface{}) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = mapstructure.Decode(kvstore, rawVal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
insensativiseMaps()
|
||||
|
||||
|
@ -221,6 +312,12 @@ func find(key string) interface{} {
|
|||
return val
|
||||
}
|
||||
|
||||
val, exists = kvstore[key]
|
||||
if exists {
|
||||
jww.TRACE.Println(key, "found in key/value store:", val)
|
||||
return val
|
||||
}
|
||||
|
||||
val, exists = defaults[key]
|
||||
if exists {
|
||||
jww.TRACE.Println(key, "found in defaults:", val)
|
||||
|
@ -289,6 +386,10 @@ func registerAlias(alias string, key string) {
|
|||
delete(config, alias)
|
||||
config[key] = val
|
||||
}
|
||||
if val, ok := kvstore[alias]; ok {
|
||||
delete(kvstore, alias)
|
||||
kvstore[key] = val
|
||||
}
|
||||
if val, ok := defaults[alias]; ok {
|
||||
delete(defaults, alias)
|
||||
defaults[key] = val
|
||||
|
@ -331,7 +432,8 @@ func SetDefault(key string, value interface{}) {
|
|||
}
|
||||
|
||||
// The user provided value (via flag)
|
||||
// Will be used instead of values obtained via config file, ENV or default
|
||||
// Will be used instead of values obtained via
|
||||
// config file, ENV, default, or key/value store
|
||||
func Set(key string, value interface{}) {
|
||||
// If alias passed in, then set the proper override
|
||||
key = realKey(strings.ToLower(key))
|
||||
|
@ -345,7 +447,7 @@ func (str UnsupportedConfigError) Error() string {
|
|||
}
|
||||
|
||||
// Viper will discover and load the configuration file from disk
|
||||
// searching in one of the defined paths.
|
||||
// and key/value stores, searching in one of the defined paths.
|
||||
func ReadInConfig() error {
|
||||
jww.INFO.Println("Attempting to read in config file")
|
||||
if !stringInSlice(getConfigType(), SupportedExts) {
|
||||
|
@ -357,6 +459,8 @@ func ReadInConfig() error {
|
|||
return err
|
||||
}
|
||||
|
||||
getKeyValueConfig()
|
||||
|
||||
MarshallReader(bytes.NewReader(file))
|
||||
return nil
|
||||
}
|
||||
|
@ -389,6 +493,29 @@ func insensativiseMaps() {
|
|||
insensativiseMap(config)
|
||||
insensativiseMap(defaults)
|
||||
insensativiseMap(override)
|
||||
insensativiseMap(kvstore)
|
||||
}
|
||||
|
||||
// retrieve the first found remote configuration
|
||||
func getKeyValueConfig() {
|
||||
for _, rp := range remoteProviders {
|
||||
val, err := getRemoteConfig(rp)
|
||||
if err != nil {
|
||||
kvstore = val
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getRemoteConfig(provider *remoteProvider) (map[string]interface{}, error) {
|
||||
switch provider.provider {
|
||||
case "etcd":
|
||||
// do something
|
||||
case "consul":
|
||||
// do something
|
||||
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func insensativiseMap(m map[string]interface{}) {
|
||||
|
@ -412,6 +539,10 @@ func AllKeys() []string {
|
|||
m[key] = struct{}{}
|
||||
}
|
||||
|
||||
for key, _ := range kvstore {
|
||||
m[key] = struct{}{}
|
||||
}
|
||||
|
||||
for key, _ := range override {
|
||||
m[key] = struct{}{}
|
||||
}
|
||||
|
@ -594,6 +725,8 @@ func absPathify(inPath string) string {
|
|||
func Debug() {
|
||||
fmt.Println("Config:")
|
||||
pretty.Println(config)
|
||||
fmt.Println("Key/Value Store:")
|
||||
pretty.Println(kvstore)
|
||||
fmt.Println("Env:")
|
||||
pretty.Println(env)
|
||||
fmt.Println("Defaults:")
|
||||
|
@ -613,6 +746,7 @@ func Reset() {
|
|||
configFile = ""
|
||||
configType = ""
|
||||
|
||||
kvstore = make(map[string]interface{})
|
||||
config = make(map[string]interface{})
|
||||
override = make(map[string]interface{})
|
||||
env = make(map[string]string)
|
||||
|
|
Loading…
Reference in a new issue