vendor/github.com/spf13/viper/viper.go
changeset 251 1c52a0eeb952
parent 242 2a9ec03fe5a1
child 256 6d9efbef00a9
--- a/vendor/github.com/spf13/viper/viper.go	Wed Sep 18 19:17:42 2019 +0200
+++ b/vendor/github.com/spf13/viper/viper.go	Sun Feb 16 18:54:01 2020 +0100
@@ -3,7 +3,7 @@
 // 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.
+// Viper is an 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.
@@ -23,6 +23,7 @@
 	"bytes"
 	"encoding/csv"
 	"encoding/json"
+	"errors"
 	"fmt"
 	"io"
 	"log"
@@ -30,20 +31,22 @@
 	"path/filepath"
 	"reflect"
 	"strings"
+	"sync"
 	"time"
 
-	yaml "gopkg.in/yaml.v2"
-
 	"github.com/fsnotify/fsnotify"
 	"github.com/hashicorp/hcl"
 	"github.com/hashicorp/hcl/hcl/printer"
 	"github.com/magiconair/properties"
 	"github.com/mitchellh/mapstructure"
-	toml "github.com/pelletier/go-toml"
+	"github.com/pelletier/go-toml"
 	"github.com/spf13/afero"
 	"github.com/spf13/cast"
 	jww "github.com/spf13/jwalterweatherman"
 	"github.com/spf13/pflag"
+	"github.com/subosito/gotenv"
+	"gopkg.in/ini.v1"
+	"gopkg.in/yaml.v2"
 )
 
 // ConfigMarshalError happens when failing to marshal the configuration.
@@ -113,6 +116,14 @@
 	return fmt.Sprintf("Config File %q Not Found in %q", fnfe.name, fnfe.locations)
 }
 
+// ConfigFileAlreadyExistsError denotes failure to write new configuration file.
+type ConfigFileAlreadyExistsError string
+
+// Error returns the formatted error when configuration already exists.
+func (faee ConfigFileAlreadyExistsError) Error() string {
+	return fmt.Sprintf("Config File %q Already Exists", string(faee))
+}
+
 // A DecoderConfigOption can be passed to viper.Unmarshal to configure
 // mapstructure.DecoderConfig options
 type DecoderConfigOption func(*mapstructure.DecoderConfig)
@@ -179,13 +190,15 @@
 	remoteProviders []*defaultRemoteProvider
 
 	// Name of file to look for inside the path
-	configName string
-	configFile string
-	configType string
-	envPrefix  string
+	configName        string
+	configFile        string
+	configType        string
+	configPermissions os.FileMode
+	envPrefix         string
 
 	automaticEnvApplied bool
-	envKeyReplacer      *strings.Replacer
+	envKeyReplacer      StringReplacer
+	allowEmptyEnv       bool
 
 	config         map[string]interface{}
 	override       map[string]interface{}
@@ -208,6 +221,7 @@
 	v := new(Viper)
 	v.keyDelim = "."
 	v.configName = "config"
+	v.configPermissions = os.FileMode(0644)
 	v.fs = afero.NewOsFs()
 	v.config = make(map[string]interface{})
 	v.override = make(map[string]interface{})
@@ -221,12 +235,58 @@
 	return v
 }
 
-// Intended for testing, will reset all to default settings.
+// Option configures Viper using the functional options paradigm popularized by Rob Pike and Dave Cheney.
+// If you're unfamiliar with this style,
+// see https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html and
+// https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis.
+type Option interface {
+	apply(v *Viper)
+}
+
+type optionFunc func(v *Viper)
+
+func (fn optionFunc) apply(v *Viper) {
+	fn(v)
+}
+
+// KeyDelimiter sets the delimiter used for determining key parts.
+// By default it's value is ".".
+func KeyDelimiter(d string) Option {
+	return optionFunc(func(v *Viper) {
+		v.keyDelim = d
+	})
+}
+
+// StringReplacer applies a set of replacements to a string.
+type StringReplacer interface {
+	// Replace returns a copy of s with all replacements performed.
+	Replace(s string) string
+}
+
+// EnvKeyReplacer sets a replacer used for mapping environment variables to internal keys.
+func EnvKeyReplacer(r StringReplacer) Option {
+	return optionFunc(func(v *Viper) {
+		v.envKeyReplacer = r
+	})
+}
+
+// NewWithOptions creates a new Viper instance.
+func NewWithOptions(opts ...Option) *Viper {
+	v := New()
+
+	for _, opt := range opts {
+		opt.apply(v)
+	}
+
+	return v
+}
+
+// Reset is intended for testing, will reset all to default settings.
 // In the public interface for the viper package so applications
 // can use it in their testing as well.
 func Reset() {
 	v = New()
-	SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl"}
+	SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "dotenv", "env", "ini"}
 	SupportedRemoteProviders = []string{"etcd", "consul"}
 }
 
