spf13 / viper

Go configuration with fangs
MIT License
27.32k stars 2.02k forks source link

Incosistant merging with GetStringMapString and environment variables #708

Open mdaffin opened 5 years ago

mdaffin commented 5 years ago

When getting a value with GetStringMapString it produced a different value to AllSettings and Get/GetString when the value is set with an environment variable.


import "strings"
import "log"
import "github.com/spf13/viper"

func main() {
    viper.SetEnvPrefix("test")
    viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
    viper.AutomaticEnv()

    viper.SetDefault("some.key", "default")

    log.Printf("AllSettings: %#v", viper.AllSettings())

    log.Printf("GetStringMapString('some'): %#v", viper.GetStringMapString("some"))
    log.Printf("GetString('some.key'): %#v", viper.GetString("some.key"))
    log.Printf("Get('some.key'): %#v", viper.Get("some.key"))
}

When executing produces:

$ TEST_SOME_KEY=env go run main.go
2019/05/28 12:27:01 AllSettings: map[string]interface {}{"some":map[string]interface {}{"key":"env"}}
2019/05/28 12:27:01 GetStringMapString('some'): map[string]string{"key":"default"}
2019/05/28 12:27:01 GetString('some.key'): "env"
2019/05/28 12:27:01 Get('some.key'): "env"

When I expected the GetStringMapString to output the same value as GetString, Get and AllSettings, instead it uses the default value.

When you throw a config into the mix you get the value from the config rather than the default:

package main

import "strings"
import "log"
import "fmt"  
import "github.com/spf13/viper"

func main() {
    viper.SetEnvPrefix("test")
    viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
    viper.AutomaticEnv()

    viper.SetDefault("some.key", "default")

    viper.SetConfigFile("./config.yml")  
    err := viper.ReadInConfig()  
    if err != nil {  
            log.Fatal(fmt.Errorf("fatal error config file: %s", err))
    }    

    log.Printf("AllSettings: %#v", viper.AllSettings())

    log.Printf("GetStringMapString('some'): %#v", viper.GetStringMapString("some"))
    log.Printf("GetString('some.key'): %#v", viper.GetString("some.key"))
    log.Printf("Get('some.key'): %#v", viper.Get("some.key"))
}
$ TEST_SOME_KEY=env go run main.go
2019/05/28 12:28:26 AllSettings: map[string]interface {}{"some":map[string]interface {}{"key":"env"}}
2019/05/28 12:28:26 GetStringMapString('some'): map[string]string{"key":"config"}
2019/05/28 12:28:26 GetString('some.key'): "env"
2019/05/28 12:28:26 Get('some.key'): "env"
safaci2000 commented 2 years ago

I just ran into that as well. It would be really nice if the configmap supported all the various overrides as well.

Noksa commented 2 years ago

Up!

rikkuness commented 2 years ago

I pushed PR #1337 to fix this, but until it's merged as a workaround I'm using a helper function that seems to work okay.

func GetStringMapFixed(conf *viper.Viper, key string) map[string]interface{} {
    out := make(map[string]interface{})
    for _, k := range conf.AllKeys() {
        if strings.HasPrefix(k, key) {
            out[strings.TrimPrefix(k, key+".")] = conf.Get(k)
        }
    }
    return out
}
Ran-Xing commented 2 years ago

@rikkuness Thanks Very Much , +1 I found that if key=abc then if k = abcdefg will also match

Ran-Xing commented 2 years ago
            /* TODO GetStringMap error */
            key := "group"
            out := make(map[string]interface{})
            v1 := viper.AllKeys()
            for _, k := range v1 {
                fmt.Println(k)
                if !strings.Contains(k, ".") {
                    continue
                }
                lastInd := strings.LastIndex(k, ".")
                if k[:lastInd] == key {
                    //if strings.HasPrefix(, key) {
                    out[strings.TrimPrefix(k, key+".")] = viper.Get(k)
                }
            }
            //out := viper.GetStringMap("group")
            /* https://github.com/spf13/viper/issues/708 */
Ran-Xing commented 2 years ago
image image

why? what happened