Goの環境変数を扱う(viper)


はじめに

golangで環境設定ファイルを簡単にするライブラリとしてviperというものがあります。
今回はそのviperの使い方を簡単に紹介します

viper

シンプルにviperを使ってみる
今回は構造体にマッピングしてそれを引き回して使うように実装を行います

ソースツリー
.
├── config
│   ├── config.yaml
│   └── definition.go
└── main.go
config.yaml
# 環境変数設定用のyamlファイル
user: 
  name: "kosuke"

本来はデータベースのユーザー名等を入れることが多いですが、今回は簡単に上記のような環境変数を設定します。

definition.go
package config

import (
    "fmt"

    "github.com/spf13/viper"
)

// マッピング用の構造体
type Config struct {
    User User   `yaml:user`
}

type User struct {
    Name string `yaml:"name"`
}

func Load() (*Config, error) {
    viper.SetConfigName("config")   // 設定ファイル名を指定
    viper.SetConfigType("yaml")     // 設定ファイルの形式を指定
    viper.AddConfigPath("config/")  // ファイルのpathを指定

    err := viper.ReadInConfig()     // 設定ファイルを探索して読み取る
    if err != nil {            
        return nil, fmt.Errorf("設定ファイル読み込みエラー: %s \n", err)
    }

    var cfg Config

    err = viper.Unmarshal(&cfg)
    if err != nil {
        return nil, fmt.Errorf("unmarshal error: %s \n", err)
    }

    return &cfg, nil
}

Load()と言う関数の中で、環境変数のファイルを探しConfigという構造体に環境変数をマッピングしていきます

main.go
package main

import (
    "fmt"
    "viper-test/config"
)

func main() {
    cfg, err := config.Load()
    if err != nil {
        panic(err)
    }

    fmt.Println(cfg.User.Name)
}

今回は構造体にデータが入っていることを確かめるためにConfigの中身を表示しているだけですが、
本来であればこの構造体を引数に撮ってDBコネクション等を行っていきます。

$ go run main.go
kosuke

また、本番環境で運用等を行うときは外部から環境変数を受けとりたいということがあると思います。
その際は、、、

definition.go
package config

import (
    "fmt"

    "github.com/spf13/viper"
)

// マッピング用の構造体
type Config struct {
    User User   `yaml:user`
}

type User struct {
    Name string `yaml:"name"`
}

func Load() (*Config, error) {
    viper.SetConfigName("config")
    viper.SetConfigType("yaml")
    viper.AddConfigPath("config/")

    // ===========↓追記=============
    // 環境変数がすでに指定されてる場合はそちらを優先させる
    viper.AutomaticEnv()            

    // データ構造をキャメルケースに切り替える用の設定
    viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
    // ===========↑ここまで=============

    err := viper.ReadInConfig()
    if err != nil {            
        return nil, fmt.Errorf("設定ファイル読み込みエラー: %s \n", err)
    }

    var cfg Config

    err = viper.Unmarshal(&cfg)
    if err != nil {
        return nil, fmt.Errorf("unmarshal error: %s \n", err)
    }

    return &cfg, nil
}

上記のような構文を使うことで環境変数が指定された時にはconfigファイルの値ではなく、
環境変数の値を参照することができます

# 環境変数を与えて `go run`
$ USER_NAME=tarou go run main.go
tarou

終わりに

今回はとてもシンプルな使い方でしたが、環境変数を構造体にマッピングして引き回していくのは余計な処理を書かなくて済むのてとても楽かなと思います。
もちろんですが、、、秘匿情報の扱い(AWSのアクセスキーなど)には気をつけて使用していきたいところです。

参考資料