envconfig package - github.com/vrischmann/envconfig - Go Packages

Package envconfig implements a configuration reader which reads each value from an environment variable.

The basic idea is that you define a configuration struct, like this:

var conf struct {
    Addr string
    Port int
    Auth struct {
        Key      string
        Endpoint string
    }
    Partitions []int
    Shards     []struct {
        Name string
        Id   int
    }
}

Once you have that, you need to initialize the configuration:

if err := envconfig.Init(&conf); err != nil {
    log.Fatalln(err)
}

Then it's just a matter of setting the environment variables when calling your binary:

ADDR=localhost PORT=6379 AUTH_KEY=foobar ./mybinary

Layout of the conf struct

Your conf struct must follow the following rules:

  • no unexported fields by default (can turn off with Options.AllowUnexported)
  • only supported types (no map fields for example)

Naming of the keys

By default, envconfig generates all possible keys based on the field chain according to a flexible naming scheme.

The field chain is how you access your field in the configuration struct. For example:

var conf struct {
    Shard struct {
        Name string
    }
}

With that struct, you access the name field via the chain *Shard.Name*

The default naming scheme takes that and transforms it into the following:

  • SHARD_NAME
  • shard_name

It can handles more complicated cases, with multiple words in one field name. It needs to be in the correct case though, for example:

var conf struct {
    Cassandra struct {
        SSLCert string
        SslKey  string
    }
}

With that struct, you access the name field via the chain *Cassandra.SSLCert* or *Cassandra.SslKey*

The default naming scheme takes that and transforms it into the following:

  • CASSANDRA_SSL_CERT, cassandra_ssl_cert, CASSANDRA_SSLCERT, cassandra_sslcert
  • CASSANDRA_SSL_KEY, cassandra_ssl_key, CASSANDRA_SSLKEY, cassandra_sslkey

And, if that is not good enough for you, you always have the option to use a custom key:

var conf struct {
    Cassandra struct {
        Name string `envconfig:"cassandraMyName"`
    }
}

Now envconfig will only ever checks the environment variable _cassandraMyName_.

Content of the variables

There are three types of content for a single variable:

  • for simple types, a single string representing the value, and parseable into the type.
  • for slices or arrays, a comma-separated list of strings. Each string must be parseable into the element type of the slice or array.
  • for structs, a comma-separated list of specially formatted strings representing structs.

Example of a valid slice value:

foo,bar,baz

The format for a struct is as follow:

  • prefixed with {
  • suffixed with }
  • contains a comma-separated list of field values, in the order in which they are defined in the struct

Example of a valid struct value:

type MyStruct struct {
    Name    string
    Id      int
    Timeout time.Duration
}

{foobar,10,120s}

Example of a valid slice of struct values:

{foobar,10,120s},{barbaz,20,50s}

Special case for bytes slices

For bytes slices, you generally don't want to type out a comma-separated list of byte values.

For this use case, we support base64 encoded values.

Here's an example:

var conf struct {
    Data []byte
}

os.Setenv("DATA", "Rk9PQkFS")

This will decode DATA to FOOBAR and put that into conf.Data.

Optional values

Sometimes you don't absolutely need a value. Here's how we tell envconfig a value is optional:

var conf struct {
    Name string `envconfig:"optional"`
}

Skipped fields

Sometimes you want a field to be skipped entirely.

    var conf struct {
	Internal string `envconfig:"-"`
    }

Default values

Often times you have configuration keys which almost never changes, but you still want to be able to change them.

In such cases, you might want to provide a default value.

Here's to do this with envconfig:

var conf struct {
    Timeout time.Duration `envconfig:"default=1m"`
}

Combining options

You can of course combine multiple options. The syntax is simple enough, separate each option with a comma.

For example:

var conf struct {
    Timeout time.Duration `envconfig:"default=1m,myTimeout"`
}

This would give you the default timeout of 1 minute, and lookup the myTimeout environment variable.

Supported types

envconfig supports the following list of types:

  • bool
  • string
  • intX
  • uintX
  • floatX
  • time.Duration
  • pointers to all of the above types