@@ -265,7 +325,7 @@
 }
 
 // SupportedExts are universally supported extensions.
-var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl"}
+var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "dotenv", "env", "ini"}
 
 // SupportedRemoteProviders are universally supported remote providers.
 var SupportedRemoteProviders = []string{"etcd", "consul"}
@@ -276,50 +336,74 @@
 }
 
 func WatchConfig() { v.WatchConfig() }
+
 func (v *Viper) WatchConfig() {
+	initWG := sync.WaitGroup{}
+	initWG.Add(1)
 	go func() {
 		watcher, err := fsnotify.NewWatcher()
 		if err != nil {
 			log.Fatal(err)
 		}
 		defer watcher.Close()
-
 		// we have to watch the entire directory to pick up renames/atomic saves in a cross-platform way
 		filename, err := v.getConfigFile()
 		if err != nil {
-			log.Println("error:", err)
+			log.Printf("error: %v\n", err)
+			initWG.Done()
 			return
 		}
 
 		configFile := filepath.Clean(filename)
 		configDir, _ := filepath.Split(configFile)
+		realConfigFile, _ := filepath.EvalSymlinks(filename)
 
-		done := make(chan bool)
+		eventsWG := sync.WaitGroup{}
+		eventsWG.Add(1)
 		go func() {
 			for {
 				select {
-				case event := <-watcher.Events:
-					// we only care about the config file
-					if filepath.Clean(event.Name) == configFile {
-						if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create {
-							err := v.ReadInConfig()
-							if err != nil {
-								log.Println("error:", err)
-							}
-							if v.onConfigChange != nil {
-								v.onConfigChange(event)
-							}
+				case event, ok := <-watcher.Events:
+					if !ok { // 'Events' channel is closed
+						eventsWG.Done()
+						return
+					}
+					currentConfigFile, _ := filepath.EvalSymlinks(filename)
+					// we only care about the config file with the following cases:
+					// 1 - if the config file was modified or created
+					// 2 - if the real path to the config file changed (eg: k8s ConfigMap replacement)
+					const writeOrCreateMask = fsnotify.Write | fsnotify.Create
+					if (filepath.Clean(event.Name) == configFile &&
+						event.Op&writeOrCreateMask != 0) ||
+						(currentConfigFile != "" && currentConfigFile != realConfigFile) {
+						realConfigFile = currentConfigFile
+						err := v.ReadInConfig()
+						if err != nil {
+							log.Printf("error reading config file: %v\n", err)
 						}
+						if v.onConfigChange != nil {
+							v.onConfigChange(event)
+						}
+					} else if filepath.Clean(event.Name) == configFile &&
+						event.Op&fsnotify.Remove&fsnotify.Remove != 0 {
+						eventsWG.Done()
+						return
 					}
-				case err := <-watcher.Errors:
-					log.Println("error:", err)
+
+				case err, ok := <-watcher.Errors:
+					if ok { // 'Errors' channel is not closed
+						log.Printf("watcher error: %v\n", err)
+					}
+					eventsWG.Done()
+					return
 				}
 			}
 		}()
-
 		watcher.Add(configDir)
-		<-done
+		initWG.Done()   // done initializing the watch in this go routine, so the parent routine can move on...
+		eventsWG.Wait() // now, wait for event loop to end in this go-routine...
 	}()
+	initWG.Wait() // make sure that the go routine above fully ended before returning
 }
 
 // SetConfigFile explicitly defines the path, name and extension of the config file.
@@ -349,6 +433,14 @@
 	return strings.ToUpper(in)
 }
 
+// AllowEmptyEnv tells Viper to consider set,
+// but empty environment variables as valid values instead of falling back.
+// For backward compatibility reasons this is false by default.
+func AllowEmptyEnv(allowEmptyEnv bool) { v.AllowEmptyEnv(allowEmptyEnv) }
+func (v *Viper) AllowEmptyEnv(allowEmptyEnv bool) {
+	v.allowEmptyEnv = allowEmptyEnv
+}
+
 // TODO: should getEnv logic be moved into find(). Can generalize the use of
 // rewriting keys many things, Ex: Get('someKey') -> some_key
 // (camel case to snake case for JSON keys perhaps)
@@ -356,11 +448,14 @@
 // getEnv is a wrapper around os.Getenv which replaces characters in the original
 // key. This allows env vars which have different keys than the config object
 // keys.
-func (v *Viper) getEnv(key string) string {
+func (v *Viper) getEnv(key string) (string, bool) {
 	if v.envKeyReplacer != nil {
 		key = v.envKeyReplacer.Replace(key)
 	}
-	return os.Getenv(key)
+
+	val, ok := os.LookupEnv(key)
+
+	return val, ok && (v.allowEmptyEnv || val != "")
 }
 
 // ConfigFileUsed returns the file used to populate the config registry.
@@ -587,10 +682,9 @@
 //       "foo.bar.baz" in a lower-priority map
 func (v *Viper) isPathShadowedInAutoEnv(path []string) string {
 	var parentKey string
-	var val string
 	for i := 1; i < len(path); i++ {
 		parentKey = strings.Join(path[0:i], v.keyDelim)
-		if val = v.getEnv(v.mergeWithEnvPrefix(parentKey)); val != "" {
+		if _, ok := v.getEnv(v.mergeWithEnvPrefix(parentKey)); ok {
 			return parentKey
 		}
 	}
@@ -631,7 +725,7 @@
 func Get(key string) interface{} { return v.Get(key) }
 func (v *Viper) Get(key string) interface{} {
 	lcaseKey := strings.ToLower(key)
-	val := v.find(lcaseKey)
+	val := v.find(lcaseKey, true)
 	if val == nil {
 		return nil
 	}
@@ -652,6 +746,12 @@
 			return cast.ToString(val)
 		case int32, int16, int8, int:
 			return cast.ToInt(val)
+		case uint:
+			return cast.ToUint(val)
+		case uint32:
+			return cast.ToUint32(val)
+		case uint64:
+			return cast.ToUint64(val)
 		case int64:
 			return cast.ToInt64(val)
 		case float64, float32:
@@ -662,6 +762,8 @@
 			return cast.ToDuration(val)
 		case []string:
 			return cast.ToStringSlice(val)
+		case []int:
+			return cast.ToIntSlice(val)
 		}
 	}
 
@@ -715,6 +817,24 @@
 	return cast.ToInt64(v.Get(key))
 }
 
+// GetUint returns the value associated with the key as an unsigned integer.
+func GetUint(key string) uint { return v.GetUint(key) }
+func (v *Viper) GetUint(key string) uint {
+	return cast.ToUint(v.Get(key))
+}
+
+// GetUint32 returns the value associated with the key as an unsigned integer.
+func GetUint32(key string) uint32 { return v.GetUint32(key) }
+func (v *Viper) GetUint32(key string) uint32 {
+	return cast.ToUint32(v.Get(key))
+}
+
+// GetUint64 returns the value associated with the key as an unsigned integer.
+func GetUint64(key string) uint64 { return v.GetUint64(key) }
+func (v *Viper) GetUint64(key string) uint64 {
+	return cast.ToUint64(v.Get(key))
+}
+
 // GetFloat64 returns the value associated with the key as a float64.
 func GetFloat64(key string) float64 { return v.GetFloat64(key) }
 func (v *Viper) GetFloat64(key string) float64 {
@@ -733,6 +853,12 @@
 	return cast.ToDuration(v.Get(key))
 }
 
+// GetIntSlice returns the value associated with the key as a slice of int values.
+func GetIntSlice(key string) []int { return v.GetIntSlice(key) }
+func (v *Viper) GetIntSlice(key string) []int {
+	return cast.ToIntSlice(v.Get(key))
+}
+
 // GetStringSlice returns the value associated with the key as a slice of strings.
 func GetStringSlice(key string) []string { return v.GetStringSlice(key) }
 func (v *Viper) GetStringSlice(key string) []string {
@@ -776,8 +902,6 @@
 		return err
 	}
 
-	v.insensitiviseMaps()
-
 	return nil
 }
 
@@ -793,8 +917,6 @@
 		return err
 	}
 
