背景
ではconfig.yamlを読み込ませてサーバを起動するのですが、その中にSlackのAPIトークンを入れる箇所がありました。
config.yaml自体はConfigMapで渡しているのですが、中に記述されているAPIトークンは環境変数でSecretで管理できないかなぁと思ったのがきっかけです。
環境
- go 1.18.2
対応方法
os.ExpandEnv()を使うと文字列に環境変数の値を反映できます。
なのでYAMLを読み込んだ後にos.ExpandEnv()を実行すればOKです。
os.ExpandEnvの挙動
以下のように環境変数を文字列に展開することができます。
func main() { os.Setenv("ID", "gopher") os.Setenv("PASSWORD", "qwertyuiop") fmt.Println(os.ExpandEnv("{ID: $ID, PW: ${PASSWORD}}")) }
https://go.dev/play/p/fs5Xz9IpTcQ
{ID: gopher, PW: qwertyuiop}
環境変数の記法
環境変数の記法としては
- $VAR
- ${VAR}
のどちらの記法も対応しています。
YAMLファイルの場合
では実際にyamlファイルを扱うときの書き方です。
.yamlの修正
元々ベタ書きだった箇所を
token: "some_token" channel: "#general"
以下のように環境変数の記法に書き換えます。
token: ${API_TOKEN} channel: "#general"
goの実装
yamlファイルを読み込んだあとでos.ExpandEnv()
を実行します。
package main import ( "flag" "fmt" "io/ioutil" "log" "os" "gopkg.in/yaml.v2" ) func main() { var fp string flag.StringVar(&fp, "c", "./config.yaml", "set yaml file path") flag.Parse() b, err := ioutil.ReadFile(fp) if err != nil { log.Fatal(err) } expaneded := os.ExpandEnv(string(b)) // here cfg := Config{} if err := yaml.Unmarshal([]byte(expaneded), &cfg); err != nil { log.Fatal(err) } fmt.Printf("%#v\n", cfg) } type Config struct { Token string `yaml:"token"` Channel string `yaml:"channel"` }
動作確認
環境変数を渡さないと空のままですが、
$ go run main.go main.Config{Token:"", Channel:"#general"}
環境変数を設定すると反映されます。
$ API_TOKEN=foobar go run main.go main.Config{Token:"foobar", Channel:"general"}
その他
docker-composeも対応してる
Environment variables in Compose | Docker Documentation
にあるように環境変数対応しています。
YAMLファイル内で$記号を使いたい時は?
docker-composeでは$$
を使えばエスケープできるとありますが、標準パッケージのos.ExpandEnv()
はそれをサポートしていません。
回避策としては以下のように別の環境変数の値として$
を定義し、置換していく実装があります。
func main() { os.Setenv("ID", "gopher") os.Setenv("PASSWORD", "qwertyuiop") fmt.Println(expandEnv("{ID: $ID, PW: ${PASSWORD}, Money: $$100}")) } func expandEnv(s string) string { os.Setenv("ESCAPE_DOLLAR", "$") return os.ExpandEnv(strings.Replace(s, "$$", "${ESCAPE_DOLLAR}", -1)) }
https://go.dev/play/p/EO3bnWm-Eu9
{ID: gopher, PW: qwertyuiop, Money: $100}
まとめ
yamlやjsonを使った設定ファイルは多いのでos.ExpandEnv()
を追加するだけで環境変数の注入が行えるのは非常に便利ですね。