Golang centralized configuration with Viper

นาย เค
TakoDigital
Published in
2 min readApr 4, 2024
Image generated by Leonardo.ai

สวัสดีครับ และแล้วเราก็มาถึงตอนที่ต้องใช้ centralized configuration กันสักทีหลังจากที่เกริ่นกันมายาวนานมากเกือบปี library ที่ผมจะมาแนะนำในการอ่าน configuration ที่นิยมใน Golang ก็มี Viper นี่แหละครับ

Viper คือ Go-based configuration framework ซึ่งรองรับ config type ได้อย่างหลากหลายมาก เช่น json, yaml และ properties และยังสามารถอ่านค่า configuration ได้จากหลากหลาย environment ยกเว้น loading configuration จาก Spring Cloud (ก็เราทำโดยเริ่มจาก Java Spring นี่นะ เลยต้องนำ Spring Cloud เข้ามาใช้ด้วย จึงมี centralized configuration จากบทความ https://medium.com/takodigital/golang-clean-architecture-with-gin-and-gorm-ce2d89ddb806) ดังนั้นเราจึงต้องออกแรงบางอย่างเพื่อรับ Config จาก Centralize Configuration

ใน .env เราจะเพิ่ม config ตรงนี้

APP_ENV=development
PROFILE=default
CONTEXT_PATH=go-service-client
CLOUD_CONFIG_URL=http://localhost:8100/go-centralize-configuration

ที่ env.go เราก็จะเพิ่ม Property สำหรับค่าที่อ่านได้มาจาก centralized configuration

type property struct {
Id uint `json:"id"`
Application string `json:"application"`
Profile string `json:"profile"`
Key string `json:"key"`
Value string `json:"value"`
}

struct สำหรับเก็บค่า CLOUD_CONFIG_URL จะเป็นดังนี้

type Env struct {
AppEnv string `mapstructure:"APP_ENV"`
Profile string `mapstructure:"PROFILE"`
ContextPath string `mapstructure:"CONTEXT_PATH"`
ContextTimeout int `mapstructure:"CONTEXT_TIMEOUT"`
CloudConfigUrl string `mapstructure:"CLOUD_CONFIG_URL"`
Properties map[string]interface{}
}

เมื่อเราต้องการรวมทุกอย่างไปเก็บไว้ใน env เราก็จะรวมงานทั้งหมดไว้ใน function NewEnv ตามนี้ครับ

func NewEnv() *Env {
env := Env{}
viper.SetConfigFile(".env")

err := viper.ReadInConfig()
if err != nil {
log.Fatal("Can't find the file .env : ", err)
}

err = viper.Unmarshal(&env)
if err != nil {
log.Fatal("Environment can't be loaded: ", err)
}

if env.AppEnv == "development" {
log.Println("The App is running in development env")
}

configUrl := fmt.Sprintf("%s/%s/%s", env.CloudConfigUrl, env.ContextPath, env.Profile)

resp, err := http.Get(configUrl)
if err != nil {
panic("Couldn't load configuration, cannot start. Terminating. Error: " + err.Error())
}
body, err := io.ReadAll(resp.Body)
if err != nil {
panic("Couldn't load configuration, cannot start. Terminating. Error: " + err.Error())
}

var properties []property
json.Unmarshal(body, &properties)
propertyMap := map[string]any{}
for _, property := range properties {
key := property.Key
value := property.Value
propertyMap[key] = value
viper.Set(key, value)
}
env.Properties = propertyMap

return &env
}

ซึ่งจะทำให้ go-service-client อ่าน centralize-configuration ได้

Link ที่เกี่ยวข้อง

Reference

https://callistaenterprise.se/blogg/teknik/2017/05/15/go-blog-series-part8

--

--