-	v.insensitiviseMaps()
-
 	return nil
 }
 
@@ -827,8 +949,11 @@
 
 // UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent
 // in the destination struct.
-func (v *Viper) UnmarshalExact(rawVal interface{}) error {
-	config := defaultDecoderConfig(rawVal)
+func UnmarshalExact(rawVal interface{}, opts ...DecoderConfigOption) error {
+	return v.UnmarshalExact(rawVal, opts...)
+}
+func (v *Viper) UnmarshalExact(rawVal interface{}, opts ...DecoderConfigOption) error {
+	config := defaultDecoderConfig(rawVal, opts...)
 	config.ErrorUnused = true
 
 	err := decode(v.AllSettings(), config)
@@ -837,8 +962,6 @@
 		return err
 	}
 
-	v.insensitiviseMaps()
-
 	return nil
 }
 
@@ -895,7 +1018,7 @@
 func (v *Viper) BindEnv(input ...string) error {
 	var key, envkey string
 	if len(input) == 0 {
-		return fmt.Errorf("BindEnv missing key to bind to")
+		return fmt.Errorf("missing key to bind to")
 	}
 
 	key = strings.ToLower(input[0])
@@ -912,12 +1035,15 @@
 }
 
 // Given a key, find the value.
-// Viper will check in the following order:
-// flag, env, config file, key/value store, default.
+//
 // Viper will check to see if an alias exists first.
