Viper now supports multiple vipers. No API changes.
diff --git a/util.go b/util.go
new file mode 100644
index 0000000..1875cc5
--- /dev/null
+++ b/util.go
@@ -0,0 +1,112 @@
+// Copyright © 2014 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.
+
+// Viper is a application configuration system.
+// It believes that applications can be configured a variety of ways
+// via flags, ENVIRONMENT variables, configuration files retrieved
+// from the file system, or a remote key/value store.
+
+package viper
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+
+ jww "github.com/spf13/jwalterweatherman"
+)
+
+func insensativiseMap(m map[string]interface{}) {
+ for key, val := range m {
+ lower := strings.ToLower(key)
+ if key != lower {
+ delete(m, key)
+ m[lower] = val
+ }
+ }
+}
+
+func absPathify(inPath string) string {
+ jww.INFO.Println("Trying to resolve absolute path to", inPath)
+
+ if strings.HasPrefix(inPath, "$HOME") {
+ inPath = userHomeDir() + inPath[5:]
+ }
+
+ if strings.HasPrefix(inPath, "$") {
+ end := strings.Index(inPath, string(os.PathSeparator))
+ inPath = os.Getenv(inPath[1:end]) + inPath[end:]
+ }
+
+ if filepath.IsAbs(inPath) {
+ return filepath.Clean(inPath)
+ }
+
+ p, err := filepath.Abs(inPath)
+ if err == nil {
+ return filepath.Clean(p)
+ } else {
+ jww.ERROR.Println("Couldn't discover absolute path")
+ jww.ERROR.Println(err)
+ }
+ return ""
+}
+
+// Check if File / Directory Exists
+func exists(path string) (bool, error) {
+ _, err := os.Stat(path)
+ if err == nil {
+ return true, nil
+ }
+ if os.IsNotExist(err) {
+ return false, nil
+ }
+ return false, err
+}
+
+func stringInSlice(a string, list []string) bool {
+ for _, b := range list {
+ if b == a {
+ return true
+ }
+ }
+ return false
+}
+
+func userHomeDir() string {
+ if runtime.GOOS == "windows" {
+ home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
+ if home == "" {
+ home = os.Getenv("USERPROFILE")
+ }
+ return home
+ }
+ return os.Getenv("HOME")
+}
+
+func findCWD() (string, error) {
+ serverFile, err := filepath.Abs(os.Args[0])
+
+ if err != nil {
+ return "", fmt.Errorf("Can't get absolute path for executable: %v", err)
+ }
+
+ path := filepath.Dir(serverFile)
+ realFile, err := filepath.EvalSymlinks(serverFile)
+
+ if err != nil {
+ if _, err = os.Stat(serverFile + ".exe"); err == nil {
+ realFile = filepath.Clean(serverFile + ".exe")
+ }
+ }
+
+ if err == nil && realFile != serverFile {
+ path = filepath.Dir(realFile)
+ }
+
+ return path, nil
+}
diff --git a/viper.go b/viper.go
index 3a55f23..800230f 100644
--- a/viper.go
+++ b/viper.go
@@ -27,7 +27,6 @@
"os"
"path/filepath"
"reflect"
- "runtime"
"strings"
"time"
@@ -41,6 +40,65 @@
"gopkg.in/yaml.v1"
)
+var v *viper
+
+func init() {
+ v = New()
+}
+
+type UnsupportedConfigError string
+
+func (str UnsupportedConfigError) Error() string {
+ return fmt.Sprintf("Unsupported Config Type %q", string(str))
+}
+
+type UnsupportedRemoteProviderError string
+
+func (str UnsupportedRemoteProviderError) Error() string {
+ return fmt.Sprintf("Unsupported Remote Provider Type %q", string(str))
+}
+
+type RemoteConfigError string
+
+func (rce RemoteConfigError) Error() string {
+ return fmt.Sprintf("Remote Configurations Error: %s", string(rce))
+}
+
+type viper struct {
+ // A set of paths to look for the config file in
+ configPaths []string
+
+ // A set of remote providers to search for the configuration
+ remoteProviders []*remoteProvider
+
+ // Name of file to look for inside the path
+ configName string
+ configFile string
+ configType string
+
+ config map[string]interface{}
+ override map[string]interface{}
+ defaults map[string]interface{}
+ kvstore map[string]interface{}
+ pflags map[string]*pflag.Flag
+ env map[string]string
+ aliases map[string]string
+}
+
+func New() *viper {
+ v := new(viper)
+ v.configName = "config"
+ v.config = make(map[string]interface{})
+ v.override = make(map[string]interface{})
+ v.defaults = make(map[string]interface{})
+ v.kvstore = make(map[string]interface{})
+ v.pflags = make(map[string]*pflag.Flag)
+ v.env = make(map[string]string)
+ v.aliases = make(map[string]string)
+
+ return v
+}
+
// remoteProvider stores the configuration necessary
// to connect to a remote key/value store.
// Optional secretKeyring to unencrypt encrypted values
@@ -52,50 +110,34 @@
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
+// universally supported extensions
var SupportedExts []string = []string{"json", "toml", "yaml", "yml"}
-var SupportedRemoteProviders []string = []string{"etcd", "consul"}
-var configFile string
-var configType string
-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)
+// universally supported remote providers
+var SupportedRemoteProviders []string = []string{"etcd", "consul"}
// Explicitly define the path, name and extension of the config file
// Viper will use this and not check any of the config paths
-func SetConfigFile(in string) {
+func SetConfigFile(in string) { v.SetConfigFile(in) }
+func (v *viper) SetConfigFile(in string) {
if in != "" {
- configFile = in
+ v.configFile = in
}
}
-func ConfigFileUsed() string {
- return configFile
-}
+// Return the config file used
+func ConfigFileUsed() string { return v.ConfigFileUsed() }
+func (v *viper) ConfigFileUsed() string { return v.configFile }
// Add a path for viper to search for the config file in.
// Can be called multiple times to define multiple search paths.
-
-func AddConfigPath(in string) {
+func AddConfigPath(in string) { v.AddConfigPath(in) }
+func (v *viper) AddConfigPath(in string) {
if in != "" {
absin := absPathify(in)
jww.INFO.Println("adding", absin, "to paths to search")
- if !stringInSlice(absin, configPaths) {
- configPaths = append(configPaths, absin)
+ if !stringInSlice(absin, v.configPaths) {
+ v.configPaths = append(v.configPaths, absin)
}
}
}
@@ -109,6 +151,9 @@
// you should set path to /configs and set config name (SetConfigName()) to
// "myapp"
func AddRemoteProvider(provider, endpoint, path string) error {
+ return v.AddRemoteProvider(provider, endpoint, path)
+}
+func (v *viper) AddRemoteProvider(provider, endpoint, path string) error {
if !stringInSlice(provider, SupportedRemoteProviders) {
return UnsupportedRemoteProviderError(provider)
}
@@ -119,8 +164,8 @@
provider: provider,
path: path,
}
- if !providerPathExists(rp) {
- remoteProviders = append(remoteProviders, rp)
+ if !v.providerPathExists(rp) {
+ v.remoteProviders = append(v.remoteProviders, rp)
}
}
return nil
@@ -137,6 +182,10 @@
// "myapp"
// Secure Remote Providers are implemented with github.com/xordataexchange/crypt
func AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error {
+ return v.AddSecureRemoteProvider(provider, endpoint, path, secretkeyring)
+}
+
+func (v *viper) AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error {
if !stringInSlice(provider, SupportedRemoteProviders) {
return UnsupportedRemoteProviderError(provider)
}
@@ -147,16 +196,15 @@
provider: provider,
path: path,
}
- if !providerPathExists(rp) {
- remoteProviders = append(remoteProviders, rp)
+ if !v.providerPathExists(rp) {
+ v.remoteProviders = append(v.remoteProviders, rp)
}
}
return nil
}
-func providerPathExists(p *remoteProvider) bool {
-
- for _, y := range remoteProviders {
+func (v *viper) providerPathExists(p *remoteProvider) bool {
+ for _, y := range v.remoteProviders {
if reflect.DeepEqual(y, p) {
return true
}
@@ -164,69 +212,73 @@
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 {
+func GetString(key string) string { return v.GetString(key) }
+func (v *viper) GetString(key string) string {
return cast.ToString(Get(key))
}
-func GetBool(key string) bool {
+func GetBool(key string) bool { return v.GetBool(key) }
+func (v *viper) GetBool(key string) bool {
return cast.ToBool(Get(key))
}
-func GetInt(key string) int {
+func GetInt(key string) int { return v.GetInt(key) }
+func (v *viper) GetInt(key string) int {
return cast.ToInt(Get(key))
}
-func GetFloat64(key string) float64 {
+func GetFloat64(key string) float64 { return v.GetFloat64(key) }
+func (v *viper) GetFloat64(key string) float64 {
return cast.ToFloat64(Get(key))
}
-func GetTime(key string) time.Time {
+func GetTime(key string) time.Time { return v.GetTime(key) }
+func (v *viper) GetTime(key string) time.Time {
return cast.ToTime(Get(key))
}
-func GetStringSlice(key string) []string {
+func GetStringSlice(key string) []string { return v.GetStringSlice(key) }
+func (v *viper) GetStringSlice(key string) []string {
return cast.ToStringSlice(Get(key))
}
-func GetStringMap(key string) map[string]interface{} {
+func GetStringMap(key string) map[string]interface{} { return v.GetStringMap(key) }
+func (v *viper) GetStringMap(key string) map[string]interface{} {
return cast.ToStringMap(Get(key))
}
-func GetStringMapString(key string) map[string]string {
+func GetStringMapString(key string) map[string]string { return v.GetStringMapString(key) }
+func (v *viper) GetStringMapString(key string) map[string]string {
return cast.ToStringMapString(Get(key))
}
// Takes a single key and marshals it into a Struct
-func MarshalKey(key string, rawVal interface{}) error {
+func MarshalKey(key string, rawVal interface{}) error { return v.MarshalKey(key, rawVal) }
+func (v *viper) MarshalKey(key string, rawVal interface{}) error {
return mapstructure.Decode(Get(key), rawVal)
}
// Marshals the config into a Struct
-func Marshal(rawVal interface{}) error {
- err := mapstructure.Decode(defaults, rawVal)
+func Marshal(rawVal interface{}) error { return v.Marshal(rawVal) }
+func (v *viper) Marshal(rawVal interface{}) error {
+ err := mapstructure.Decode(v.defaults, rawVal)
if err != nil {
return err
}
- err = mapstructure.Decode(config, rawVal)
+ err = mapstructure.Decode(v.config, rawVal)
if err != nil {
return err
}
- err = mapstructure.Decode(override, rawVal)
+ err = mapstructure.Decode(v.override, rawVal)
if err != nil {
return err
}
- err = mapstructure.Decode(kvstore, rawVal)
+ err = mapstructure.Decode(v.kvstore, rawVal)
if err != nil {
return err
}
- insensativiseMaps()
+ v.insensativiseMaps()
return nil
}
@@ -236,11 +288,12 @@
// serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
// viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
//
-func BindPFlag(key string, flag *pflag.Flag) (err error) {
+func BindPFlag(key string, flag *pflag.Flag) (err error) { return v.BindPFlag(key, flag) }
+func (v *viper) BindPFlag(key string, flag *pflag.Flag) (err error) {
if flag == nil {
return fmt.Errorf("flag for %q is nil", key)
}
- pflags[strings.ToLower(key)] = flag
+ v.pflags[strings.ToLower(key)] = flag
switch flag.Value.Type() {
case "int", "int8", "int16", "int32", "int64":
@@ -256,7 +309,8 @@
// Binds a viper key to a ENV variable
// ENV variables are case sensitive
// If only a key is provided, it will use the env key matching the key, uppercased.
-func BindEnv(input ...string) (err error) {
+func BindEnv(input ...string) (err error) { return v.BindEnv(input...) }
+func (v *viper) BindEnv(input ...string) (err error) {
var key, envkey string
if len(input) == 0 {
return fmt.Errorf("BindEnv missing key to bind to")
@@ -270,20 +324,20 @@
envkey = input[1]
}
- env[key] = envkey
+ v.env[key] = envkey
return nil
}
-func find(key string) interface{} {
+func (v *viper) find(key string) interface{} {
var val interface{}
var exists bool
// if the requested key is an alias, then return the proper key
- key = realKey(key)
+ key = v.realKey(key)
// PFlag Override first
- flag, exists := pflags[key]
+ flag, exists := v.pflags[key]
if exists {
if flag.Changed {
jww.TRACE.Println(key, "found in override (via pflag):", val)
@@ -291,13 +345,13 @@
}
}
- val, exists = override[key]
+ val, exists = v.override[key]
if exists {
jww.TRACE.Println(key, "found in override:", val)
return val
}
- envkey, exists := env[key]
+ envkey, exists := v.env[key]
if exists {
jww.TRACE.Println(key, "registered as env var", envkey)
if val = os.Getenv(envkey); val != "" {
@@ -308,19 +362,19 @@
}
}
- val, exists = config[key]
+ val, exists = v.config[key]
if exists {
jww.TRACE.Println(key, "found in config:", val)
return val
}
- val, exists = kvstore[key]
+ val, exists = v.kvstore[key]
if exists {
jww.TRACE.Println(key, "found in key/value store:", val)
return val
}
- val, exists = defaults[key]
+ val, exists = v.defaults[key]
if exists {
jww.TRACE.Println(key, "found in defaults:", val)
return val
@@ -331,151 +385,157 @@
// Get returns an interface..
// Must be typecast or used by something that will typecast
-func Get(key string) interface{} {
+func Get(key string) interface{} { return v.Get(key) }
+func (v *viper) Get(key string) interface{} {
key = strings.ToLower(key)
- v := find(key)
+ val := v.find(key)
- if v == nil {
+ if val == nil {
return nil
}
- switch v.(type) {
+ switch val.(type) {
case bool:
- return cast.ToBool(v)
+ return cast.ToBool(val)
case string:
- return cast.ToString(v)
+ return cast.ToString(val)
case int64, int32, int16, int8, int:
- return cast.ToInt(v)
+ return cast.ToInt(val)
case float64, float32:
- return cast.ToFloat64(v)
+ return cast.ToFloat64(val)
case time.Time:
- return cast.ToTime(v)
+ return cast.ToTime(val)
case []string:
- return v
+ return val
}
- return v
+ return val
}
-func IsSet(key string) bool {
- t := Get(key)
+func IsSet(key string) bool { return v.IsSet(key) }
+func (v *viper) IsSet(key string) bool {
+ t := v.Get(key)
return t != nil
}
// Have viper check ENV variables for all
// keys set in config, default & flags
-func AutomaticEnv() {
- for _, x := range AllKeys() {
- BindEnv(x)
+func AutomaticEnv() { v.AutomaticEnv() }
+func (v *viper) AutomaticEnv() {
+ for _, x := range v.AllKeys() {
+ v.BindEnv(x)
}
}
// Aliases provide another accessor for the same key.
// This enables one to change a name without breaking the application
-func RegisterAlias(alias string, key string) {
- registerAlias(alias, strings.ToLower(key))
+func RegisterAlias(alias string, key string) { v.RegisterAlias(alias, key) }
+func (v *viper) RegisterAlias(alias string, key string) {
+ v.registerAlias(alias, strings.ToLower(key))
}
-func registerAlias(alias string, key string) {
+func (v *viper) registerAlias(alias string, key string) {
alias = strings.ToLower(alias)
- if alias != key && alias != realKey(key) {
- _, exists := aliases[alias]
+ if alias != key && alias != v.realKey(key) {
+ _, exists := v.aliases[alias]
if !exists {
// if we alias something that exists in one of the maps to another
// name, we'll never be able to get that value using the original
// name, so move the config value to the new realkey.
- if val, ok := config[alias]; ok {
- delete(config, alias)
- config[key] = val
+ if val, ok := v.config[alias]; ok {
+ delete(v.config, alias)
+ v.config[key] = val
}
- if val, ok := kvstore[alias]; ok {
- delete(kvstore, alias)
- kvstore[key] = val
+ if val, ok := v.kvstore[alias]; ok {
+ delete(v.kvstore, alias)
+ v.kvstore[key] = val
}
- if val, ok := defaults[alias]; ok {
- delete(defaults, alias)
- defaults[key] = val
+ if val, ok := v.defaults[alias]; ok {
+ delete(v.defaults, alias)
+ v.defaults[key] = val
}
- if val, ok := override[alias]; ok {
- delete(override, alias)
- override[key] = val
+ if val, ok := v.override[alias]; ok {
+ delete(v.override, alias)
+ v.override[key] = val
}
- aliases[alias] = key
+ v.aliases[alias] = key
}
} else {
- jww.WARN.Println("Creating circular reference alias", alias, key, realKey(key))
+ jww.WARN.Println("Creating circular reference alias", alias, key, v.realKey(key))
}
}
-func realKey(key string) string {
- newkey, exists := aliases[key]
+func (v *viper) realKey(key string) string {
+ newkey, exists := v.aliases[key]
if exists {
jww.DEBUG.Println("Alias", key, "to", newkey)
- return realKey(newkey)
+ return v.realKey(newkey)
} else {
return key
}
}
-func InConfig(key string) bool {
+func InConfig(key string) bool { return v.InConfig(key) }
+func (v *viper) InConfig(key string) bool {
// if the requested key is an alias, then return the proper key
- key = realKey(key)
+ key = v.realKey(key)
- _, exists := config[key]
+ _, exists := v.config[key]
return exists
}
// Set the default value for this key.
// Default only used when no value is provided by the user via flag, config or ENV.
-func SetDefault(key string, value interface{}) {
+func SetDefault(key string, value interface{}) { v.SetDefault(key, value) }
+func (v *viper) SetDefault(key string, value interface{}) {
// If alias passed in, then set the proper default
- key = realKey(strings.ToLower(key))
- defaults[key] = value
+ key = v.realKey(strings.ToLower(key))
+ v.defaults[key] = value
}
// The user provided value (via flag)
// Will be used instead of values obtained via
// config file, ENV, default, or key/value store
-func Set(key string, value interface{}) {
+func Set(key string, value interface{}) { v.Set(key, value) }
+func (v *viper) Set(key string, value interface{}) {
// If alias passed in, then set the proper override
- key = realKey(strings.ToLower(key))
- override[key] = value
-}
-
-type UnsupportedConfigError string
-
-func (str UnsupportedConfigError) Error() string {
- return fmt.Sprintf("Unsupported Config Type %q", string(str))
+ key = v.realKey(strings.ToLower(key))
+ v.override[key] = value
}
// Viper will discover and load the configuration file from disk
// and key/value stores, searching in one of the defined paths.
-func ReadInConfig() error {
+func ReadInConfig() error { return v.ReadInConfig() }
+func (v *viper) ReadInConfig() error {
jww.INFO.Println("Attempting to read in config file")
- if !stringInSlice(getConfigType(), SupportedExts) {
- return UnsupportedConfigError(getConfigType())
+ if !stringInSlice(v.getConfigType(), SupportedExts) {
+ return UnsupportedConfigError(v.getConfigType())
}
- file, err := ioutil.ReadFile(getConfigFile())
+ file, err := ioutil.ReadFile(v.getConfigFile())
if err != nil {
return err
}
- MarshallReader(bytes.NewReader(file), config)
+ v.MarshallReader(bytes.NewReader(file), v.config)
return nil
}
-func ReadRemoteConfig() error {
- err := getKeyValueConfig()
+
+func ReadRemoteConfig() error { return v.ReadRemoteConfig() }
+func (v *viper) ReadRemoteConfig() error {
+ err := v.getKeyValueConfig()
if err != nil {
return err
}
return nil
}
-func MarshallReader(in io.Reader, c map[string]interface{}) {
+
+func MarshallReader(in io.Reader, c map[string]interface{}) { v.MarshallReader(in, c) }
+func (v *viper) MarshallReader(in io.Reader, c map[string]interface{}) {
buf := new(bytes.Buffer)
buf.ReadFrom(in)
- switch getConfigType() {
+ switch v.getConfigType() {
case "yaml", "yml":
if err := yaml.Unmarshal(buf.Bytes(), &c); err != nil {
jww.ERROR.Fatalf("Error parsing config: %s", err)
@@ -495,33 +555,27 @@
insensativiseMap(c)
}
-func insensativiseMaps() {
- insensativiseMap(config)
- insensativiseMap(defaults)
- insensativiseMap(override)
- insensativiseMap(kvstore)
+func (v *viper) insensativiseMaps() {
+ insensativiseMap(v.config)
+ insensativiseMap(v.defaults)
+ insensativiseMap(v.override)
+ insensativiseMap(v.kvstore)
}
// retrieve the first found remote configuration
-func getKeyValueConfig() error {
- for _, rp := range remoteProviders {
- val, err := getRemoteConfig(rp)
+func (v *viper) getKeyValueConfig() error {
+ for _, rp := range v.remoteProviders {
+ val, err := v.getRemoteConfig(rp)
if err != nil {
continue
}
- kvstore = val
+ v.kvstore = val
return nil
}
return RemoteConfigError("No Files Found")
}
-type RemoteConfigError string
-
-func (rce RemoteConfigError) Error() string {
- return fmt.Sprintf("Remote Configurations Error: %s", string(rce))
-}
-
-func getRemoteConfig(provider *remoteProvider) (map[string]interface{}, error) {
+func (v *viper) getRemoteConfig(provider *remoteProvider) (map[string]interface{}, error) {
var cm crypt.ConfigManager
var err error
@@ -551,36 +605,27 @@
return nil, err
}
reader := bytes.NewReader(b)
- MarshallReader(reader, kvstore)
- return kvstore, err
+ v.MarshallReader(reader, v.kvstore)
+ return v.kvstore, err
}
-func insensativiseMap(m map[string]interface{}) {
- for key, val := range m {
- lower := strings.ToLower(key)
- if key != lower {
- delete(m, key)
- m[lower] = val
- }
- }
-}
-
-func AllKeys() []string {
+func AllKeys() []string { return v.AllKeys() }
+func (v *viper) AllKeys() []string {
m := map[string]struct{}{}
- for key, _ := range defaults {
+ for key, _ := range v.defaults {
m[key] = struct{}{}
}
- for key, _ := range config {
+ for key, _ := range v.config {
m[key] = struct{}{}
}
- for key, _ := range kvstore {
+ for key, _ := range v.kvstore {
m[key] = struct{}{}
}
- for key, _ := range override {
+ for key, _ := range v.override {
m[key] = struct{}{}
}
@@ -592,10 +637,11 @@
return a
}
-func AllSettings() map[string]interface{} {
+func AllSettings() map[string]interface{} { return v.AllSettings() }
+func (v *viper) AllSettings() map[string]interface{} {
m := map[string]interface{}{}
- for _, x := range AllKeys() {
- m[x] = Get(x)
+ for _, x := range v.AllKeys() {
+ m[x] = v.Get(x)
}
return m
@@ -603,24 +649,26 @@
// Name for the config file.
// Does not include extension.
-func SetConfigName(in string) {
+func SetConfigName(in string) { v.SetConfigName(in) }
+func (v *viper) SetConfigName(in string) {
if in != "" {
- configName = in
+ v.configName = in
}
}
-func SetConfigType(in string) {
+func SetConfigType(in string) { v.SetConfigType(in) }
+func (v *viper) SetConfigType(in string) {
if in != "" {
- configType = in
+ v.configType = in
}
}
-func getConfigType() string {
- if configType != "" {
- return configType
+func (v *viper) getConfigType() string {
+ if v.configType != "" {
+ return v.configType
}
- cf := getConfigFile()
+ cf := v.getConfigFile()
ext := filepath.Ext(cf)
if len(ext) > 1 {
@@ -630,169 +678,78 @@
}
}
-func getConfigFile() string {
+func (v *viper) getConfigFile() string {
// if explicitly set, then use it
- if configFile != "" {
- return configFile
+ if v.configFile != "" {
+ return v.configFile
}
- cf, err := findConfigFile()
+ cf, err := v.findConfigFile()
if err != nil {
return ""
}
- configFile = cf
- return getConfigFile()
+ v.configFile = cf
+ return v.getConfigFile()
}
-func searchInPath(in string) (filename string) {
+func (v *viper) searchInPath(in string) (filename string) {
jww.DEBUG.Println("Searching for config in ", in)
for _, ext := range SupportedExts {
-
- jww.DEBUG.Println("Checking for", filepath.Join(in, configName+"."+ext))
- if b, _ := exists(filepath.Join(in, configName+"."+ext)); b {
- jww.DEBUG.Println("Found: ", filepath.Join(in, configName+"."+ext))
- return filepath.Join(in, configName+"."+ext)
+ jww.DEBUG.Println("Checking for", filepath.Join(in, v.configName+"."+ext))
+ if b, _ := exists(filepath.Join(in, v.configName+"."+ext)); b {
+ jww.DEBUG.Println("Found: ", filepath.Join(in, v.configName+"."+ext))
+ return filepath.Join(in, v.configName+"."+ext)
}
}
return ""
}
-func findConfigFile() (string, error) {
- jww.INFO.Println("Searching for config in ", configPaths)
+func (v *viper) findConfigFile() (string, error) {
+ jww.INFO.Println("Searching for config in ", v.configPaths)
- for _, cp := range configPaths {
- file := searchInPath(cp)
+ for _, cp := range v.configPaths {
+ file := v.searchInPath(cp)
if file != "" {
return file, nil
}
}
cwd, _ := findCWD()
- file := searchInPath(cwd)
+ file := v.searchInPath(cwd)
if file != "" {
return file, nil
}
// try the current working directory
wd, _ := os.Getwd()
- file = searchInPath(wd)
+ file = v.searchInPath(wd)
if file != "" {
return file, nil
}
- return "", fmt.Errorf("config file not found in: %s", configPaths)
+ return "", fmt.Errorf("config file not found in: %s", v.configPaths)
}
-func findCWD() (string, error) {
- serverFile, err := filepath.Abs(os.Args[0])
-
- if err != nil {
- return "", fmt.Errorf("Can't get absolute path for executable: %v", err)
- }
-
- path := filepath.Dir(serverFile)
- realFile, err := filepath.EvalSymlinks(serverFile)
-
- if err != nil {
- if _, err = os.Stat(serverFile + ".exe"); err == nil {
- realFile = filepath.Clean(serverFile + ".exe")
- }
- }
-
- if err == nil && realFile != serverFile {
- path = filepath.Dir(realFile)
- }
-
- return path, nil
-}
-
-// Check if File / Directory Exists
-func exists(path string) (bool, error) {
- _, err := os.Stat(path)
- if err == nil {
- return true, nil
- }
- if os.IsNotExist(err) {
- return false, nil
- }
- return false, err
-}
-
-func stringInSlice(a string, list []string) bool {
- for _, b := range list {
- if b == a {
- return true
- }
- }
- return false
-}
-
-func userHomeDir() string {
- if runtime.GOOS == "windows" {
- home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
- if home == "" {
- home = os.Getenv("USERPROFILE")
- }
- return home
- }
- return os.Getenv("HOME")
-}
-
-func absPathify(inPath string) string {
- jww.INFO.Println("Trying to resolve absolute path to", inPath)
-
- if strings.HasPrefix(inPath, "$HOME") {
- inPath = userHomeDir() + inPath[5:]
- }
-
- if strings.HasPrefix(inPath, "$") {
- end := strings.Index(inPath, string(os.PathSeparator))
- inPath = os.Getenv(inPath[1:end]) + inPath[end:]
- }
-
- if filepath.IsAbs(inPath) {
- return filepath.Clean(inPath)
- }
-
- p, err := filepath.Abs(inPath)
- if err == nil {
- return filepath.Clean(p)
- } else {
- jww.ERROR.Println("Couldn't discover absolute path")
- jww.ERROR.Println(err)
- }
- return ""
-}
-
-func Debug() {
+func Debug() { v.Debug() }
+func (v *viper) Debug() {
fmt.Println("Config:")
- pretty.Println(config)
+ pretty.Println(v.config)
fmt.Println("Key/Value Store:")
- pretty.Println(kvstore)
+ pretty.Println(v.kvstore)
fmt.Println("Env:")
- pretty.Println(env)
+ pretty.Println(v.env)
fmt.Println("Defaults:")
- pretty.Println(defaults)
+ pretty.Println(v.defaults)
fmt.Println("Override:")
- pretty.Println(override)
+ pretty.Println(v.override)
fmt.Println("Aliases:")
- pretty.Println(aliases)
+ pretty.Println(v.aliases)
}
+// Intended for testing, will reset all to default settings.
func Reset() {
- configPaths = nil
- configName = "config"
-
- // extensions Supported
+ v = New()
SupportedExts = []string{"json", "toml", "yaml", "yml"}
- configFile = ""
- configType = ""
-
- kvstore = make(map[string]interface{})
- config = make(map[string]interface{})
- override = make(map[string]interface{})
- env = make(map[string]string)
- defaults = make(map[string]interface{})
- aliases = make(map[string]string)
+ SupportedRemoteProviders = []string{"etcd", "consul"}
}
diff --git a/viper_test.go b/viper_test.go
index bc4059f..3795ed9 100644
--- a/viper_test.go
+++ b/viper_test.go
@@ -83,7 +83,7 @@
func TestBasics(t *testing.T) {
SetConfigFile("/tmp/config.yaml")
- assert.Equal(t, "/tmp/config.yaml", getConfigFile())
+ assert.Equal(t, "/tmp/config.yaml", v.getConfigFile())
}
func TestDefault(t *testing.T) {
@@ -95,7 +95,7 @@
SetConfigType("yaml")
r := bytes.NewReader(yamlExample)
- MarshallReader(r, config)
+ MarshallReader(r, v.config)
assert.True(t, InConfig("name"))
assert.False(t, InConfig("state"))
assert.Equal(t, "steve", Get("name"))
@@ -136,7 +136,7 @@
SetConfigType("yml")
r := bytes.NewReader(yamlExample)
- MarshallReader(r, config)
+ MarshallReader(r, v.config)
assert.Equal(t, "steve", Get("name"))
}
@@ -144,7 +144,7 @@
SetConfigType("json")
r := bytes.NewReader(jsonExample)
- MarshallReader(r, config)
+ MarshallReader(r, v.config)
assert.Equal(t, "0001", Get("id"))
}
@@ -152,17 +152,17 @@
SetConfigType("toml")
r := bytes.NewReader(tomlExample)
- MarshallReader(r, config)
+ MarshallReader(r, v.config)
assert.Equal(t, "TOML Example", Get("title"))
}
func TestRemotePrecedence(t *testing.T) {
SetConfigType("json")
r := bytes.NewReader(jsonExample)
- MarshallReader(r, config)
+ MarshallReader(r, v.config)
remote := bytes.NewReader(remoteExample)
assert.Equal(t, "0001", Get("id"))
- MarshallReader(remote, kvstore)
+ MarshallReader(remote, v.kvstore)
assert.Equal(t, "0001", Get("id"))
assert.NotEqual(t, "cronut", Get("type"))
assert.Equal(t, "remote", Get("newkey"))
@@ -175,7 +175,7 @@
func TestEnv(t *testing.T) {
SetConfigType("json")
r := bytes.NewReader(jsonExample)
- MarshallReader(r, config)
+ MarshallReader(r, v.config)
BindEnv("id")
BindEnv("f", "FOOD")