[英]Go viper .yaml values environment variables override
我試圖在 go 應用程序中有application.yaml
文件,其中包含我想用環境變量覆蓋的${RMQ_HOST}
值。
在application.yaml
我有:
rmq:
test:
host: ${RMQ_HOST}
port: ${RMQ_PORT}
在我的裝載機中,我有:
log.Println("Loading config...")
viper.SetConfigName("application")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
viper.AutomaticEnv()
err := viper.ReadInConfig()
我${RMQ_HOST}
的問題是${RMQ_HOST}
不會被我在環境變量中設置的值替換,並嘗試使用此字符串連接到 RabbitMQ
amqp://test:test@${RMQ_HOST}:${RMQ_PORT}/test
代替
amqp://test:test@test:test/test
Viper 無法在鍵/值對中保留值的占位符,所以我設法用這個代碼片段解決了我的問題:
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
}
這將覆蓋集合中找到的所有占位符。
更新:
我使用此功能擴展了本機 yaml 解析器並將其發布在github 上。
用法:
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)
}
這是application.yaml 的樣子:
port: ${SERVER_PORT}
rabbitmq:
host: ${RMQ_HOST}
port: ${RMQ_PORT}
username: ${RMQ_USERNAME}
password: ${RMQ_PASSWORD}
vhost: test
vhost值將像往常一樣被解析,而用“${”和“}”包圍的所有內容都將被環境變量替換。
您可以在調用“Unmarshal”方法之前顯式替換 env 變量。 假設配置存儲在“Config”變量中,下面的代碼片段應該可以工作。
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)
}
我首先使用正則regexp
替換 ENV 解決了類似的問題,這是我的解決方案:
# config.yaml
DB_URI: ${DB_USER}
和 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
}
和我的輸出:
>>> DB_USER=iamddjsaio go run main.go
{iamddjsaio}
我認為更好的方法是在將配置解碼為結構時使用 Viper 的DecodeHooks概念:
如果你有一個像下面這樣的yaml配置文件:
server:
property: ${AN_ENV_VARIABLE}
然后在解碼鈎子內,在解碼發生時獲取 ENV 變量:
decodeHook := func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
if f.Kind() == reflect.String {
stringData := data.(string)
if strings.HasPrefix(stringData, "${") && strings.HasSuffix(stringData, "}") {
envVarValue := os.Getenv(strings.TrimPrefix(strings.TrimSuffix(stringData, "}"), "${"))
if len(envVarValue) > 0 {
return envVarValue, nil
}
}
}
return data, nil
}
err := viper.Unmarshal(c, viper.DecodeHook(decodeHook))
我認為這可以進一步改進,因為您可能有一個可能需要設置為 int 的傳入屬性,例如。 但是,這個一般概念應該適用於大多數用例。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.