+// Viper will then check in the following order:
+// flag, env, config file, key/value store.
+// Lastly, if no value was found and flagDefault is true, and if the key
+// corresponds to a flag, the flag's default value is returned.
+//
 // Note: this assumes a lower-cased key given.
-func (v *Viper) find(lcaseKey string) interface{} {
-
+func (v *Viper) find(lcaseKey string, flagDefault bool) interface{} {
 	var (
 		val    interface{}
 		exists bool
@@ -957,6 +1083,11 @@
 			s = strings.TrimSuffix(s, "]")
 			res, _ := readAsCSV(s)
 			return res
+		case "intSlice":
+			s := strings.TrimPrefix(flag.ValueString(), "[")
+			s = strings.TrimSuffix(s, "]")
+			res, _ := readAsCSV(s)
+			return cast.ToIntSlice(res)
 		default:
 			return flag.ValueString()
 		}
@@ -969,7 +1100,7 @@
 	if v.automaticEnvApplied {
 		// even if it hasn't been registered, if automaticEnv is used,
 		// check any Get request
-		if val = v.getEnv(v.mergeWithEnvPrefix(lcaseKey)); val != "" {
+		if val, ok := v.getEnv(v.mergeWithEnvPrefix(lcaseKey)); ok {
 			return val
 		}
 		if nested && v.isPathShadowedInAutoEnv(path) != "" {
@@ -978,7 +1109,7 @@
 	}
 	envkey, exists := v.env[lcaseKey]
 	if exists {
-		if val = v.getEnv(envkey); val != "" {
+		if val, ok := v.getEnv(envkey); ok {
 			return val
 		}
 	}
@@ -1013,24 +1144,31 @@
 		return nil
 	}
 
-	// last chance: if no other value is returned and a flag does exist for the value,
-	// get the flag's value even if the flag's value has not changed
-	if flag, exists := v.pflags[lcaseKey]; exists {
-		switch flag.ValueType() {
-		case "int", "int8", "int16", "int32", "int64":
-			return cast.ToInt(flag.ValueString())
-		case "bool":
-			return cast.ToBool(flag.ValueString())
-		case "stringSlice":
-			s := strings.TrimPrefix(flag.ValueString(), "[")
-			s = strings.TrimSuffix(s, "]")
-			res, _ := readAsCSV(s)
-			return res
-		default:
-			return flag.ValueString()
+	if flagDefault {
+		// last chance: if no value is found and a flag does exist for the key,
+		// get the flag's default value even if the flag's value has not been set.
+		if flag, exists := v.pflags[lcaseKey]; exists {
+			switch flag.ValueType() {
+			case "int", "int8", "int16", "int32", "int64":
+				return cast.ToInt(flag.ValueString())
+			case "bool":
+				return cast.ToBool(flag.ValueString())
+			case "stringSlice":
+				s := strings.TrimPrefix(flag.ValueString(), "[")
+				s = strings.TrimSuffix(s, "]")
+				res, _ := readAsCSV(s)
+				return res
+			case "intSlice":
+				s := strings.TrimPrefix(flag.ValueString(), "[")
+				s = strings.TrimSuffix(s, "]")
+				res, _ := readAsCSV(s)
+				return cast.ToIntSlice(res)
+			default:
+				return flag.ValueString()
+			}
 		}
+		// last item, no need to check shadowing
 	}
-	// last item, no need to check shadowing
 
 	return nil
 }
@@ -1049,7 +1187,7 @@
 func IsSet(key string) bool { return v.IsSet(key) }
 func (v *Viper) IsSet(key string) bool {
 	lcaseKey := strings.ToLower(key)
-	val := v.find(lcaseKey)
+	val := v.find(lcaseKey, false)
 	return val != nil
 }
 
@@ -1068,8 +1206,8 @@
 	v.envKeyReplacer = r
 }
 
-// Aliases provide another accessor for the same key.
-// This enables one to change a name without breaking the application
+// RegisterAlias creates an alias that provides another accessor for the same key.
+// This enables one to change a name without breaking the application.
 func RegisterAlias(alias string, key string) { v.RegisterAlias(alias, key) }
 func (v *Viper) RegisterAlias(alias string, key string) {
 	v.registerAlias(alias, strings.ToLower(key))
@@ -1224,13 +1362,21 @@
 // MergeConfig merges a new configuration with an existing config.
 func MergeConfig(in io.Reader) error { return v.MergeConfig(in) }
 func (v *Viper) MergeConfig(in io.Reader) error {
-	if v.config == nil {
-		v.config = make(map[string]interface{})
-	}
 	cfg := make(map[string]interface{})
 	if err := v.unmarshalReader(in, cfg); err != nil {
 		return err
 	}
+	return v.MergeConfigMap(cfg)
+}
+
+// MergeConfigMap merges the configuration from the map given with an existing config.
+// Note that the map given may be modified.
+func MergeConfigMap(cfg map[string]interface{}) error { return v.MergeConfigMap(cfg) }
+func (v *Viper) MergeConfigMap(cfg map[string]interface{}) error {
+	if v.config == nil {
+		v.config = make(map[string]interface{})
+	}
+	insensitiviseMap(cfg)
 	mergeMaps(cfg, v.config, nil)
 	return nil
 }
@@ -1248,11 +1394,10 @@
 // SafeWriteConfig writes current configuration to file only if the file does not exist.
 func SafeWriteConfig() error { return v.SafeWriteConfig() }
 func (v *Viper) SafeWriteConfig() error {
-	filename, err := v.getConfigFile()
-	if err != nil {
-		return err
+	if len(v.configPaths) < 1 {
+		return errors.New("missing configuration for 'configPath'")
 	}
-	return v.writeConfig(filename, false)
+	return v.SafeWriteConfigAs(filepath.Join(v.configPaths[0], v.configName+"."+v.configType))
 }
 
 // WriteConfigAs writes current configuration to a given filename.
@@ -1264,15 +1409,18 @@
 // SafeWriteConfigAs writes current configuration to a given filename if it does not exist.
 func SafeWriteConfigAs(filename string) error { return v.SafeWriteConfigAs(filename) }
 func (v *Viper) SafeWriteConfigAs(filename string) error {
+	alreadyExists, err := afero.Exists(v.fs, filename)
+	if alreadyExists && err == nil {
+		return ConfigFileAlreadyExistsError(filename)
+	}
 	return v.writeConfig(filename, false)
 }
 
-func writeConfig(filename string, force bool) error { return v.writeConfig(filename, force) }
 func (v *Viper) writeConfig(filename string, force bool) error {
 	jww.INFO.Println("Attempting to write configuration to file.")
 	ext := filepath.Ext(filename)
 	if len(ext) <= 1 {
-		return fmt.Errorf("Filename: %s requires valid extension.", filename)
+		return fmt.Errorf("filename: %s requires valid extension", filename)
 	}
 	configType := ext[1:]
 	if !stringInSlice(configType, SupportedExts) {
@@ -1281,21 +1429,21 @@
 	if v.config == nil {
 		v.config = make(map[string]interface{})
 	}
-	var flags int
-	if force == true {
-		flags = os.O_CREATE | os.O_TRUNC | os.O_WRONLY
-	} else {
-		if _, err := os.Stat(filename); os.IsNotExist(err) {
-			flags = os.O_WRONLY
-		} else {
-			return fmt.Errorf("File: %s exists. Use WriteConfig to overwrite.", filename)
-		}
+	flags := os.O_CREATE | os.O_TRUNC | os.O_WRONLY
+	if !force {
+		flags |= os.O_EXCL
 	}
-	f, err := v.fs.OpenFile(filename, flags, os.FileMode(0644))
+	f, err := v.fs.OpenFile(filename, flags, v.configPermissions)
 	if err != nil {
 		return err
 	}
-	return v.marshalWriter(f, configType)
+	defer f.Close()
+
+	if err := v.marshalWriter(f, configType); err != nil {
+		return err
+	}
+
+	return f.Sync()
 }
 
 // Unmarshal a Reader into a map.
@@ -1319,7 +1467,7 @@
 		}
 
 	case "hcl":
-		obj, err := hcl.Parse(string(buf.Bytes()))
+		obj, err := hcl.Parse(buf.String())
 		if err != nil {
 			return ConfigParseError{err}
 		}
@@ -1337,6 +1485,15 @@
 			c[k] = v
 		}
 
+	case "dotenv", "env":
+		env, err := gotenv.StrictParse(buf)
+		if err != nil {
+			return ConfigParseError{err}
+		}
+		for k, v := range env {
+			c[k] = v
+		}
+
 	case "properties", "props", "prop":
 		v.properties = properties.NewProperties()
 		var err error
@@ -1352,6 +1509,23 @@
 			// set innermost value
 			deepestMap[lastKey] = value
 		}
+
+	case "ini":
+		cfg := ini.Empty()
+		err := cfg.Append(buf.Bytes())
+		if err != nil {
+			return ConfigParseError{err}
+		}
+		sections := cfg.Sections()
+		for i := 0; i < len(sections); i++ {
+			section := sections[i]
+			keys := section.Keys()
+			for j := 0; j < len(keys); j++ {
+				key := keys[j]
+				value := cfg.Section(section.Name()).Key(key.Name()).String()
+				c[section.Name()+"."+key.Name()] = value
+			}
+		}
 	}
 
 	insensitiviseMap(c)
@@ -1359,9 +1533,6 @@
 }
 
 // Marshal a map into Writer.
-func marshalWriter(f afero.File, configType string) error {
-	return v.marshalWriter(f, configType)
-}
 func (v *Viper) marshalWriter(f afero.File, configType string) error {
 	c := v.AllSettings()
 	switch configType {
@@ -1377,6 +1548,9 @@
 
 	case "hcl":
 		b, err := json.Marshal(c)
+		if err != nil {
+			return ConfigMarshalError{err}
+		}
 		ast, err := hcl.Parse(string(b))
 		if err != nil {
 			return ConfigMarshalError{err}
@@ -1402,6 +1576,18 @@
 			return ConfigMarshalError{err}
 		}
 
+	case "dotenv", "env":
+		lines := []string{}
+		for _, key := range v.AllKeys() {
+			envName := strings.ToUpper(strings.Replace(key, ".", "_", -1))
+			val := v.Get(key)
+			lines = append(lines, fmt.Sprintf("%v=%v", envName, val))
+		}
+		s := strings.Join(lines, "\n")
+		if _, err := f.WriteString(s); err != nil {
+			return ConfigMarshalError{err}
+		}
+
 	case "toml":
 		t, err := toml.TreeFromMap(c)
 		if err != nil {
@@ -1420,6 +1606,22 @@
 		if _, err = f.WriteString(string(b)); err != nil {
 			return ConfigMarshalError{err}
 		}
+
+	case "ini":
+		keys := v.AllKeys()
+		cfg := ini.Empty()
+		ini.PrettyFormat = false
+		for i := 0; i < len(keys); i++ {
+			key := keys[i]
+			lastSep := strings.LastIndex(key, ".")
+			sectionName := key[:(lastSep)]
+			keyName := key[(lastSep + 1):]
+			if sectionName == "default" {
+				sectionName = ""
+			}
+			cfg.Section(sectionName).Key(keyName).SetValue(Get(key).(string))
+		}
+		cfg.WriteTo(f)
 	}
 	return nil
 }
@@ -1536,13 +1738,6 @@
 	return v.watchKeyValueConfigOnChannel()
 }
 
-func (v *Viper) insensitiviseMaps() {
-	insensitiviseMap(v.config)
-	insensitiviseMap(v.defaults)
-	insensitiviseMap(v.override)
-	insensitiviseMap(v.kvstore)
-}
-
 // Retrieve the first found remote configuration.
 func (v *Viper) getKeyValueConfig() error {
 	if RemoteConfig == nil {
@@ -1573,7 +1768,7 @@
 func (v *Viper) watchKeyValueConfigOnChannel() error {
 	for _, rp := range v.remoteProviders {
 		respc, _ := RemoteConfig.WatchChannel(rp)
-		//Todo: Add quit channel
+		// Todo: Add quit channel
 		go func(rc <-chan *RemoteResponse) {
 			for {
 				b := <-rc
@@ -1609,7 +1804,7 @@
 }
 
 // AllKeys returns all keys holding a value, regardless of where they are set.
-// Nested keys are returned with a v.keyDelim (= ".") separator
+// Nested keys are returned with a v.keyDelim separator
 func AllKeys() []string { return v.AllKeys() }
 func (v *Viper) AllKeys() []string {
 	m := map[string]bool{}
@@ -1623,7 +1818,7 @@
 	m = v.flattenAndMergeMap(m, v.defaults, "")
 
 	// convert set of paths to list
-	a := []string{}
+	a := make([]string, 0, len(m))
 	for x := range m {
 		a = append(a, x)
 	}
@@ -1632,7 +1827,7 @@
 
 // flattenAndMergeMap recursively flattens the given map into a map[string]bool
 // of key paths (used as a set, easier to manipulate than a []string):
-// - each path is merged into a single key string, delimited with v.keyDelim (= ".")
+// - each path is merged into a single key string, delimited with v.keyDelim
 // - if a path is shadowed by an earlier value in the initial shadow map,
 //   it is skipped.
 // The resulting set of paths is merged to the given shadow set at the same time.
@@ -1672,7 +1867,7 @@
 func (v *Viper) mergeFlatMap(shadow map[string]bool, m map[string]interface{}) map[string]bool {
 	// scan keys
 outer:
-	for k, _ := range m {
+	for k := range m {
 		path := strings.Split(k, v.keyDelim)
 		// scan intermediate paths
 		var parentKey string
@@ -1735,6 +1930,12 @@
 	}
 }
 
+// SetConfigPermissions sets the permissions for the config file.
+func SetConfigPermissions(perm os.FileMode) { v.SetConfigPermissions(perm) }
+func (v *Viper) SetConfigPermissions(perm os.FileMode) {
+	v.configPermissions = perm.Perm()
+}
+
 func (v *Viper) getConfigType() string {
 	if v.configType != "" {
 		return v.configType
@@ -1775,6 +1976,12 @@
 		}
 	}
 
+	if v.configType != "" {
+		if b, _ := exists(v.fs, filepath.Join(in, v.configName)); b {
+			return filepath.Join(in, v.configName)
+		}
+	}
+
 	return ""
 }