在YAML文件中使用环境变量

huangapple go评论93阅读模式
英文:

Use environment variables in YAML file

问题

我有一个生成 Pager Duty 值班报告的 GO 脚本,它有自己的 config.yaml 文件,如下所示:

# PagerDuty 授权令牌
pdAuthToken: 12345

# 显式设置报告时间范围(RFC822)
reportTimeRange:
  start: 01 Jan 20 00:00 UTC
  end: 01 Feb 20 00:00 UTC

# 值班轮换的一般信息
rotationInfo:
  dailyRotationStartsAt: 8
  checkRotationChangeEvery: 30 # 分钟

我需要在 config.yaml 文件中传递环境变量。我尝试使用 ${THE_VARIABLE},如下所示:

reportTimeRange:
    start: ${THE_VARIABLE}

有人可以帮忙解决如何在不编辑脚本的情况下,在 config.yaml 文件中传递 Linux 环境变量吗?

英文:

I have a GO script that generates pager duty on call reporting, and it has its own config.yaml file as such:

# PagerDuty auth token
pdAuthToken: 12345

# Explicitly set report time range (RFC822)
reportTimeRange:
  start: 01 Jan 20 00:00 UTC
  end: 01 Feb 20 00:00 UTC

# Rotation general information
rotationInfo:
  dailyRotationStartsAt: 8
  checkRotationChangeEvery: 30 # minutes

I need to pass environment variables in the config.yaml file. I tried to use ${THE_VARIABLE} as such:

reportTimeRange:
    start: ${THE_VARIABLE}

Can anyone help on how I can passmy Linux environment variables in the config.yaml file without the need of editing the script.

答案1

得分: 1

在解组 YAML 文件后,您可以对结果使用 reflect,以更新任何值与您选择的变量格式匹配的 string 字段。

var reVar = regexp.MustCompile(`^${(\w+)}$`)

func fromenv(v interface{}) {
	_fromenv(reflect.ValueOf(v).Elem())// 假设是指向结构体的指针
}

// 递归函数
func _fromenv(rv reflect.Value) {
	for i := 0; i < rv.NumField(); i++ {
		fv := rv.Field(i)
		if fv.Kind() == reflect.Ptr {
			fv = fv.Elem()
		}
		if fv.Kind() == reflect.Struct {
			_fromenv(fv)
			continue
		}
		if fv.Kind() == reflect.String {
			match := reVar.FindStringSubmatch(fv.String())
			if len(match) > 1 {
				fv.SetString(os.Getenv(match[1]))
			}
		}
	}
}

或者,您可以声明实现 yaml.Unmarshaler 接口的类型,并将这些类型用于配置结构体中的字段,这些字段期望相应的 YAML 属性包含环境变量。

type Config struct {
	ReportTimeRange struct {
		Start StringFromEnv `yaml:"start"`
	} `yaml:"reportTimeRange"`
}

var reVar = regexp.MustCompile(`^${(\w+)}$`)

type StringFromEnv string

func (e *StringFromEnv) UnmarshalYAML(value *yaml.Node) error {
	var s string
	if err := value.Decode(&s); err != nil {
		return err
	}
	if match := reVar.FindStringSubmatch(s); len(match) > 0 {
		*e = StringFromEnv(os.Getenv(match[1]))
	} else {
		*e = StringFromEnv(s)
	}
	return nil
}
英文:

After unmarshaling the yaml file you can use reflect on the result to update any string fields whose value matches variable format of your choice.

var reVar = regexp.MustCompile(`^${(\w+)}$`)

func fromenv(v interface{}) {
	_fromenv(reflect.ValueOf(v).Elem())// assumes pointer to struct
}

// recursive
func _fromenv(rv reflect.Value) {
	for i := 0; i &lt; rv.NumField(); i++ {
		fv := rv.Field(i)
		if fv.Kind() == reflect.Ptr {
			fv = fv.Elem()
		}
		if fv.Kind() == reflect.Struct {
			_fromenv(fv)
			continue
		}
		if fv.Kind() == reflect.String {
			match := reVar.FindStringSubmatch(fv.String())
			if len(match) &gt; 1 {
				fv.SetString(os.Getenv(match[1]))
			}
		}
	}
}

https://play.golang.org/p/1zuK7Mhtvsa


Alternatively, you could declare types that implement the yaml.Unmarshaler interface and use those types for fields in the config struct that expect the corresponding yaml properties to contain environment variables.

type Config struct {
	ReportTimeRange struct {
		Start StringFromEnv `yaml:&quot;start&quot;`
	} `yaml:&quot;reportTimeRange&quot;`
}

var reVar = regexp.MustCompile(`^${(\w+)}$`)

type StringFromEnv string

func (e *StringFromEnv) UnmarshalYAML(value *yaml.Node) error {
	var s string
	if err := value.Decode(&amp;s); err != nil {
		return err
	}
	if match := reVar.FindStringSubmatch(s); len(match) &gt; 0 {
		*e = StringFromEnv(os.Getenv(match[1]))
	} else {
		*e = StringFromEnv(s)
	}
	return nil
}

https://play.golang.org/p/Zy0rXJ7RRdC

答案2

得分: 0

我理解的是,你想在程序启动时将占位符{{ .THE_VARIABLE }}替换为内存中的环境变量,而不修改yaml文件。

思路是将yaml文件加载到一个变量中,使用template.Execute替换数据。最后解析字符串。

我只是保持简单。

YAML文件:

Start: {{ .THE_VARIABLE }}

替换数据:

fileData, _ := ioutil.ReadFile("test.yml")
var finalData bytes.Buffer
t := template.New("config")
t, err := t.Parse(string(fileData))
if err != nil {
    panic(err)
}

data := struct {
    THE_VARIABLE int
}{
    THE_VARIABLE: 30,  // 替换为 os.Getenv("FOO")
}
t.Execute(&finalData, data)
str := finalData.String()
fmt.Println(str)
// 在这里解析YAML - 使用 finalData.Bytes()

输出:

Start: 30

英文:

What I understand is that you want to replace the placeholder {{ .THE_VARIABLE }} with a environment var at program start in memory, without modifying the yaml file.

The idea is to load the yaml file into a var, use template.Execute to replace the data. Finally Unmarshal the string.

I just kept it simple.

YAML FILE:

Start: {{ .THE_VARIABLE }}

Replacing data:

fileData, _ := ioutil.ReadFile(&quot;test.yml&quot;)
var finalData bytes.Buffer
t := template.New(&quot;config&quot;)
t, err := t.Parse(string(fileData))
if err != nil {
	panic(err)
}

data := struct {
	THE_VARIABLE int
}{
	THE_VARIABLE: 30,  // replace with os.Getenv(&quot;FOO&quot;)
}
t.Execute(&amp;finalData, data)
str := finalData.String()
fmt.Println(str)
// unmarshal YAML here - from finalData.Bytes()

Output:
Start: 30

huangapple
  • 本文由 发表于 2021年10月21日 14:58:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/69657289.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定