Naftuli Kay
Naftuli Kay

Reputation: 91660

Viper Automatic Environment Does Not Read from Environment

I'm trying to complete a very simple example wherein I use viper to load configuration from environment variables. I have installed it in my project via go get github.com/spf13/viper, and the dependency is in my go.mod:

module github.com/naftulikay/viper-demo

go 1.16

require github.com/spf13/viper v1.7.1 // indirect

Here is my code, which does compile and run:

package main

import (
    "fmt"
    "github.com/spf13/viper"
)

var config Config

type Config struct {
    MyString string //`mapstructure:"MY_STRING"`
}

func main() {
    v := viper.New()
    v.AutomaticEnv()

    var err error

    err = v.Unmarshal(&config)

    if err != nil {
        fmt.Printf("Unable to load config: %s\n", err)
    }

    fmt.Printf("%+v\n", &config)
}

I am setting environment variables to try to test different permutations of the environment variable name

MY_STRING=a my_string=b MyString=c ./viper-demo

I've tried a lot of different things, removing the mapstructure annotation, changing its value, etc., but all I get when I execute is:

&{MyString:}

I'm following the instructions from the README as well as other demos I've found online and it just seems like viper does not load environment variables.

I feel like this is a very simple example, my requirements are mapping config to a struct, and fetching that config from environment variables. I may eventually start doing CLI flags and a config file, but environment variables are make-or-break for me, I need them to work.

What am I missing?

Upvotes: 5

Views: 10926

Answers (3)

Froz01
Froz01

Reputation: 1

You could do something like this

package main

import (
    "reflect"
    "github.com/spf13/viper"
)

func automaticBindEnv() {
    v := reflect.ValueOf(&Config{})
    t := v.Elem().Type()
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        env := field.Tag.Get("mapstructure")
        if env == "" {
            continue
        }
        viper.BindEnv(env)
    }
}

Upvotes: 0

Wumms
Wumms

Reputation: 8186

A fix has been merged since Dec 2023. You have to enable the feature though [1], e.g.:

go build -tags=viper_bind_struct ...

  1. https://github.com/spf13/viper/pull/1429#issuecomment-1870976604

Upvotes: 1

Zombo
Zombo

Reputation: 1

This seems to be a long running issue, for several years now [1]. However, in that thread it is suggested that you can use BindEnv [2]:

package main

import (
    "fmt"
    "github.com/spf13/viper"
)

func main() {
   var c struct {
      TMP string
   }
   v := viper.New()
   v.AutomaticEnv()
   v.BindEnv("TMP")
   v.Unmarshal(&c)
   fmt.Printf("%+v\n", &c)
}

For another approach, you can use os.Environ [3]:

package main

import (
   "fmt"
   "os"
   "strings"
)

func environ() map[string]string {
   m := make(map[string]string)
   for _, s := range os.Environ() {
      a := strings.Split(s, "=")
      m[a[0]] = a[1]
   }
   return m
}

func main() {
   m := environ()
   fmt.Printf("%q\n", m)
}
  1. https://github.com/spf13/viper/issues/188
  2. https://pkg.go.dev/github.com/spf13/viper#Viper.BindEnv
  3. https://golang.org/pkg/os#Environ

Upvotes: 3

Related Questions