• Stars
    star
    1,037
  • Rank 44,215 (Top 0.9 %)
  • Language
    Go
  • License
    Apache License 2.0
  • Created over 4 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A Go library for parsing struct tags from environment variables.

Envconfig

GoDoc

Envconfig populates struct field values based on environment variables or arbitrary lookup functions. It supports pre-setting mutations, which is useful for things like converting values to uppercase, trimming whitespace, or looking up secrets.

Note: Versions prior to v0.2 used a different import path. This README and examples are for v0.2+.

Usage

Define a struct with fields using the env tag:

type MyConfig struct {
  Port     int    `env:"PORT"`
  Username string `env:"USERNAME"`
}

Set some environment variables:

export PORT=5555
export USERNAME=yoyo

Process it using envconfig:

package main

import (
  "context"
  "log"

  "github.com/sethvargo/go-envconfig"
)

func main() {
  ctx := context.Background()

  var c MyConfig
  if err := envconfig.Process(ctx, &c); err != nil {
    log.Fatal(err)
  }

  // c.Port = 5555
  // c.Username = "yoyo"
}

You can also use nested structs, just remember that any fields you want to process must be public:

type MyConfig struct {
  Database *DatabaseConfig
}

type DatabaseConfig struct {
  Port     int    `env:"PORT"`
  Username string `env:"USERNAME"`
}

Configuration

Use the env struct tag to define configuration.

Required

If a field is required, processing will error if the environment variable is unset.

type MyStruct struct {
  Port int `env:"PORT,required"`
}

It is invalid to have a field as both required and default.

Default

If an environment variable is not set, the field will be set to the default value. Note that the environment variable must not be set (e.g. unset PORT). If the environment variable is the empty string, that counts as a "value" and the default will not be used.

type MyStruct struct {
  Port int `env:"PORT,default=5555"`
}

You can also set the default value to another field or value from the environment, for example:

type MyStruct struct {
  DefaultPort int `env:"DEFAULT_PORT,default=5555"`
  Port        int `env:"OVERRIDE_PORT,default=$DEFAULT_PORT"`
}

The value for Port will default to the value of DEFAULT_PORT.

It is invalid to have a field as both required and default.

Prefix

For shared, embedded structs, you can define a prefix to use when processing struct values for that embed.

type SharedConfig struct {
  Port int `env:"PORT,default=5555"`
}

type Server1 struct {
  // This processes Port from $FOO_PORT.
  *SharedConfig `env:",prefix=FOO_"`
}

type Server2 struct {
  // This processes Port from $BAR_PORT.
  *SharedConfig `env:",prefix=BAR_"`
}

It is invalid to specify a prefix on non-struct fields.

Overwrite

If overwrite is set, the value will be overwritten if there is an environment variable match regardless if the value is non-zero.

type MyStruct struct {
  Port int `env:"PORT,overwrite"`
}

The rules for overwrite + default are:

  • If the struct field has the zero value and a default is set:

    • If no environment variable is specified, the struct field will be populated with the default value.

    • If an environment variable is specified, the struct field will be populate with the environment variable value.

  • If the struct field has a non-zero value and a default is set:

    • If no environment variable is specified, the struct field's existing value will be used (the default is ignored).

    • If an environment variable is specified, the struct field's existing value will be overwritten with the environment variable value.

Complex Types

Note: Complex types are only decoded or unmarshalled when the environment variable is defined or a default is specified. The decoding/unmarshalling functions are not invoked when a value is not defined.

Durations

In the environment, time.Duration values are specified as a parsable Go duration:

type MyStruct struct {
  MyVar time.Duration `env:"MYVAR"`
}
export MYVAR="10m" # 10 * time.Minute

TextUnmarshaler / BinaryUnmarshaler

Types that implement TextUnmarshaler or BinaryUnmarshaler are processed as such.

json.Unmarshaler

Types that implement json.Unmarshaler are processed as such.

gob.Decoder

Types that implement gob.Decoder are processed as such.

Slices

Slices are specified as comma-separated values:

type MyStruct struct {
  MyVar []string `env:"MYVAR"`
}
export MYVAR="a,b,c,d" # []string{"a", "b", "c", "d"}

Define a custom delimiter with delimiter:

