add functions/vendor files

This commit is contained in:
Reed Allman
2017-06-11 02:05:36 -07:00
parent 6ee9c1fa0a
commit f2c7aa5ee6
7294 changed files with 1629834 additions and 0 deletions

10
vendor/github.com/vrischmann/envconfig/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,10 @@
language: go
go:
- 1.2
- 1.3
- 1.4
- 1.5
- 1.6
- 1.7
- tip

19
vendor/github.com/vrischmann/envconfig/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2015 Vincent Rischmann
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

151
vendor/github.com/vrischmann/envconfig/README.md generated vendored Normal file
View File

@@ -0,0 +1,151 @@
envconfig
=========
[![Build Status](https://travis-ci.org/vrischmann/envconfig.svg?branch=master)](https://travis-ci.org/vrischmann/envconfig)
[![GoDoc](https://godoc.org/github.com/vrischmann/envconfig?status.svg)](https://godoc.org/github.com/vrischmann/envconfig)
envconfig is a library which allows you to parse your configuration from environment variables and fill an arbitrary struct.
See [the example](https://godoc.org/github.com/vrischmann/envconfig#example-Init) to understand how to use it, it's pretty simple.
Supported types
---------------
* Almost all standard types plus `time.Duration` are supported by default.
* Slices and arrays
* Arbitrary structs
* Custom types via the [Unmarshaler](https://godoc.org/github.com/vrischmann/envconfig/#Unmarshaler) interface.
How does it work
----------------
*envconfig* takes the hierarchy of your configuration struct and the names of the fields to create a environment variable key.
For example:
```go
var conf struct {
Name string
Shard struct {
Host string
Port int
}
}
```
This will check for those 3 keys:
* NAME or name
* SHARD\_HOST, or shard\_host
* SHARD\_PORT, or shard\_port
Flexible key naming
-------------------
*envconfig* supports having underscores in the key names where there is a _word boundary_. Now, that term is not super explicit, so let me show you an example:
```go
var conf struct {
Cassandra struct {
SSLCert string
SslKey string
}
}
```
This will check all of the following keys:
* CASSANDRA\_SSL\_CERT, CASSANDRA\_SSLCERT, cassandra\_ssl\_cert, cassandra\_sslcert
* CASSANDRA\_SSL\_KEY, CASSANDRA\_SSLKEY, cassandra\_ssl\_key, cassandra\_sslkey
If that is not good enough, look just below.
Custom environment variable names
---------------------------------
*envconfig* supports custom environment variable names:
```go
var conf struct {
Name string `envconfig:"myName"`
}
```
Default values
--------------
*envconfig* supports default values:
```go
var conf struct {
Name string `envconfig:"default=Vincent"`
}
```
Optional values
---------------
*envconfig* supports optional values:
```go
var conf struct {
Name string `envconfig:"optional"`
Age int `envconfig:"-"`
}
```
The two syntax are equivalent.
Combining multiple options in one tag
-------------------------------------
You can of course combine multiple options:
```go
var conf struct {
Name string `envconfig:"default=Vincent,myName"`
}
```
Slices or arrays
----------------
With slices or arrays, the same naming is applied for the slice. To put multiple elements into the slice or array, you need to separate
them with a *,* (will probably be configurable in the future, or at least have a way to escape)
For example:
```go
var conf struct {
Ports []int
}
```
This will check for the key __PORTS__:
* if your variable is *9000* the slice will contain only 9000
* if your variable is *9000,100* the slice will contain 9000 and 100
For slices of structs, it's a little more complicated. The same splitting of slice elements is done with a *comma*, however, each token must follow
a specific format like this: `{<first field>,<second field>,...}`
For example:
```go
var conf struct {
Shards []struct {
Name string
Port int
}
}
```
This will check for the key __SHARDS__. Example variable content: `{foobar,9000},{barbaz,20000}`
This will result in two struct defined in the *Shards* slice.
Future work
-----------
* support for time.Time values with a layout defined via a field tag
* support for complex types

199
vendor/github.com/vrischmann/envconfig/doc.go generated vendored Normal file
View File

@@ -0,0 +1,199 @@
/*
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"`
Age int `envconfig:"-"`
}
The two syntax are equivalent.
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
}
*/
package envconfig

487
vendor/github.com/vrischmann/envconfig/envconfig.go generated vendored Normal file
View File

@@ -0,0 +1,487 @@
package envconfig
import (
"bytes"
"encoding/base64"
"errors"
"fmt"
"os"
"reflect"
"sort"
"strconv"
"strings"
"time"
"unicode"
)
var (
// ErrUnexportedField is the error returned by the Init* functions when a field of the config struct is not exported and the option AllowUnexported is not used.
ErrUnexportedField = errors.New("envconfig: unexported field")
// ErrNotAPointer is the error returned by the Init* functions when the configuration object is not a pointer.
ErrNotAPointer = errors.New("envconfig: value is not a pointer")
// ErrInvalidValueKind is the error returned by the Init* functions when the configuration object is not a struct.
ErrInvalidValueKind = errors.New("envconfig: invalid value kind, only works on structs")
// ErrDefaultUnsupportedOnSlice is the error returned by the Init* functions when there is a default tag on a slice.
// The `default` tag is unsupported on slices because slice parsing uses , as the separator, as does the envconfig tags separator.
ErrDefaultUnsupportedOnSlice = errors.New("envconfig: default tag unsupported on slice")
)
type context struct {
name string
customName string
defaultVal string
parents []reflect.Value
optional, leaveNil bool
allowUnexported bool
}
// Unmarshaler is the interface implemented by objects that can unmarshal
// a environment variable string of themselves.
type Unmarshaler interface {
Unmarshal(s string) error
}
// Options is used to customize the behavior of envconfig. Use it with InitWithOptions.
type Options struct {
// Prefix allows specifying a prefix for each key.
Prefix string
// AllOptional determines whether to not throw errors by default for any key
// that is not found. AllOptional=true means errors will not be thrown.
AllOptional bool
// LeaveNil specifies whether to not create new pointers for any pointer fields
// found within the passed config. Rather, it behaves such that if and only if
// there is a) a non-empty field in the value or b) a non-empty value that
// the pointer is pointing to will a new pointer be created. By default,
// LeaveNil=false will create all pointers in all structs if they are nil.
//
// var X struct {
// A *struct{
// B string
// }
// }
// envconfig.InitWithOptions(&X, Options{LeaveNil: true})
//
// $ ./program
//
// X.A == nil
//
// $ A_B="string" ./program
//
// X.A.B="string" // A will not be nil
LeaveNil bool
// AllowUnexported allows unexported fields to be present in the passed config.
AllowUnexported bool
}
// Init reads the configuration from environment variables and populates the conf object. conf must be a pointer
func Init(conf interface{}) error {
return InitWithOptions(conf, Options{})
}
// 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.
func InitWithPrefix(conf interface{}, prefix string) error {
return InitWithOptions(conf, Options{Prefix: prefix})
}
// InitWithOptions reads the configuration from environment variables and populates the conf object.
// conf must be a pointer.
func InitWithOptions(conf interface{}, opts Options) error {
value := reflect.ValueOf(conf)
if value.Kind() != reflect.Ptr {
return ErrNotAPointer
}
elem := value.Elem()
ctx := context{
name: opts.Prefix,
optional: opts.AllOptional,
leaveNil: opts.LeaveNil,
allowUnexported: opts.AllowUnexported,
}
switch elem.Kind() {
case reflect.Ptr:
if elem.IsNil() {
elem.Set(reflect.New(elem.Type().Elem()))
}
_, err := readStruct(elem.Elem(), &ctx)
return err
case reflect.Struct:
_, err := readStruct(elem, &ctx)
return err
default:
return ErrInvalidValueKind
}
}
type tag struct {
customName string
optional bool
skip bool
defaultVal string
}
func parseTag(s string) *tag {
var t tag
tokens := strings.Split(s, ",")
for _, v := range tokens {
switch {
case v == "-":
t.skip = true
case v == "optional":
t.optional = true
case strings.HasPrefix(v, "default="):
t.defaultVal = strings.TrimPrefix(v, "default=")
default:
t.customName = v
}
}
return &t
}
func readStruct(value reflect.Value, ctx *context) (nonNil bool, err error) {
var parents []reflect.Value
for i := 0; i < value.NumField(); i++ {
field := value.Field(i)
name := value.Type().Field(i).Name
tag := parseTag(value.Type().Field(i).Tag.Get("envconfig"))
if tag.skip || !field.CanSet() {
if !field.CanSet() && !ctx.allowUnexported {
return false, ErrUnexportedField
}
continue
}
parents = ctx.parents
doRead:
switch field.Kind() {
case reflect.Ptr:
// it's a pointer, create a new value and restart the switch
if field.IsNil() {
field.Set(reflect.New(field.Type().Elem()))
parents = append(parents, field) // track parent pointers to deallocate if no children are filled in
}
field = field.Elem()
goto doRead
case reflect.Struct:
var nonNilIn bool
nonNilIn, err = readStruct(field, &context{
name: combineName(ctx.name, name),
optional: ctx.optional || tag.optional,
defaultVal: tag.defaultVal,
parents: parents,
leaveNil: ctx.leaveNil,
allowUnexported: ctx.allowUnexported,
})
nonNil = nonNil || nonNilIn
default:
var ok bool
ok, err = setField(field, &context{
name: combineName(ctx.name, name),
customName: tag.customName,
optional: ctx.optional || tag.optional,
defaultVal: tag.defaultVal,
parents: parents,
leaveNil: ctx.leaveNil,
allowUnexported: ctx.allowUnexported,
})
nonNil = nonNil || ok
}
if err != nil {
return false, err
}
}
if !nonNil && ctx.leaveNil { // re-zero
for _, p := range parents {
p.Set(reflect.Zero(p.Type()))
}
}
return nonNil, err
}
var byteSliceType = reflect.TypeOf([]byte(nil))
func setField(value reflect.Value, ctx *context) (ok bool, err error) {
str, err := readValue(ctx)
if err != nil {
return false, err
}
if len(str) == 0 && ctx.optional {
return false, nil
}
isSliceNotUnmarshaler := value.Kind() == reflect.Slice && !isUnmarshaler(value.Type())
switch {
case isSliceNotUnmarshaler && value.Type() == byteSliceType:
return true, parseBytesValue(value, str)
case isSliceNotUnmarshaler:
return true, setSliceField(value, str, ctx)
default:
return true, parseValue(value, str, ctx)
}
}
func setSliceField(value reflect.Value, str string, ctx *context) error {
if ctx.defaultVal != "" {
return ErrDefaultUnsupportedOnSlice
}
elType := value.Type().Elem()
tnz := newSliceTokenizer(str)
slice := reflect.MakeSlice(value.Type(), value.Len(), value.Cap())
for tnz.scan() {
token := tnz.text()
el := reflect.New(elType).Elem()
if err := parseValue(el, token, ctx); err != nil {
return err
}
slice = reflect.Append(slice, el)
}
value.Set(slice)
return tnz.Err()
}
var (
durationType = reflect.TypeOf(new(time.Duration)).Elem()
unmarshalerType = reflect.TypeOf(new(Unmarshaler)).Elem()
)
func isDurationField(t reflect.Type) bool {
return t.AssignableTo(durationType)
}
func isUnmarshaler(t reflect.Type) bool {
return t.Implements(unmarshalerType) || reflect.PtrTo(t).Implements(unmarshalerType)
}
func parseValue(v reflect.Value, str string, ctx *context) (err error) {
vtype := v.Type()
// Special case when the type is a map: we need to make the map
switch vtype.Kind() {
case reflect.Map:
v.Set(reflect.MakeMap(vtype))
}
// Special case for Unmarshaler
if isUnmarshaler(vtype) {
return parseWithUnmarshaler(v, str)
}
// Special case for time.Duration
if isDurationField(vtype) {
return parseDuration(v, str)
}
kind := vtype.Kind()
switch kind {
case reflect.Bool:
err = parseBoolValue(v, str)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
err = parseIntValue(v, str)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
err = parseUintValue(v, str)
case reflect.Float32, reflect.Float64:
err = parseFloatValue(v, str)
case reflect.Ptr:
v.Set(reflect.New(vtype.Elem()))
return parseValue(v.Elem(), str, ctx)
case reflect.String:
v.SetString(str)
case reflect.Struct:
err = parseStruct(v, str, ctx)
default:
return fmt.Errorf("envconfig: kind %v not supported", kind)
}
return
}
func parseWithUnmarshaler(v reflect.Value, str string) error {
var u = v.Addr().Interface().(Unmarshaler)
return u.Unmarshal(str)
}
func parseDuration(v reflect.Value, str string) error {
d, err := time.ParseDuration(str)
if err != nil {
return err
}
v.SetInt(int64(d))
return nil
}
// NOTE(vincent): this is only called when parsing structs inside a slice.
func parseStruct(value reflect.Value, token string, ctx *context) error {
tokens := strings.Split(token[1:len(token)-1], ",")
if len(tokens) != value.NumField() {
return fmt.Errorf("envconfig: struct token has %d fields but struct has %d", len(tokens), value.NumField())
}
for i := 0; i < value.NumField(); i++ {
field := value.Field(i)
t := tokens[i]
if err := parseValue(field, t, ctx); err != nil {
return err
}
}
return nil
}
func parseBoolValue(v reflect.Value, str string) error {
val, err := strconv.ParseBool(str)
if err != nil {
return err
}
v.SetBool(val)
return nil
}
func parseIntValue(v reflect.Value, str string) error {
val, err := strconv.ParseInt(str, 10, 64)
if err != nil {
return err
}
v.SetInt(val)
return nil
}
func parseUintValue(v reflect.Value, str string) error {
val, err := strconv.ParseUint(str, 10, 64)
if err != nil {
return err
}
v.SetUint(val)
return nil
}
func parseFloatValue(v reflect.Value, str string) error {
val, err := strconv.ParseFloat(str, 64)
if err != nil {
return err
}
v.SetFloat(val)
return nil
}
func parseBytesValue(v reflect.Value, str string) error {
val, err := base64.StdEncoding.DecodeString(str)
if err != nil {
return err
}
v.SetBytes(val)
return nil
}
func combineName(parentName, name string) string {
if parentName == "" {
return name
}
return parentName + "." + name
}
func readValue(ctx *context) (string, error) {
keys := makeAllPossibleKeys(ctx)
var str string
for _, key := range keys {
str = os.Getenv(key)
if str != "" {
break
}
}
if str != "" {
return str, nil
}
if ctx.defaultVal != "" {
return ctx.defaultVal, nil
}
if ctx.optional {
return "", nil
}
return "", fmt.Errorf("envconfig: keys %s not found", strings.Join(keys, ", "))
}
func makeAllPossibleKeys(ctx *context) (res []string) {
if ctx.customName != "" {
return []string{ctx.customName}
}
tmp := make(map[string]struct{})
{
n := []rune(ctx.name)
var buf bytes.Buffer // this is the buffer where we put extra underscores on "word" boundaries
var buf2 bytes.Buffer // this is the buffer with the standard naming scheme
wroteUnderscore := false
for i, r := range ctx.name {
if r == '.' {
buf.WriteRune('_')
buf2.WriteRune('_')
wroteUnderscore = true
continue
}
prevOrNextLower := i+1 < len(n) && i-1 > 0 && (unicode.IsLower(n[i+1]) || unicode.IsLower(n[i-1]))
if i > 0 && unicode.IsUpper(r) && prevOrNextLower && !wroteUnderscore {
buf.WriteRune('_')
}
buf.WriteRune(r)
buf2.WriteRune(r)
wroteUnderscore = false
}
tmp[strings.ToLower(buf.String())] = struct{}{}
tmp[strings.ToUpper(buf.String())] = struct{}{}
tmp[strings.ToLower(buf2.String())] = struct{}{}
tmp[strings.ToUpper(buf2.String())] = struct{}{}
}
for k := range tmp {
res = append(res, k)
}
sort.Strings(res)
return
}

View File

@@ -0,0 +1,597 @@
package envconfig_test
import (
"fmt"
"os"
"strconv"
"strings"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/vrischmann/envconfig"
)
func TestParseSimpleConfig(t *testing.T) {
var conf struct {
Name string
Log struct {
Path string
}
}
err := envconfig.Init(&conf)
require.Equal(t, "envconfig: keys NAME, name not found", err.Error())
os.Setenv("NAME", "foobar")
err = envconfig.Init(&conf)
require.Equal(t, "envconfig: keys LOG_PATH, log_path not found", err.Error())
os.Setenv("LOG_PATH", "/var/log/foobar")
err = envconfig.Init(&conf)
require.Nil(t, err)
require.Equal(t, "foobar", conf.Name)
require.Equal(t, "/var/log/foobar", conf.Log.Path)
// Clean up at the end of the test - some tests share the same key and we don't values to be seen by those tests
os.Setenv("NAME", "")
os.Setenv("LOG_PATH", "")
}
func TestParseIntegerConfig(t *testing.T) {
var conf struct {
Port int
Long uint64
Version uint8
}
timestamp := time.Now().UnixNano()
os.Setenv("PORT", "80")
os.Setenv("LONG", fmt.Sprintf("%d", timestamp))
os.Setenv("VERSION", "2")
err := envconfig.Init(&conf)
require.Nil(t, err)
require.Equal(t, 80, conf.Port)
require.Equal(t, uint64(timestamp), conf.Long)
require.Equal(t, uint8(2), conf.Version)
}
func TestParseBoolConfig(t *testing.T) {
var conf struct {
DoIt bool
}
os.Setenv("DOIT", "true")
err := envconfig.Init(&conf)
require.Nil(t, err)
require.Equal(t, true, conf.DoIt)
}
func TestParseBytesConfig(t *testing.T) {
var conf struct {
Data []byte
}
os.Setenv("DATA", "Rk9PQkFS")
err := envconfig.Init(&conf)
require.Nil(t, err)
require.Equal(t, []byte("FOOBAR"), conf.Data)
}
func TestParseFloatConfig(t *testing.T) {
var conf struct {
Delta float32
DeltaV float64
}
os.Setenv("DELTA", "0.02")
os.Setenv("DELTAV", "400.20000000001")
err := envconfig.Init(&conf)
require.Nil(t, err)
require.Equal(t, float32(0.02), conf.Delta)
require.Equal(t, float64(400.20000000001), conf.DeltaV)
}
func TestParseSliceConfig(t *testing.T) {
var conf struct {
Names []string
Ports []int
Shards []struct {
Name string
Addr string
}
}
os.Setenv("NAMES", "foobar,barbaz")
os.Setenv("PORTS", "900,100")
os.Setenv("SHARDS", "{foobar,localhost:2929},{barbaz,localhost:2828}")
err := envconfig.Init(&conf)
require.Nil(t, err)
require.Equal(t, 2, len(conf.Names))
require.Equal(t, "foobar", conf.Names[0])
require.Equal(t, "barbaz", conf.Names[1])
require.Equal(t, 2, len(conf.Ports))
require.Equal(t, 900, conf.Ports[0])
require.Equal(t, 100, conf.Ports[1])
require.Equal(t, 2, len(conf.Shards))
require.Equal(t, "foobar", conf.Shards[0].Name)
require.Equal(t, "localhost:2929", conf.Shards[0].Addr)
require.Equal(t, "barbaz", conf.Shards[1].Name)
require.Equal(t, "localhost:2828", conf.Shards[1].Addr)
}
func TestParseStructSliceWrongData(t *testing.T) {
var conf struct {
Shards []struct {
Name string
Addr string
}
}
os.Setenv("SHARDS", "foobar")
err := envconfig.Init(&conf)
require.Equal(t, "envconfig: struct token has 1 fields but struct has 2", err.Error())
}
func TestParseStructSliceWrongValue(t *testing.T) {
var conf struct {
Shards []struct {
Name string
Port int32
}
}
os.Setenv("SHARDS", "{foobar,barbaz}")
err := envconfig.Init(&conf)
require.Equal(t, `strconv.ParseInt: parsing "barbaz": invalid syntax`, err.Error())
}
func TestParseWrongValues(t *testing.T) {
var conf struct{ OK bool }
os.Setenv("OK", "foobar")
err := envconfig.Init(&conf)
require.Equal(t, `strconv.ParseBool: parsing "foobar": invalid syntax`, err.Error())
var conf2 struct{ Port int }
os.Setenv("PORT", "foobar")
err = envconfig.Init(&conf2)
require.Equal(t, `strconv.ParseInt: parsing "foobar": invalid syntax`, err.Error())
var conf3 struct{ Port uint }
os.Setenv("PORT", "foobar")
err = envconfig.Init(&conf3)
require.Equal(t, `strconv.ParseUint: parsing "foobar": invalid syntax`, err.Error())
var conf4 struct{ Port float32 }
os.Setenv("PORT", "foobar")
err = envconfig.Init(&conf4)
require.Equal(t, `strconv.ParseFloat: parsing "foobar": invalid syntax`, err.Error())
var conf5 struct{ Data []byte }
os.Setenv("DATA", "foobar")
err = envconfig.Init(&conf5)
require.Equal(t, "illegal base64 data at input byte 4", err.Error())
}
func TestDurationConfig(t *testing.T) {
var conf struct {
Timeout time.Duration
}
os.Setenv("TIMEOUT", "1m")
err := envconfig.Init(&conf)
require.Nil(t, err)
require.Equal(t, time.Minute*1, conf.Timeout)
}
func TestInvalidDurationConfig(t *testing.T) {
var conf struct {
Timeout time.Duration
}
os.Setenv("TIMEOUT", "foo")
err := envconfig.Init(&conf)
require.NotNil(t, err)
}
func TestAllPointerConfig(t *testing.T) {
var conf struct {
Name *string
Port *int
Delta *float32
DeltaV *float64
Hosts *[]string
Shards *[]*struct {
Name *string
Addr *string
}
Master *struct {
Name *string
Addr *string
}
Timeout *time.Duration
}
os.Setenv("NAME", "foobar")
os.Setenv("PORT", "9000")
os.Setenv("DELTA", "40.01")
os.Setenv("DELTAV", "200.00001")
os.Setenv("HOSTS", "localhost,free.fr")
os.Setenv("SHARDS", "{foobar,localhost:2828},{barbaz,localhost:2929}")
os.Setenv("MASTER_NAME", "master")
os.Setenv("MASTER_ADDR", "localhost:2727")
os.Setenv("TIMEOUT", "1m")
err := envconfig.Init(&conf)
require.Nil(t, err)
require.Equal(t, "foobar", *conf.Name)
require.Equal(t, 9000, *conf.Port)
require.Equal(t, float32(40.01), *conf.Delta)
require.Equal(t, 200.00001, *conf.DeltaV)
require.Equal(t, 2, len(*conf.Hosts))
require.Equal(t, "localhost", (*conf.Hosts)[0])
require.Equal(t, "free.fr", (*conf.Hosts)[1])
require.Equal(t, 2, len(*conf.Shards))
require.Equal(t, "foobar", *(*conf.Shards)[0].Name)
require.Equal(t, "localhost:2828", *(*conf.Shards)[0].Addr)
require.Equal(t, "barbaz", *(*conf.Shards)[1].Name)
require.Equal(t, "localhost:2929", *(*conf.Shards)[1].Addr)
require.Equal(t, "master", *conf.Master.Name)
require.Equal(t, "localhost:2727", *conf.Master.Addr)
require.Equal(t, time.Minute*1, *conf.Timeout)
}
type logMode uint
const (
logFile logMode = iota + 1
logStdout
)
func (m *logMode) Unmarshal(s string) error {
switch strings.ToLower(s) {
case "file":
*m = logFile
case "stdout":
*m = logStdout
default:
return fmt.Errorf("unable to unmarshal %s", s)
}
return nil
}
func TestUnmarshaler(t *testing.T) {
var conf struct {
LogMode logMode
}
os.Setenv("LOGMODE", "file")
err := envconfig.Init(&conf)
require.Nil(t, err)
require.Equal(t, logFile, conf.LogMode)
var conf2 struct {
LogMode *logMode
}
err = envconfig.Init(&conf2)
require.Nil(t, err)
require.Equal(t, logFile, *conf2.LogMode)
}
func TestParseOptionalConfig(t *testing.T) {
var conf struct {
Name string `envconfig:"optional"`
Flag bool `envconfig:"optional"`
Timeout time.Duration `envconfig:"optional"`
Port int `envconfig:"optional"`
Port2 uint `envconfig:"optional"`
Delta float32 `envconfig:"optional"`
DeltaV float64 `envconfig:"optional"`
Slice []string `envconfig:"optional"`
Struct struct {
A string
B int
} `envconfig:"optional"`
}
os.Setenv("NAME", "")
os.Setenv("FLAG", "")
os.Setenv("TIMEOUT", "")
os.Setenv("PORT", "")
os.Setenv("PORT2", "")
os.Setenv("DELTA", "")
os.Setenv("DELTAV", "")
os.Setenv("SLICE", "")
os.Setenv("STRUCT_A", "")
err := envconfig.Init(&conf)
require.Nil(t, err)
require.Equal(t, "", conf.Name)
os.Setenv("NAME", "foobar")
os.Setenv("SLICE", "a,b")
os.Setenv("STRUCT_A", "foobar")
os.Setenv("STRUCT_B", "1")
err = envconfig.Init(&conf)
require.Nil(t, err)
require.Equal(t, "foobar", conf.Name)
require.Equal(t, []string{"a", "b"}, conf.Slice)
require.Equal(t, "foobar", conf.Struct.A)
require.Equal(t, 1, conf.Struct.B)
}
func TestParseSkippableConfig(t *testing.T) {
var conf struct {
Flag bool `envconfig:"-"`
}
os.Setenv("FLAG", "true")
err := envconfig.Init(&conf)
require.Nil(t, err)
require.Equal(t, false, conf.Flag)
}
func TestParseCustomNameConfig(t *testing.T) {
var conf struct {
Name string `envconfig:"customName"`
}
os.Setenv("customName", "foobar")
err := envconfig.Init(&conf)
require.Nil(t, err)
require.Equal(t, "foobar", conf.Name)
}
func TestParseOptionalStruct(t *testing.T) {
var conf struct {
Master struct {
Name string
} `envconfig:"optional"`
}
os.Setenv("MASTER_NAME", "")
err := envconfig.Init(&conf)
require.Nil(t, err)
require.Equal(t, "", conf.Master.Name)
}
func TestParsePrefixedStruct(t *testing.T) {
var conf struct {
Name string
}
os.Setenv("NAME", "")
os.Setenv("FOO_NAME", "")
os.Setenv("NAME", "bad")
err := envconfig.InitWithPrefix(&conf, "FOO")
require.NotNil(t, err)
os.Setenv("FOO_NAME", "good")
err = envconfig.InitWithPrefix(&conf, "FOO")
require.Nil(t, err)
require.Equal(t, "good", conf.Name)
}
func TestUnexportedField(t *testing.T) {
var conf struct {
name string
}
os.Setenv("NAME", "foobar")
err := envconfig.Init(&conf)
require.Equal(t, envconfig.ErrUnexportedField, err)
err = envconfig.InitWithOptions(&conf, envconfig.Options{AllowUnexported: true})
require.Equal(t, nil, err)
}
func TestNestedUnexportedField(t *testing.T) {
var conf struct {
Foo struct {
Bar struct {
baz string
}
}
}
os.Setenv("FOO_BAR_BAZ", "foobar")
err := envconfig.Init(&conf)
require.Equal(t, envconfig.ErrUnexportedField, err)
err = envconfig.InitWithOptions(&conf, envconfig.Options{AllowUnexported: true})
require.Equal(t, nil, err)
}
type sliceWithUnmarshaler []int
func (sl *sliceWithUnmarshaler) Unmarshal(s string) error {
tokens := strings.Split(s, ".")
for _, tok := range tokens {
tmp, err := strconv.Atoi(tok)
if err != nil {
return err
}
*sl = append(*sl, tmp)
}
return nil
}
func TestSliceTypeWithUnmarshaler(t *testing.T) {
var conf struct {
Data sliceWithUnmarshaler
}
os.Setenv("DATA", "1.2.3")
err := envconfig.Init(&conf)
require.Nil(t, err)
require.Equal(t, 3, len(conf.Data))
require.Equal(t, 1, conf.Data[0])
require.Equal(t, 2, conf.Data[1])
require.Equal(t, 3, conf.Data[2])
}
func TestParseDefaultVal(t *testing.T) {
var conf struct {
MySQL struct {
Master struct {
Address string `envconfig:"default=localhost"`
Port int `envconfig:"default=3306"`
}
Timeout time.Duration `envconfig:"default=1m,myTimeout"`
LocalTimeout time.Duration `envconfig:"myTimeout2,default=1m"`
}
}
err := envconfig.Init(&conf)
require.Nil(t, err)
require.Equal(t, "localhost", conf.MySQL.Master.Address)
require.Equal(t, 3306, conf.MySQL.Master.Port)
require.Equal(t, time.Minute*1, conf.MySQL.Timeout)
os.Setenv("myTimeout", "2m")
os.Setenv("myTimeout2", "20m")
err = envconfig.Init(&conf)
require.Nil(t, err)
require.Equal(t, "localhost", conf.MySQL.Master.Address)
require.Equal(t, 3306, conf.MySQL.Master.Port)
require.Equal(t, time.Minute*2, conf.MySQL.Timeout)
require.Equal(t, time.Minute*20, conf.MySQL.LocalTimeout)
}
func TestDefaultSlice(t *testing.T) {
// See https://github.com/vrischmann/envconfig/pull/15
//
// The way people think about the following default value, is that the slice will be [a,b]
// However this never worked because we split the entire envconfig tag on , therefore default is just `a` here.
// The proper thing to do is to introduce a new format in the tag that doesn't have this limitation, but we don't have that yet.
// For now, we simply return an error indicating default is not unsupported on slices.
var conf struct {
Hosts []string `envconfig:"default=a,b"`
}
err := envconfig.Init(&conf)
require.NotNil(t, err)
require.Equal(t, envconfig.ErrDefaultUnsupportedOnSlice, err)
}
func TestInitNotAPointer(t *testing.T) {
err := envconfig.Init("foobar")
require.Equal(t, envconfig.ErrNotAPointer, err)
}
func TestInitPointerToAPointer(t *testing.T) {
type Conf struct {
Name string
}
var tmp *Conf
os.Setenv("NAME", "foobar")
err := envconfig.Init(&tmp)
require.Nil(t, err)
require.Equal(t, "foobar", tmp.Name)
}
func TestInitInvalidValueKind(t *testing.T) {
sl := []string{"foo", "bar"}
err := envconfig.Init(&sl)
require.Equal(t, envconfig.ErrInvalidValueKind, err)
}
func TestInvalidFieldValueKind(t *testing.T) {
var conf struct {
Foo interface{}
}
os.Setenv("FOO", "lalala")
err := envconfig.Init(&conf)
require.Equal(t, "envconfig: kind interface not supported", err.Error())
}
func TestInvalidSliceElementValueKind(t *testing.T) {
var conf struct {
Foo []interface{}
}
os.Setenv("FOO", "lalala")
err := envconfig.Init(&conf)
require.Equal(t, "envconfig: kind interface not supported", err.Error())
}
func TestParseEmptyTag(t *testing.T) {
var conf struct {
Name string `envconfig:""`
}
os.Setenv("NAME", "foobar")
err := envconfig.Init(&conf)
require.Nil(t, err)
require.Equal(t, "foobar", conf.Name)
}
func TestLeaveNil(t *testing.T) {
var conf struct {
MySQL *struct {
Name string
}
}
err := envconfig.InitWithOptions(&conf, envconfig.Options{
AllOptional: true,
LeaveNil: true,
})
require.Nil(t, err)
require.Nil(t, conf.MySQL)
}
type myMapType map[string]int
func (t *myMapType) Unmarshal(s string) error {
(*t)[s] = 1
return nil
}
func TestParseMapType(t *testing.T) {
var conf struct {
Map myMapType
}
os.Setenv("MAP", "a")
err := envconfig.Init(&conf)
require.Nil(t, err)
require.Equal(t, 1, conf.Map["a"])
}

89
vendor/github.com/vrischmann/envconfig/example_test.go generated vendored Normal file
View File

@@ -0,0 +1,89 @@
package envconfig_test
import (
"fmt"
"os"
"time"
"github.com/vrischmann/envconfig"
)
func ExampleInit() {
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 ExampleInitWithPrefix() {
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
}

62
vendor/github.com/vrischmann/envconfig/keys_test.go generated vendored Normal file
View File

@@ -0,0 +1,62 @@
package envconfig
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestMakeAllPossibleKeys(t *testing.T) {
fieldName := "CassandraSslCert"
keys := makeAllPossibleKeys(&context{
name: fieldName,
})
require.Equal(t, 4, len(keys))
require.Equal(t, "CASSANDRASSLCERT", keys[0])
require.Equal(t, "CASSANDRA_SSL_CERT", keys[1])
require.Equal(t, "cassandra_ssl_cert", keys[2])
require.Equal(t, "cassandrasslcert", keys[3])
fieldName = "CassandraSSLCert"
keys = makeAllPossibleKeys(&context{
name: fieldName,
})
require.Equal(t, 4, len(keys))
require.Equal(t, "CASSANDRASSLCERT", keys[0])
require.Equal(t, "CASSANDRA_SSL_CERT", keys[1])
require.Equal(t, "cassandra_ssl_cert", keys[2])
require.Equal(t, "cassandrasslcert", keys[3])
fieldName = "Cassandra.SslCert"
keys = makeAllPossibleKeys(&context{
name: fieldName,
})
require.Equal(t, 4, len(keys))
require.Equal(t, "CASSANDRA_SSLCERT", keys[0])
require.Equal(t, "CASSANDRA_SSL_CERT", keys[1])
require.Equal(t, "cassandra_ssl_cert", keys[2])
require.Equal(t, "cassandra_sslcert", keys[3])
fieldName = "Cassandra.SSLCert"
keys = makeAllPossibleKeys(&context{
name: fieldName,
})
require.Equal(t, 4, len(keys))
require.Equal(t, "CASSANDRA_SSLCERT", keys[0])
require.Equal(t, "CASSANDRA_SSL_CERT", keys[1])
require.Equal(t, "cassandra_ssl_cert", keys[2])
require.Equal(t, "cassandra_sslcert", keys[3])
fieldName = "Name"
keys = makeAllPossibleKeys(&context{
name: fieldName,
})
require.Equal(t, 2, len(keys))
require.Equal(t, "NAME", keys[0])
require.Equal(t, "name", keys[1])
}

76
vendor/github.com/vrischmann/envconfig/slice.go generated vendored Normal file
View File

@@ -0,0 +1,76 @@
package envconfig
import (
"bufio"
"bytes"
"io"
"strings"
)
type sliceTokenizer struct {
err error
r *bufio.Reader
buf bytes.Buffer
inBraces bool
}
var eof = rune(0)
func newSliceTokenizer(str string) *sliceTokenizer {
return &sliceTokenizer{
r: bufio.NewReader(strings.NewReader(str)),
}
}
func (t *sliceTokenizer) scan() bool {
for {
if t.err == io.EOF && t.buf.Len() == 0 {
return false
}
ch := t.readRune()
if ch == eof {
return true
}
if ch == '{' {
t.inBraces = true
}
if ch == '}' {
t.inBraces = false
}
if ch == ',' && !t.inBraces {
return true
}
// NOTE(vincent): we ignore the WriteRune error here because there is NO WAY
// for WriteRune to return an error.
// Yep. Seriously. Look here http://golang.org/src/bytes/buffer.go?s=7661:7714#L227
_, _ = t.buf.WriteRune(ch)
}
}
func (t *sliceTokenizer) readRune() rune {
ch, _, err := t.r.ReadRune()
if err != nil {
t.err = err
return eof
}
return ch
}
func (t *sliceTokenizer) text() string {
str := t.buf.String()
t.buf.Reset()
return str
}
func (t *sliceTokenizer) Err() error {
if t.err == io.EOF {
return nil
}
return t.err
}

47
vendor/github.com/vrischmann/envconfig/slice_test.go generated vendored Normal file
View File

@@ -0,0 +1,47 @@
package envconfig
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestSliceTokenizer(t *testing.T) {
str := "foobar,barbaz"
tnz := newSliceTokenizer(str)
b := tnz.scan()
require.Nil(t, tnz.Err())
require.Equal(t, true, b)
require.Equal(t, "foobar", tnz.text())
b = tnz.scan()
require.Nil(t, tnz.Err())
require.Equal(t, true, b)
require.Equal(t, "barbaz", tnz.text())
b = tnz.scan()
require.Nil(t, tnz.Err())
require.Equal(t, false, b)
}
func TestSliceOfStructsTokenizer(t *testing.T) {
str := "{foobar,100},{barbaz,200}"
tnz := newSliceTokenizer(str)
b := tnz.scan()
require.Nil(t, tnz.Err())
require.Equal(t, true, b)
require.Equal(t, "{foobar,100}", tnz.text())
b = tnz.scan()
require.Nil(t, tnz.Err())
require.Equal(t, true, b)
require.Equal(t, "{barbaz,200}", tnz.text())
b = tnz.scan()
require.Nil(t, tnz.Err())
require.Equal(t, false, b)
}