vendor deps
Change-Id: Ia5385cbe6b9d6c9ad6065b7d2c402ebe164246d0
This commit is contained in:
+7
@@ -0,0 +1,7 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.4
|
||||
- 1.5
|
||||
- 1.6
|
||||
- tip
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2013 Kelsey Hightower
|
||||
|
||||
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.
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
Kelsey Hightower kelsey.hightower@gmail.com github.com/kelseyhightower
|
||||
Travis Parker travis.parker@gmail.com github.com/teepark
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
# envconfig
|
||||
|
||||
[](https://travis-ci.org/kelseyhightower/envconfig)
|
||||
|
||||
```Go
|
||||
import "github.com/kelseyhightower/envconfig"
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
See [godoc](http://godoc.org/github.com/kelseyhightower/envconfig)
|
||||
|
||||
## Usage
|
||||
|
||||
Set some environment variables:
|
||||
|
||||
```Bash
|
||||
export MYAPP_DEBUG=false
|
||||
export MYAPP_PORT=8080
|
||||
export MYAPP_USER=Kelsey
|
||||
export MYAPP_RATE="0.5"
|
||||
export MYAPP_TIMEOUT="3m"
|
||||
export MYAPP_USERS="rob,ken,robert"
|
||||
```
|
||||
|
||||
Write some code:
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/kelseyhightower/envconfig"
|
||||
)
|
||||
|
||||
type Specification struct {
|
||||
Debug bool
|
||||
Port int
|
||||
User string
|
||||
Users []string
|
||||
Rate float32
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
func main() {
|
||||
var s Specification
|
||||
err := envconfig.Process("myapp", &s)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
format := "Debug: %v\nPort: %d\nUser: %s\nRate: %f\nTimeout: %s\n"
|
||||
_, err = fmt.Printf(format, s.Debug, s.Port, s.User, s.Rate)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
fmt.Println("Users:")
|
||||
for _, u := range s.Users {
|
||||
fmt.Printf(" %s\n", u)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Results:
|
||||
|
||||
```Bash
|
||||
Debug: false
|
||||
Port: 8080
|
||||
User: Kelsey
|
||||
Rate: 0.500000
|
||||
Timeout: 3m0s
|
||||
Users:
|
||||
rob
|
||||
ken
|
||||
robert
|
||||
```
|
||||
|
||||
## Struct Tag Support
|
||||
|
||||
Envconfig supports the use of struct tags to specify alternate, default, and required
|
||||
environment variables.
|
||||
|
||||
For example, consider the following struct:
|
||||
|
||||
```Go
|
||||
type Specification struct {
|
||||
MultiWordVar string `envconfig:"multi_word_var"`
|
||||
DefaultVar string `default:"foobar"`
|
||||
RequiredVar string `required:"true"`
|
||||
IgnoredVar string `ignored:"true"`
|
||||
}
|
||||
```
|
||||
|
||||
Envconfig will process value for `MultiWordVar` by populating it with the
|
||||
value for `MYAPP_MULTI_WORD_VAR`.
|
||||
|
||||
```Bash
|
||||
export MYAPP_MULTI_WORD_VAR="this will be the value"
|
||||
|
||||
# export MYAPP_MULTIWORDVAR="and this will not"
|
||||
```
|
||||
|
||||
If envconfig can't find an environment variable value for `MYAPP_DEFAULTVAR`,
|
||||
it will populate it with "foobar" as a default value.
|
||||
|
||||
If envconfig can't find an environment variable value for `MYAPP_REQUIREDVAR`,
|
||||
it will return an error when asked to process the struct.
|
||||
|
||||
If envconfig can't find an environment variable in the form `PREFIX_MYVAR`, and there
|
||||
is a struct tag defined, it will try to populate your variable with an environment
|
||||
variable that directly matches the envconfig tag in your struct definition:
|
||||
|
||||
```shell
|
||||
export SERVICE_HOST=127.0.0.1
|
||||
export MYAPP_DEBUG=true
|
||||
```
|
||||
```Go
|
||||
type Specification struct {
|
||||
ServiceHost string `envconfig:"SERVICE_HOST"`
|
||||
Debug bool
|
||||
}
|
||||
```
|
||||
|
||||
Envconfig won't process a field with the "ignored" tag set to "true", even if a corresponding
|
||||
environment variable is set.
|
||||
|
||||
## Supported Struct Field Types
|
||||
|
||||
envconfig supports supports these struct field types:
|
||||
|
||||
* string
|
||||
* int8, int16, int32, int64
|
||||
* bool
|
||||
* float32, float64
|
||||
|
||||
Embedded structs using these fields are also supported.
|
||||
|
||||
## Custom Decoders
|
||||
|
||||
Any field whose type (or pointer-to-type) implements `envconfig.Decoder` can
|
||||
control its own deserialization:
|
||||
|
||||
```Bash
|
||||
export DNS_SERVER=8.8.8.8
|
||||
```
|
||||
|
||||
```Go
|
||||
type IPDecoder net.IP
|
||||
|
||||
func (ipd *IPDecoder) Decode(value string) error {
|
||||
*ipd = IPDecoder(net.ParseIP(value))
|
||||
return nil
|
||||
}
|
||||
|
||||
type DNSConfig struct {
|
||||
Address IPDecoder `envconfig:"DNS_SERVER"`
|
||||
}
|
||||
```
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
|
||||
// Use of this source code is governed by the MIT License that can be found in
|
||||
// the LICENSE file.
|
||||
|
||||
// Package envconfig implements decoding of environment variables based on a user
|
||||
// defined specification. A typical use is using environment variables for
|
||||
// configuration settings.
|
||||
package envconfig
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
|
||||
// Use of this source code is governed by the MIT License that can be found in
|
||||
// the LICENSE file.
|
||||
|
||||
package envconfig
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ErrInvalidSpecification indicates that a specification is of the wrong type.
|
||||
var ErrInvalidSpecification = errors.New("specification must be a struct pointer")
|
||||
|
||||
// A ParseError occurs when an environment variable cannot be converted to
|
||||
// the type required by a struct field during assignment.
|
||||
type ParseError struct {
|
||||
KeyName string
|
||||
FieldName string
|
||||
TypeName string
|
||||
Value string
|
||||
}
|
||||
|
||||
// A Decoder is a type that knows how to de-serialize environment variables
|
||||
// into itself.
|
||||
type Decoder interface {
|
||||
Decode(value string) error
|
||||
}
|
||||
|
||||
func (e *ParseError) Error() string {
|
||||
return fmt.Sprintf("envconfig.Process: assigning %[1]s to %[2]s: converting '%[3]s' to type %[4]s", e.KeyName, e.FieldName, e.Value, e.TypeName)
|
||||
}
|
||||
|
||||
// Process populates the specified struct based on environment variables
|
||||
func Process(prefix string, spec interface{}) error {
|
||||
s := reflect.ValueOf(spec)
|
||||
|
||||
if s.Kind() != reflect.Ptr {
|
||||
return ErrInvalidSpecification
|
||||
}
|
||||
s = s.Elem()
|
||||
if s.Kind() != reflect.Struct {
|
||||
return ErrInvalidSpecification
|
||||
}
|
||||
typeOfSpec := s.Type()
|
||||
for i := 0; i < s.NumField(); i++ {
|
||||
f := s.Field(i)
|
||||
if !f.CanSet() || typeOfSpec.Field(i).Tag.Get("ignored") == "true" {
|
||||
continue
|
||||
}
|
||||
|
||||
if typeOfSpec.Field(i).Anonymous && f.Kind() == reflect.Struct {
|
||||
embeddedPtr := f.Addr().Interface()
|
||||
if err := Process(prefix, embeddedPtr); err != nil {
|
||||
return err
|
||||
}
|
||||
f.Set(reflect.ValueOf(embeddedPtr).Elem())
|
||||
}
|
||||
|
||||
alt := typeOfSpec.Field(i).Tag.Get("envconfig")
|
||||
fieldName := typeOfSpec.Field(i).Name
|
||||
if alt != "" {
|
||||
fieldName = alt
|
||||
}
|
||||
key := strings.ToUpper(fmt.Sprintf("%s_%s", prefix, fieldName))
|
||||
// `os.Getenv` cannot differentiate between an explicitly set empty value
|
||||
// and an unset value. `os.LookupEnv` is preferred to `syscall.Getenv`,
|
||||
// but it is only available in go1.5 or newer.
|
||||
value, ok := syscall.Getenv(key)
|
||||
if !ok && alt != "" {
|
||||
key := strings.ToUpper(fieldName)
|
||||
value, ok = syscall.Getenv(key)
|
||||
}
|
||||
|
||||
def := typeOfSpec.Field(i).Tag.Get("default")
|
||||
if def != "" && !ok {
|
||||
value = def
|
||||
}
|
||||
|
||||
req := typeOfSpec.Field(i).Tag.Get("required")
|
||||
if !ok && def == "" {
|
||||
if req == "true" {
|
||||
return fmt.Errorf("required key %s missing value", key)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
err := processField(value, f)
|
||||
if err != nil {
|
||||
return &ParseError{
|
||||
KeyName: key,
|
||||
FieldName: fieldName,
|
||||
TypeName: f.Type().String(),
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MustProcess is the same as Process but panics if an error occurs
|
||||
func MustProcess(prefix string, spec interface{}) {
|
||||
if err := Process(prefix, spec); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func processField(value string, field reflect.Value) error {
|
||||
typ := field.Type()
|
||||
|
||||
decoder := decoderFrom(field)
|
||||
if decoder != nil {
|
||||
return decoder.Decode(value)
|
||||
}
|
||||
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
if field.IsNil() {
|
||||
field.Set(reflect.New(typ))
|
||||
}
|
||||
field = field.Elem()
|
||||
}
|
||||
|
||||
switch typ.Kind() {
|
||||
case reflect.String:
|
||||
field.SetString(value)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
var (
|
||||
val int64
|
||||
err error
|
||||
)
|
||||
if field.Kind() == reflect.Int64 && typ.PkgPath() == "time" && typ.Name() == "Duration" {
|
||||
var d time.Duration
|
||||
d, err = time.ParseDuration(value)
|
||||
val = int64(d)
|
||||
} else {
|
||||
val, err = strconv.ParseInt(value, 0, typ.Bits())
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
field.SetInt(val)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
val, err := strconv.ParseUint(value, 0, typ.Bits())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
field.SetUint(val)
|
||||
case reflect.Bool:
|
||||
val, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
field.SetBool(val)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
val, err := strconv.ParseFloat(value, typ.Bits())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
field.SetFloat(val)
|
||||
case reflect.Slice:
|
||||
vals := strings.Split(value, ",")
|
||||
sl := reflect.MakeSlice(typ, len(vals), len(vals))
|
||||
for i, val := range vals {
|
||||
err := processField(val, sl.Index(i))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
field.Set(sl)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decoderFrom(field reflect.Value) Decoder {
|
||||
if field.CanInterface() {
|
||||
dec, ok := field.Interface().(Decoder)
|
||||
if ok {
|
||||
return dec
|
||||
}
|
||||
}
|
||||
|
||||
// also check if pointer-to-type implements Decoder,
|
||||
// and we can get a pointer to our field
|
||||
if field.CanAddr() {
|
||||
field = field.Addr()
|
||||
dec, ok := field.Interface().(Decoder)
|
||||
if ok {
|
||||
return dec
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+493
@@ -0,0 +1,493 @@
|
||||
// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
|
||||
// Use of this source code is governed by the MIT License that can be found in
|
||||
// the LICENSE file.
|
||||
|
||||
package envconfig
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Specification struct {
|
||||
Embedded
|
||||
EmbeddedButIgnored `ignored:"true"`
|
||||
Debug bool
|
||||
Port int
|
||||
Rate float32
|
||||
User string
|
||||
TTL uint32
|
||||
Timeout time.Duration
|
||||
AdminUsers []string
|
||||
MagicNumbers []int
|
||||
MultiWordVar string
|
||||
SomePointer *string
|
||||
SomePointerWithDefault *string `default:"foo2baz"`
|
||||
MultiWordVarWithAlt string `envconfig:"MULTI_WORD_VAR_WITH_ALT"`
|
||||
MultiWordVarWithLowerCaseAlt string `envconfig:"multi_word_var_with_lower_case_alt"`
|
||||
NoPrefixWithAlt string `envconfig:"SERVICE_HOST"`
|
||||
DefaultVar string `default:"foobar"`
|
||||
RequiredVar string `required:"true"`
|
||||
NoPrefixDefault string `envconfig:"BROKER" default:"127.0.0.1"`
|
||||
RequiredDefault string `required:"true" default:"foo2bar"`
|
||||
Ignored string `ignored:"true"`
|
||||
}
|
||||
|
||||
type Embedded struct {
|
||||
Enabled bool
|
||||
EmbeddedPort int
|
||||
MultiWordVar string
|
||||
MultiWordVarWithAlt string `envconfig:"MULTI_WITH_DIFFERENT_ALT"`
|
||||
EmbeddedAlt string `envconfig:"EMBEDDED_WITH_ALT"`
|
||||
EmbeddedIgnored string `ignored:"true"`
|
||||
}
|
||||
|
||||
type EmbeddedButIgnored struct {
|
||||
FirstEmbeddedButIgnored string
|
||||
SecondEmbeddedButIgnored string
|
||||
}
|
||||
|
||||
func TestProcess(t *testing.T) {
|
||||
var s Specification
|
||||
os.Clearenv()
|
||||
os.Setenv("ENV_CONFIG_DEBUG", "true")
|
||||
os.Setenv("ENV_CONFIG_PORT", "8080")
|
||||
os.Setenv("ENV_CONFIG_RATE", "0.5")
|
||||
os.Setenv("ENV_CONFIG_USER", "Kelsey")
|
||||
os.Setenv("ENV_CONFIG_TIMEOUT", "2m")
|
||||
os.Setenv("ENV_CONFIG_ADMINUSERS", "John,Adam,Will")
|
||||
os.Setenv("ENV_CONFIG_MAGICNUMBERS", "5,10,20")
|
||||
os.Setenv("SERVICE_HOST", "127.0.0.1")
|
||||
os.Setenv("ENV_CONFIG_TTL", "30")
|
||||
os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
|
||||
os.Setenv("ENV_CONFIG_IGNORED", "was-not-ignored")
|
||||
err := Process("env_config", &s)
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
if s.NoPrefixWithAlt != "127.0.0.1" {
|
||||
t.Errorf("expected %v, got %v", "127.0.0.1", s.NoPrefixWithAlt)
|
||||
}
|
||||
if !s.Debug {
|
||||
t.Errorf("expected %v, got %v", true, s.Debug)
|
||||
}
|
||||
if s.Port != 8080 {
|
||||
t.Errorf("expected %d, got %v", 8080, s.Port)
|
||||
}
|
||||
if s.Rate != 0.5 {
|
||||
t.Errorf("expected %f, got %v", 0.5, s.Rate)
|
||||
}
|
||||
if s.TTL != 30 {
|
||||
t.Errorf("expected %d, got %v", 30, s.TTL)
|
||||
}
|
||||
if s.User != "Kelsey" {
|
||||
t.Errorf("expected %s, got %s", "Kelsey", s.User)
|
||||
}
|
||||
if s.Timeout != 2*time.Minute {
|
||||
t.Errorf("expected %s, got %s", 2*time.Minute, s.Timeout)
|
||||
}
|
||||
if s.RequiredVar != "foo" {
|
||||
t.Errorf("expected %s, got %s", "foo", s.RequiredVar)
|
||||
}
|
||||
if len(s.AdminUsers) != 3 ||
|
||||
s.AdminUsers[0] != "John" ||
|
||||
s.AdminUsers[1] != "Adam" ||
|
||||
s.AdminUsers[2] != "Will" {
|
||||
t.Errorf("expected %#v, got %#v", []string{"John", "Adam", "Will"}, s.AdminUsers)
|
||||
}
|
||||
if len(s.MagicNumbers) != 3 ||
|
||||
s.MagicNumbers[0] != 5 ||
|
||||
s.MagicNumbers[1] != 10 ||
|
||||
s.MagicNumbers[2] != 20 {
|
||||
t.Errorf("expected %#v, got %#v", []int{5, 10, 20}, s.MagicNumbers)
|
||||
}
|
||||
if s.Ignored != "" {
|
||||
t.Errorf("expected empty string, got %#v", s.Ignored)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseErrorBool(t *testing.T) {
|
||||
var s Specification
|
||||
os.Clearenv()
|
||||
os.Setenv("ENV_CONFIG_DEBUG", "string")
|
||||
os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
|
||||
err := Process("env_config", &s)
|
||||
v, ok := err.(*ParseError)
|
||||
if !ok {
|
||||
t.Errorf("expected ParseError, got %v", v)
|
||||
}
|
||||
if v.FieldName != "Debug" {
|
||||
t.Errorf("expected %s, got %v", "Debug", v.FieldName)
|
||||
}
|
||||
if s.Debug != false {
|
||||
t.Errorf("expected %v, got %v", false, s.Debug)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseErrorFloat32(t *testing.T) {
|
||||
var s Specification
|
||||
os.Clearenv()
|
||||
os.Setenv("ENV_CONFIG_RATE", "string")
|
||||
os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
|
||||
err := Process("env_config", &s)
|
||||
v, ok := err.(*ParseError)
|
||||
if !ok {
|
||||
t.Errorf("expected ParseError, got %v", v)
|
||||
}
|
||||
if v.FieldName != "Rate" {
|
||||
t.Errorf("expected %s, got %v", "Rate", v.FieldName)
|
||||
}
|
||||
if s.Rate != 0 {
|
||||
t.Errorf("expected %v, got %v", 0, s.Rate)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseErrorInt(t *testing.T) {
|
||||
var s Specification
|
||||
os.Clearenv()
|
||||
os.Setenv("ENV_CONFIG_PORT", "string")
|
||||
os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
|
||||
err := Process("env_config", &s)
|
||||
v, ok := err.(*ParseError)
|
||||
if !ok {
|
||||
t.Errorf("expected ParseError, got %v", v)
|
||||
}
|
||||
if v.FieldName != "Port" {
|
||||
t.Errorf("expected %s, got %v", "Port", v.FieldName)
|
||||
}
|
||||
if s.Port != 0 {
|
||||
t.Errorf("expected %v, got %v", 0, s.Port)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseErrorUint(t *testing.T) {
|
||||
var s Specification
|
||||
os.Clearenv()
|
||||
os.Setenv("ENV_CONFIG_TTL", "-30")
|
||||
err := Process("env_config", &s)
|
||||
v, ok := err.(*ParseError)
|
||||
if !ok {
|
||||
t.Errorf("expected ParseError, got %v", v)
|
||||
}
|
||||
if v.FieldName != "TTL" {
|
||||
t.Errorf("expected %s, got %v", "TTL", v.FieldName)
|
||||
}
|
||||
if s.TTL != 0 {
|
||||
t.Errorf("expected %v, got %v", 0, s.TTL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrInvalidSpecification(t *testing.T) {
|
||||
m := make(map[string]string)
|
||||
err := Process("env_config", &m)
|
||||
if err != ErrInvalidSpecification {
|
||||
t.Errorf("expected %v, got %v", ErrInvalidSpecification, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnsetVars(t *testing.T) {
|
||||
var s Specification
|
||||
os.Clearenv()
|
||||
os.Setenv("USER", "foo")
|
||||
os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
|
||||
if err := Process("env_config", &s); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
|
||||
// If the var is not defined the non-prefixed version should not be used
|
||||
// unless the struct tag says so
|
||||
if s.User != "" {
|
||||
t.Errorf("expected %q, got %q", "", s.User)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlternateVarNames(t *testing.T) {
|
||||
var s Specification
|
||||
os.Clearenv()
|
||||
os.Setenv("ENV_CONFIG_MULTI_WORD_VAR", "foo")
|
||||
os.Setenv("ENV_CONFIG_MULTI_WORD_VAR_WITH_ALT", "bar")
|
||||
os.Setenv("ENV_CONFIG_MULTI_WORD_VAR_WITH_LOWER_CASE_ALT", "baz")
|
||||
os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
|
||||
if err := Process("env_config", &s); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
|
||||
// Setting the alt version of the var in the environment has no effect if
|
||||
// the struct tag is not supplied
|
||||
if s.MultiWordVar != "" {
|
||||
t.Errorf("expected %q, got %q", "", s.MultiWordVar)
|
||||
}
|
||||
|
||||
// Setting the alt version of the var in the environment correctly sets
|
||||
// the value if the struct tag IS supplied
|
||||
if s.MultiWordVarWithAlt != "bar" {
|
||||
t.Errorf("expected %q, got %q", "bar", s.MultiWordVarWithAlt)
|
||||
}
|
||||
|
||||
// Alt value is not case sensitive and is treated as all uppercase
|
||||
if s.MultiWordVarWithLowerCaseAlt != "baz" {
|
||||
t.Errorf("expected %q, got %q", "baz", s.MultiWordVarWithLowerCaseAlt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequiredVar(t *testing.T) {
|
||||
var s Specification
|
||||
os.Clearenv()
|
||||
os.Setenv("ENV_CONFIG_REQUIREDVAR", "foobar")
|
||||
if err := Process("env_config", &s); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
|
||||
if s.RequiredVar != "foobar" {
|
||||
t.Errorf("expected %s, got %s", "foobar", s.RequiredVar)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlankDefaultVar(t *testing.T) {
|
||||
var s Specification
|
||||
os.Clearenv()
|
||||
os.Setenv("ENV_CONFIG_REQUIREDVAR", "requiredvalue")
|
||||
if err := Process("env_config", &s); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
|
||||
if s.DefaultVar != "foobar" {
|
||||
t.Errorf("expected %s, got %s", "foobar", s.DefaultVar)
|
||||
}
|
||||
|
||||
if *s.SomePointerWithDefault != "foo2baz" {
|
||||
t.Errorf("expected %s, got %s", "foo2baz", *s.SomePointerWithDefault)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonBlankDefaultVar(t *testing.T) {
|
||||
var s Specification
|
||||
os.Clearenv()
|
||||
os.Setenv("ENV_CONFIG_DEFAULTVAR", "nondefaultval")
|
||||
os.Setenv("ENV_CONFIG_REQUIREDVAR", "requiredvalue")
|
||||
if err := Process("env_config", &s); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
|
||||
if s.DefaultVar != "nondefaultval" {
|
||||
t.Errorf("expected %s, got %s", "nondefaultval", s.DefaultVar)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExplicitBlankDefaultVar(t *testing.T) {
|
||||
var s Specification
|
||||
os.Clearenv()
|
||||
os.Setenv("ENV_CONFIG_DEFAULTVAR", "")
|
||||
os.Setenv("ENV_CONFIG_REQUIREDVAR", "")
|
||||
|
||||
if err := Process("env_config", &s); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
|
||||
if s.DefaultVar != "" {
|
||||
t.Errorf("expected %s, got %s", "\"\"", s.DefaultVar)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlternateNameDefaultVar(t *testing.T) {
|
||||
var s Specification
|
||||
os.Clearenv()
|
||||
os.Setenv("BROKER", "betterbroker")
|
||||
os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
|
||||
if err := Process("env_config", &s); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
|
||||
if s.NoPrefixDefault != "betterbroker" {
|
||||
t.Errorf("expected %q, got %q", "betterbroker", s.NoPrefixDefault)
|
||||
}
|
||||
|
||||
os.Clearenv()
|
||||
os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
|
||||
if err := Process("env_config", &s); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
|
||||
if s.NoPrefixDefault != "127.0.0.1" {
|
||||
t.Errorf("expected %q, got %q", "127.0.0.1", s.NoPrefixDefault)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequiredDefault(t *testing.T) {
|
||||
var s Specification
|
||||
os.Clearenv()
|
||||
os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
|
||||
if err := Process("env_config", &s); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
|
||||
if s.RequiredDefault != "foo2bar" {
|
||||
t.Errorf("expected %q, got %q", "foo2bar", s.RequiredDefault)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPointerFieldBlank(t *testing.T) {
|
||||
var s Specification
|
||||
os.Clearenv()
|
||||
os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
|
||||
if err := Process("env_config", &s); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
|
||||
if s.SomePointer != nil {
|
||||
t.Errorf("expected <nil>, got %2", *s.SomePointer)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMustProcess(t *testing.T) {
|
||||
var s Specification
|
||||
os.Clearenv()
|
||||
os.Setenv("ENV_CONFIG_DEBUG", "true")
|
||||
os.Setenv("ENV_CONFIG_PORT", "8080")
|
||||
os.Setenv("ENV_CONFIG_RATE", "0.5")
|
||||
os.Setenv("ENV_CONFIG_USER", "Kelsey")
|
||||
os.Setenv("SERVICE_HOST", "127.0.0.1")
|
||||
os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
|
||||
MustProcess("env_config", &s)
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
t.Error("expected panic")
|
||||
}()
|
||||
m := make(map[string]string)
|
||||
MustProcess("env_config", &m)
|
||||
}
|
||||
|
||||
func TestEmbeddedStruct(t *testing.T) {
|
||||
var s Specification
|
||||
os.Clearenv()
|
||||
os.Setenv("ENV_CONFIG_REQUIREDVAR", "required")
|
||||
os.Setenv("ENV_CONFIG_ENABLED", "true")
|
||||
os.Setenv("ENV_CONFIG_EMBEDDEDPORT", "1234")
|
||||
os.Setenv("ENV_CONFIG_MULTIWORDVAR", "foo")
|
||||
os.Setenv("ENV_CONFIG_MULTI_WORD_VAR_WITH_ALT", "bar")
|
||||
os.Setenv("ENV_CONFIG_MULTI_WITH_DIFFERENT_ALT", "baz")
|
||||
os.Setenv("ENV_CONFIG_EMBEDDED_WITH_ALT", "foobar")
|
||||
os.Setenv("ENV_CONFIG_SOMEPOINTER", "foobaz")
|
||||
os.Setenv("ENV_CONFIG_EMBEDDED_IGNORED", "was-not-ignored")
|
||||
if err := Process("env_config", &s); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
if !s.Enabled {
|
||||
t.Errorf("expected %v, got %v", true, s.Enabled)
|
||||
}
|
||||
if s.EmbeddedPort != 1234 {
|
||||
t.Errorf("expected %d, got %v", 1234, s.EmbeddedPort)
|
||||
}
|
||||
if s.MultiWordVar != "foo" {
|
||||
t.Errorf("expected %s, got %s", "foo", s.MultiWordVar)
|
||||
}
|
||||
if s.Embedded.MultiWordVar != "foo" {
|
||||
t.Errorf("expected %s, got %s", "foo", s.Embedded.MultiWordVar)
|
||||
}
|
||||
if s.MultiWordVarWithAlt != "bar" {
|
||||
t.Errorf("expected %s, got %s", "bar", s.MultiWordVarWithAlt)
|
||||
}
|
||||
if s.Embedded.MultiWordVarWithAlt != "baz" {
|
||||
t.Errorf("expected %s, got %s", "baz", s.Embedded.MultiWordVarWithAlt)
|
||||
}
|
||||
if s.EmbeddedAlt != "foobar" {
|
||||
t.Errorf("expected %s, got %s", "foobar", s.EmbeddedAlt)
|
||||
}
|
||||
if *s.SomePointer != "foobaz" {
|
||||
t.Errorf("expected %s, got %s", "foobaz", *s.SomePointer)
|
||||
}
|
||||
if s.EmbeddedIgnored != "" {
|
||||
t.Errorf("expected empty string, got %#v", s.Ignored)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmbeddedButIgnoredStruct(t *testing.T) {
|
||||
var s Specification
|
||||
os.Clearenv()
|
||||
os.Setenv("ENV_CONFIG_REQUIREDVAR", "required")
|
||||
os.Setenv("ENV_CONFIG_FIRSTEMBEDDEDBUTIGNORED", "was-not-ignored")
|
||||
os.Setenv("ENV_CONFIG_SECONDEMBEDDEDBUTIGNORED", "was-not-ignored")
|
||||
if err := Process("env_config", &s); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
if s.FirstEmbeddedButIgnored != "" {
|
||||
t.Errorf("expected empty string, got %#v", s.Ignored)
|
||||
}
|
||||
if s.SecondEmbeddedButIgnored != "" {
|
||||
t.Errorf("expected empty string, got %#v", s.Ignored)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonPointerFailsProperly(t *testing.T) {
|
||||
var s Specification
|
||||
os.Clearenv()
|
||||
os.Setenv("ENV_CONFIG_REQUIREDVAR", "snap")
|
||||
|
||||
err := Process("env_config", s)
|
||||
if err != ErrInvalidSpecification {
|
||||
t.Errorf("non-pointer should fail with ErrInvalidSpecification, was instead %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomDecoder(t *testing.T) {
|
||||
s := struct {
|
||||
Foo string
|
||||
Bar bracketed
|
||||
}{}
|
||||
|
||||
os.Clearenv()
|
||||
os.Setenv("ENV_CONFIG_FOO", "foo")
|
||||
os.Setenv("ENV_CONFIG_BAR", "bar")
|
||||
|
||||
if err := Process("env_config", &s); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
|
||||
if s.Foo != "foo" {
|
||||
t.Errorf("foo: expected 'foo', got %q", s.Foo)
|
||||
}
|
||||
|
||||
if string(s.Bar) != "[bar]" {
|
||||
t.Errorf("bar: expected '[bar]', got %q", string(s.Bar))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomDecoderWithPointer(t *testing.T) {
|
||||
s := struct {
|
||||
Foo string
|
||||
Bar *bracketed
|
||||
}{}
|
||||
|
||||
// Decode would panic when b is nil, so make sure it
|
||||
// has an initial value to replace.
|
||||
var b bracketed = "initial_value"
|
||||
s.Bar = &b
|
||||
|
||||
os.Clearenv()
|
||||
os.Setenv("ENV_CONFIG_FOO", "foo")
|
||||
os.Setenv("ENV_CONFIG_BAR", "bar")
|
||||
|
||||
if err := Process("env_config", &s); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
|
||||
if s.Foo != "foo" {
|
||||
t.Errorf("foo: expected 'foo', got %q", s.Foo)
|
||||
}
|
||||
|
||||
if string(*s.Bar) != "[bar]" {
|
||||
t.Errorf("bar: expected '[bar]', got %q", string(*s.Bar))
|
||||
}
|
||||
}
|
||||
|
||||
type bracketed string
|
||||
|
||||
func (b *bracketed) Decode(value string) error {
|
||||
*b = bracketed("[" + value + "]")
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user