Notably, we don't (yet) support complex types simply because I had no use for it yet.

Custom unmarshaler

When the standard types are not enough, you will want to use a custom unmarshaler for your types.

You do this by implementing Unmarshaler on your type. Here's an example:

type connectionType uint

const (
    tlsConnection connectionType = iota
    insecureConnection
)

func (t *connectionType) Unmarshal(s string) error {
    switch s {
        case "tls":
            *t = tlsConnection
        case "insecure":
            *t = insecureConnection
        default:
            return fmt.Errorf("unable to unmarshal %s to a connection type", s)
    }

    return nil
}

This section is empty.

View Source

var (
	
	ErrUnexportedField = errors.New("envconfig: unexported field")
	
	ErrNotAPointer = errors.New("envconfig: value is not a pointer")
	
	ErrInvalidValueKind = errors.New("envconfig: invalid value kind, only works on structs")
)
func Init(conf interface{}) error

Init reads the configuration from environment variables and populates the conf object. conf must be a pointer

package main

import (
	"fmt"
	"os"
	"time"

	"github.com/vrischmann/envconfig"
)

func main() {
	var conf struct {
		MySQL struct {
			Host     string
			Port     int
			Database struct {
				User     string
				Password string
				Name     string
			}
			Params struct {
				Charset string `envconfig:"-"`
			}
		}
		Log struct {
			Path   string `envconfig:"default=/var/log/mylog.log"`
			Rotate bool   `envconfig:"logRotate"`
		}
		NbWorkers int
		Timeout   time.Duration
		Cassandra struct {
			SSLCert string
			SSLKey  string
		}
	}

	os.Setenv("MYSQL_HOST", "localhost")
	os.Setenv("MYSQL_PORT", "3306")
	os.Setenv("MYSQL_DATABASE_USER", "root")
	os.Setenv("MYSQL_DATABASE_PASSWORD", "foobar")
	os.Setenv("MYSQL_DATABASE_NAME", "default")
	os.Setenv("logRotate", "true")
	os.Setenv("NBWORKERS", "10")
	os.Setenv("TIMEOUT", "120s")
	os.Setenv("CASSANDRA_SSL_CERT", "/etc/cassandra/ssl.crt")
	os.Setenv("CASSANDRA_SSL_KEY", "/etc/cassandra/ssl.key")

	if err := envconfig.Init(&conf); err != nil {
		fmt.Printf("err=%s\n", err)
	}

	fmt.Println(conf.MySQL.Database.User)
	fmt.Println(conf.Log.Rotate)
	fmt.Println(conf.Timeout)
	fmt.Println(conf.Log.Path)
	fmt.Println(conf.Cassandra.SSLCert)
	fmt.Println(conf.Cassandra.SSLKey)
}
Output:

root
true
2m0s
/var/log/mylog.log
/etc/cassandra/ssl.crt
/etc/cassandra/ssl.key
func InitWithOptions(conf interface{}, opts Options) error

InitWithOptions reads the configuration from environment variables and populates the conf object. conf must be a pointer.

func InitWithPrefix(conf interface{}, prefix string) error

InitWithPrefix reads the configuration from environment variables and populates the conf object. conf must be a pointer. Each key read will be prefixed with the prefix string.

package main

import (
	"fmt"
	"os"

	"github.com/vrischmann/envconfig"
)

func main() {
	var conf struct {
		Name string
	}

	os.Setenv("NAME", "")
	os.Setenv("FOO_NAME", "")

	os.Setenv("NAME", "foobar")

	err := envconfig.InitWithPrefix(&conf, "FOO")
	fmt.Println(err)

	os.Setenv("FOO_NAME", "foobar")
	err = envconfig.InitWithPrefix(&conf, "FOO")
	fmt.Println(err)

	fmt.Println(conf.Name)
}
Output:

envconfig: keys FOO_NAME, foo_name not found
<nil>
foobar

Options is used to customize the behavior of envconfig. Use it with InitWithOptions.

type Unmarshaler interface {
	Unmarshal(s string) error
}

Unmarshaler is the interface implemented by objects that can unmarshal a environment variable string of themselves.