问题
I'm trying to have application.yaml
file in go application which contains ${RMQ_HOST}
values which I want to override with environment variables.
In application.yaml
I've got:
rmq:
test:
host: ${RMQ_HOST}
port: ${RMQ_PORT}
And in my loader I have:
log.Println("Loading config...")
viper.SetConfigName("application")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
viper.AutomaticEnv()
err := viper.ReadInConfig()
The problem I have is ${RMQ_HOST}
won't get replaced by the values I've set in my environment variables, and tries to connect to the RabbitMQ with this string
amqp://test:test@${RMQ_HOST}:${RMQ_PORT}/test
instead of
amqp://test:test@test:test/test
回答1:
Update:
I extended the native yaml parser with this functionality and released it on github.
Usage:
type Config struct {
Port string `yaml:"port"`
RabbitMQ RabbitMQ `yaml:"rabbitmq"`
}
type RabbitMQ struct {
Host string `yaml:"host"`
Port string `yaml:"port"`
Username string `yaml:"username"`
Password string `yaml:"password"`
Vhost string `yaml:"vhost"`
}
func main() {
var config Config
file, err := ioutil.ReadFile("application.yaml")
if err != nil {
panic(err)
}
yaml.Unmarshal(file, &config)
spew.Dump(config)
}
This is how application.yaml looks like:
port: ${SERVER_PORT}
rabbitmq:
host: ${RMQ_HOST}
port: ${RMQ_PORT}
username: ${RMQ_USERNAME}
password: ${RMQ_PASSWORD}
vhost: test
vhost value will get parsed as usual, while everything surrounded with "${" and "}" will get replaced with environment variables.
回答2:
Viper doesn't have the ability to keep placeholders for values in key/value pairs, so I've managed to solve my issue with this code snippet:
log.Println("Loading config...")
viper.SetConfigName("application")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
err := viper.ReadInConfig()
if err != nil {
panic("Couldn't load configuration, cannot start. Terminating. Error: " + err.Error())
}
log.Println("Config loaded successfully...")
log.Println("Getting environment variables...")
for _, k := range viper.AllKeys() {
value := viper.GetString(k)
if strings.HasPrefix(value, "${") && strings.HasSuffix(value, "}") {
viper.Set(k, getEnvOrPanic(strings.TrimSuffix(strings.TrimPrefix(value,"${"), "}")))
}
}
func getEnvOrPanic(env string) string {
res := os.Getenv(env)
if len(res) == 0 {
panic("Mandatory env variable not found:" + env)
}
return res
}
This will overwrite all the placeholders found in the collection.
回答3:
I resolved similar question by Using regexp
to replace ENV firstly, here is my solutions:
# config.yaml
DB_URI: ${DB_USER}
and main.go:
package main
import (
"fmt"
"os"
"regexp"
"github.com/spf13/viper"
)
type DBCfg struct {
DBURI string `mapstructure:"DB_URI"`
}
func main() {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err != nil {
panic(fmt.Errorf("Failed to read config"))
}
for _, key := range viper.AllKeys() {
value := viper.GetString(key)
envOrRaw := replaceEnvInConfig([]byte(value))
viper.Set(key, string(envOrRaw))
}
var config DBCfg
if err := viper.Unmarshal(&config); err != nil {
panic(fmt.Errorf("failed to load"))
}
fmt.Println(config)
}
func replaceEnvInConfig(body []byte) []byte {
search := regexp.MustCompile(`\$\{([^{}]+)\}`)
replacedBody := search.ReplaceAllFunc(body, func(b []byte) []byte {
group1 := search.ReplaceAllString(string(b), `$1`)
envValue := os.Getenv(group1)
if len(envValue) > 0 {
return []byte(envValue)
}
return []byte("")
})
return replacedBody
}
and my output:
>>> DB_USER=iamddjsaio go run main.go
{iamddjsaio}
回答4:
You can explicitly substitute the env variables before calling the "Unmarshal" method. Assuming the configuration is stored in "Config" variable, the following code snippet should work.
log.Println("Loading config...")
viper.SetConfigName("application")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err != nil {
fmt.Fprintf("Error reading config file %s\n", err)
}
for _, k := range viper.AllKeys() {
v := viper.GetString(k)
viper.Set(k, os.ExpandEnv(v))
}
if err := viper.Unmarshal(&Config); err != nil {
fmt.Fprintf("Unable to decode into struct %s\n", err)
}
来源:https://stackoverflow.com/questions/52756532/go-viper-yaml-values-environment-variables-override