Go viper.yaml值环境变量覆盖
我试图在go应用程序中使用Go viper.yaml值环境变量覆盖,go,viper-go,Go,Viper Go,我试图在go应用程序中使用application.yaml文件,其中包含${RMQ\u HOST}值,我想用环境变量覆盖这些值 在application.yaml中,我得到: rmq: test: host: ${RMQ_HOST} port: ${RMQ_PORT} 在我的加载器中,我有: log.Println("Loading config...") viper.SetConfigName("application") vipe
application.yaml
文件,其中包含${RMQ\u 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}
不会被我在环境变量中设置的值替换,并尝试使用此字符串连接到RabbitMQ
amqp://test:test@${RMQ_主机}:${RMQ_端口}/test
而不是
amqp://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
}
这将覆盖集合中找到的所有占位符。更新:
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)
}
我使用此功能扩展了本机yaml解析器,并在上发布了它
用法:
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}
当您想要使用环境变量时,为什么要使用yaml文件?因为yaml文件值映射到环境变量,避免了硬编码键,从而使其在多个部署环境中更具可扩展性。换句话说,尝试实现spring boot功能。那么我可能是您使用viper获取值的方式,您需要阅读文档以了解哪种方式适合您
len(env)==0
应该是len(res)==0
不是常见的解决方案,不适用于结构化配置值,例如map