Gerenciando as variáveis de ambiente do seu projeto Golang
22265 단어 goprogramming
Este artigo não é uma cagação de regra
Sim, as vezes é bom lembrar que o artigo é apenas para fins educativos, para passar o conhecimento adiante e levantar o 토론, não é uma cagação de regra.
오 문제
Por várias vezes eu me deparei com projetos em golang em que tem o seguinte código com o os.Getenv , como no exemplo abaixo.
func dsn() string {
return fmt.Sprintf(
"postgres://%s:%s@%s:%s/%s?sslmode=%s",
os.Getenv("DATABASE_USERNAME"),
os.Getenv("DATABASE_PASSWORD"),
os.Getenv("DATABASE_HOST"),
os.Getenv("DATABASE_PORT"),
os.Getenv("DATABASE_DBNAME"),
os.Getenv("DATABASE_SSL_MODE"),
)
}
Ok, mas qual o problema então? O problema é que esse código está acoplado, o que torna difícil testar, por example. Outro problema é que não seria interessante haver chamadas ao
os.Getenv
espalhado pelo código, tornaria ele difícil de manter, como já aconteceu comigo antes.Uma forma fácil de resolver este problema é justamente a Injeção de dependsência, o que torna muito mais limpo e organizado o código, já que suas funções, structs e outros terão apenas os recursos que precisam para funcionar, então, pela logica, o código acima ficaria como 또는 exemplo abaixo.
func dsn(host, port, user, pass, name, sslmode string) string {
return fmt.Sprintf(
"postgres://%s:%s@%s:%s/%s?sslmode=%s",
user,
pass,
host,
port,
name,
sslmode,
)
}
os.Getenv
?Você no controle das variáveis de ambiente do projeto
Hoje, você pode definir as variáveis de ambiente de duas formas, em um arquivo
.env
ou definindo elas direto no seu shell, então para termos controle sobre isto, iremos usar 2 bibliotecas em Golang disponíveis no GitHub, que são as que estão abaixo .https://github.com/joho/godotenv .
https://github.com/caarlos0/env .
A primeira biblioteca permite carregar as variáveis de ambiente de um arquivo
.env
, enquanto a segunda, permite fazer o parser das variáveis de ambiente para uma struct.Com isto, poderemos ter nossa struct para fazer o gerenciamento dos dados das nossas variáveis de ambiente, como abaixo.
type DbSSLMode string
type Config struct {
ApiEnv string `env:"API_ENV,required"`
ApiHost string `env:"API_HOST,required"`
ApiPort string `env:"API_PORT,required"`
DatabaseHost string `env:"DATABASE_HOST,required"`
DatabasePort string `env:"DATABASE_PORT,required"`
DatabaseUsername string `env:"DATABASE_USERNAME,required"`
DatabasePassword string `env:"DATABASE_PASSWORD,required"`
DatabaseDBName string `env:"DATABASE_DBNAME,required"`
DatabaseSSLMode DbSSLMode `env:"DATABASE_SSL_MODE,required"`
}
참고 que nesta struct temos tags para referenciar a origem dos dados e também quais são obrigatórias e sim, isto é Importante, porque acreditem ou não, já vi APIs quebrarem em produção porque justamente um
os.Getenv
perdido no projeto não teve sua variável deambiente 특파원 definida e isto estava derrubando a API.Caso você tenha um arquivo
.env
, você simplesmente pode carregar ele, como abaixo.const (
ENV_FILE = ".env"
)
func loadEnvFile() error {
if _, err := os.Stat(ENV_FILE); os.IsNotExist(err) {
return nil
}
return godotenv.Load(ENV_FILE)
}
Com isto você pode ter a flexibilidade de usar um arquivo
.env
caso esteja desenvolvendo seu projeto.Por fim, temos constantes e funções para você poder fazer validações que você ache interessante para também não ter surpresinhas quando seu projeto for para produção.
const (
ENV_DEV = "dev"
ENV_STAGING = "staging"
ENV_PROD = "prod"
DB_SSLMODE_DISABLE = "disable"
DB_SSLMODE_ALLOW = "allow"
DB_SSLMODE_PREFER = "prefer"
DB_SSLMODE_REQUIRE = "require"
DB_SSLMODE_VERIFY_CA = "verify-ca"
DB_SSLMODE_VERIFY_FULL = "verify-full"
)
func envModes() []string {
return []string{ENV_DEV, ENV_STAGING, ENV_PROD}
}
func dbSSLModes() []string {
return []string{
DB_SSLMODE_DISABLE,
DB_SSLMODE_ALLOW,
DB_SSLMODE_PREFER,
DB_SSLMODE_REQUIRE,
DB_SSLMODE_VERIFY_CA,
DB_SSLMODE_VERIFY_FULL,
}
}
Agora vamos para a parte interessante, como obter essas os dados das variáveis de ambiente.
func GetConfig() (Config, error) {
if err := loadEnvFile(); err != nil {
return Config{}, err
}
customParsers := map[reflect.Type]env.ParserFunc{
reflect.TypeOf(DbSSLMode("")): func(v string) (interface{}, error) {
for _, sslmode := range dbSSLModes() {
if sslmode == string(v) {
return DbSSLMode(v), nil
}
}
return nil, errors.New(fmt.Sprintf(
`Invalid environment variable "DATABASE_SSL_MODE" %s, available options are: %s`,
v,
strings.Join(dbSSLModes(), ", "),
))
},
}
config := Config{}
if err := env.ParseWithFuncs(&config, customParsers); err != nil {
return Config{}, err
}
if !slices.Contains(envModes(), config.ApiEnv) {
return Config{}, errors.New(fmt.Sprintf(
`Invalid environment variable "API_ENV" %s, available options are: %s`,
config.ApiEnv,
strings.Join(envModes(), ", "),
))
}
return config, nil
}
가장 좋은 정보는 기록 보관소
.env
, caso você tenha ele na raiz do seu projeto, cria um parser customizado caso queira, sendo que no exemplo, é o SSL Mode do banco de dados, que tem opções estritas.Depois disto, basta criar a struct, fazer o parse e pronto! Depois disto você ainda pode fazer validações também, tudo para manter o controle sobre os dados das suas variáveis de ambiente.
실행 또는 프로젝트
Uma vez criado sua struct com as validações, basta chamar a função e tratar o erro, caso algo esteja errado, como nos exemplos abaixo.
config, err := pkg.GetConfig()
if err != nil {
panic(err)
}
dev@Devs-MacBook-Pro ~/source/joubertredrat/go-env-management> go run main.go
panic: env: required environment variable "API_PORT" is not set
dev@Devs-MacBook-Pro ~/source/joubertredrat/go-env-management> go run main.go
panic: Invalid environment variable "API_ENV" testing, available options are: dev, staging, prod
dev@Devs-MacBook-Pro ~/source/joubertredrat/go-env-management> go run main.go
panic: env: parse error on field "DatabaseSSLMode" of type "pkg.DbSSLMode": Invalid environment variable "DATABASE_SSL_MODE" deny, available options are: disable, allow, prefer, require, verify-ca, verify-full
Por fim, corrigidos todas as variáveis de ambiente, você terá uma struct com os dados para usar nas funções, métodos e outros que precisar.
func main() {
config, err := pkg.GetConfig()
if err != nil {
panic(err)
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, dsn(
config.DatabaseHost,
config.DatabasePort,
config.DatabaseUsername,
config.DatabasePassword,
config.DatabaseDBName,
string(config.DatabaseSSLMode),
))
})
listen := fmt.Sprintf("%s:%s", config.ApiHost, config.ApiPort)
fmt.Printf("Running app %s at %s\n", config.ApiEnv, listen)
if err := http.ListenAndServe(listen, nil); err != nil {
panic(err)
}
}
Projeto em ação
예를 들어 Github abaixo에서 모든 기능을 사용할 수 있습니다.
https://github.com/joubertredrat/go-env-management .
Então é isto, espero ter ajudado, até a próxima!
Reference
이 문제에 관하여(Gerenciando as variáveis de ambiente do seu projeto Golang), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/joubertredrat/gerenciando-as-variaveis-de-ambiente-do-seu-projeto-golang-27g1텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)