type MyStruct struct {
  MyVar []string `env:"MYVAR,delimiter=;"`
export MYVAR="a;b;c;d" # []string{"a", "b", "c", "d"}

Note that byte slices are special cased and interpreted as strings from the environment.

Maps

Maps are specified as comma-separated key:value pairs:

type MyStruct struct {
  MyVar map[string]string `env:"MYVAR"`
}
export MYVAR="a:b,c:d" # map[string]string{"a":"b", "c":"d"}

Define a custom delimiter with delimiter:

type MyStruct struct {
  MyVar map[string]string `env:"MYVAR,delimiter=;"`
export MYVAR="a:b;c:d" # map[string]string{"a":"b", "c":"d"}

Define a separator with separator:

type MyStruct struct {
  MyVar map[string]string `env:"MYVAR,separator=|"`
}
export MYVAR="a|b,c|d" # map[string]string{"a":"b", "c":"d"}

Structs

Envconfig walks the entire struct, including nested structs, so deeply-nested fields are also supported.

If a nested struct is a pointer type, it will automatically be instantianted to the non-nil value. To change this behavior, see Initialization.

Custom

You can also define your own decoder.

Prefixing

You can define a custom prefix using the PrefixLookuper. This will lookup values in the environment by prefixing the keys with the provided value:

type MyStruct struct {
  MyVar string `env:"MYVAR"`
}
// Process variables, but look for the "APP_" prefix.
l := envconfig.PrefixLookuper("APP_", envconfig.OsLookuper())
if err := envconfig.ProcessWith(ctx, &c, l); err != nil {
  panic(err)
}
export APP_MYVAR="foo"

Initialization

By default, all pointers, slices, and maps are initialized (allocated) so they are not nil. To disable this behavior, use the tag the field as noinit:

type MyStruct struct {
  // Without `noinit`, DeleteUser would be initialized to the default boolean
  // value. With `noinit`, if the environment variable is not given, the value
  // is kept as uninitialized (nil).
  DeleteUser *bool `env:"DELETE_USER, noinit"`
}

This also applies to nested fields in a struct:

type ParentConfig struct {
  // Without `noinit` tag, `Child` would be set to `&ChildConfig{}` whether
  // or not `FIELD` is set in the env var.
  // With `noinit`, `Child` would stay nil if `FIELD` is not set in the env var.
  Child *ChildConfig `env:",noinit"`
}

type ChildConfig struct {
  Field string `env:"FIELD"`
}

The noinit tag is only applicable for pointer, slice, and map fields. Putting the tag on a different type will return an error.

Extension

All built-in types are supported except Func and Chan. If you need to define a custom decoder, implement the Decoder interface:

type MyStruct struct {
  field string
}

func (v *MyStruct) EnvDecode(val string) error {
  v.field = fmt.Sprintf("PREFIX-%s", val)
  return nil
}

If you need to modify environment variable values before processing, you can specify a custom Mutator:

type Config struct {
  Password `env:"PASSWORD"`
}

func resolveSecretFunc(ctx context.Context, key, value string) (string, error) {
  if strings.HasPrefix(value, "secret://") {
    return secretmanager.Resolve(ctx, value) // example
  }
  return value, nil
}

var config Config
envconfig.ProcessWith(ctx, &config, envconfig.OsLookuper(), resolveSecretFunc)

Testing

Relying on the environment in tests can be troublesome because environment variables are global, which makes it difficult to parallelize the tests. Envconfig supports extracting data from anything that returns a value:

lookuper := envconfig.MapLookuper(map[string]string{
  "FOO": "bar",
  "ZIP": "zap",
})

var config Config
envconfig.ProcessWith(ctx, &config, lookuper)

Now you can parallelize all your tests by providing a map for the lookup function. In fact, that's how the tests in this repo work, so check there for an example.

You can also combine multiple lookupers with MultiLookuper. See the GoDoc for more information and examples.

Inspiration

This library is conceptually similar to kelseyhightower/envconfig, with the following major behavioral differences:

  • Adds support for specifying a custom lookup function (such as a map), which is useful for testing.

  • Only populates fields if they contain zero or nil values if overwrite is unset. This means you can pre-initialize a struct and any pre-populated fields will not be overwritten during processing.

  • Support for interpolation. The default value for a field can be the value of another field.

  • Support for arbitrary mutators that change/resolve data before type conversion.

More Repositories

1

ratchet

A tool for securing CI/CD workflows with version pinning.
Go
771
star
2

go-password

A Golang library for generating high-entropy random passwords similar to 1Password or LastPass.
Go
642
star
3

go-retry

Go library for retrying with configurable backoffs
Go
631
star
4

go-limiter

A supersonic rate limiting package for Go with HTTP middleware.
Go
526
star
5

vault-on-gke

Run @HashiCorp Vault on Google Kubernetes Engine (GKE) with Terraform
HCL
497
star
6

go-githubactions

Go SDK for GitHub Actions - easily author GitHub Actions in Go
Go
434
star
7

vault-secrets-gen

A Vault secrets plugin for generating high entropy passwords and passphrases.
Go
338
star
8

go-signalcontext

Create Go contexts that cancel on signals.
Go
260
star
9

bootstrap_forms

Bootstrap Forms makes Twitter's Bootstrap on Rails easy!
Ruby
253
star
10

vault-kubernetes-authenticator

An app and container for authenticating services to @HashiCorp Vault's via the Kubernetes auth method
Go
205
star
11

powify

Powify is an easy-to-use wrapper for 37 signal's pow
Ruby
189
star
12

vault-kubernetes-workshop

Steps and scripts for running @HashiCorp Vault on @GoogleCloudPlatform Kubernetes
Shell
154
star
13

chef-sugar

143
star
14

terraform-provider-googlecalendar

A @HashiCorp Terraform provider for managing Google Calendar events.
Go
136
star
15

go-diceware

Golang library for generating passphrases via the diceware algorithm.
Go
99
star
16

secrets-in-serverless

A collection of examples for doing secrets management in serverless lambda or cloud functions.
Go
91
star
17

vault-init

Automate the initialization and unsealing of @HashiCorp Vault on @GoogleCloudPlatform
Go
82
star
18

atlantis-on-gke

A set of @HashiCorp Terraform configurations for running Atlantis on @GoogleCloud GKE
HCL
68
star
19

vault-token-helper-osx-keychain

An example @HashiCorp Vault token helper for Mac OS X Keychain.
Go
65
star
20

vault-demo

Walkthroughs and scripts for my @HashiCorp Vault talks
Shell
65
star
21

terraform-provider-filesystem

A @HashiCorp Terraform provider for interacting with the filesystem
Go
62
star
22

hashicorp-installer

Script and Docker container for installing @HashiCorp tools
Shell
50
star
23

vault-auth-slack

A @HashiCorp Vault plugin for authenticating and receiving policies via Slack.
Go
50
star
24

cleanroom

(More) safely evaluate Ruby DSLs with cleanroom
Ruby
44
star
25

go-cache

Cache implementations in Go, with support for generics.
Go
44
star
26

cloud-run-docker-mirror

Mirror images from one Docker repository to another, as a service.
Go
38
star
27

vault-puppet

Using @HashiCorp Vault with Puppet
Shell
36
star
28

gcs-cacher

Utility for saving and restoring caches backed by Google Cloud Storage.
Go
35
star
29

base64-is-not-encryption

Demo repo showing Kubernetes secrets being sad
Shell
31
star
30

fast

A CLI tool for testing download speed using Netflix's fast.com service.
Go
27
star
31

isbndb

Ruby ISBNdb is a simple, Ruby library that connects to ISBNdb.com's Web Service and API.
Ruby
26
star
32

go-redisstore

Go rate limiter interface for Redis
Go
18
star
33

terraform-provider-berglas

A Terraform provider for Berglas
Go
17
star
34

vault-fluentd-configurations

Fluentd configurations for @HashiCorp Vault
17
star
35

go-gcpkms

Wrappers around Google Cloud KMS that implement Go's crypto.Signer and crypto.Verifier interfaces.
Go
16
star
36

now-or-never-resource-optimizer

A resource optimizer for Now or Never, written in Go, compiled to WASM, run in the browser.
Go
16
star
37

go-gcslock

A Go library for acquiring a forward-looking lock in Google Cloud Storage.
Go
14
star
38

terraform-cloud-run-demo

Sample Terraform configurations for creating a publicly accessible Cloud Run service
HCL
13
star
39

vault-token-helper-gcp-kms

A @HashiCorp Vault token helper for encrypting/decrypting via @GoogleCloudPlatform KMS
Go
12
star
40

kubecon18

Scripts and demo for my Kubecon 2018 talk
HCL
10
star
41

go-malice

A malicious package to demonstrate the importance of software supply chain security.
Go
7
star
42

zapw

Finds common errors for the zap logger using static analysis.
Go
7
star
43

powify.dev

The official web-management tool for Powify
Ruby
7
star
44

envjector

Exec a subprocess with an environment specified in a file. Like env, but a single static binary across multiple operating systems.
Go
6
star
45

go-hello-githubactions

Sample code for GitHub Actions with Go
Dockerfile
5
star
46

gcpkms-rand

Use Google Cloud KMS as an io.Reader and rand.Source.
Go
5
star
47

serverless-secrets-talk

Demo script and code for secrets in serverless talk.
Go
4
star
48

envserver

A webserver that prints the environment it was spawned in
Go
3
star
49

community-zero

3
star
50

spellingbee

A small Go program to generate solutions to the NYT Spelling Bee.
Go
2
star
51

cloud-run-empathy-sms-hello-world

Sample code for sending text messages via Twilio on Cloud Run.
Python
2
star
52

go-redisstore-opencensus

Go rate limiter interface for Redis with instrumentation.
Go
2
star
53

chatty

Go
1
star
54

terraform-secret-manager-demo

HCL
1
star
55

terraform-0.13-google-cloud-demo

Sample for Terraform 0.13 unique features on Google Cloud
HCL
1
star
56

docker-postgres-pgaudit

A Docker image for postgres with pgAudit available
Makefile
1
star