Use env lib, add PROXY_PORT ENV variable

This commit is contained in:
Sergey Bogatyrets 2018-06-13 22:34:35 +03:00
parent 803767b6f7
commit 678a8cc192
13 changed files with 1241 additions and 9 deletions

8
Gopkg.lock generated
View File

@ -7,6 +7,12 @@
packages = ["."] packages = ["."]
revision = "e75332964ef517daa070d7c38a9466a0d687e0a5" revision = "e75332964ef517daa070d7c38a9466a0d687e0a5"
[[projects]]
branch = "master"
name = "github.com/caarlos0/env"
packages = ["."]
revision = "3e0f30cbf50bb82273cf60e7bb70869f4852e9b8"
[[projects]] [[projects]]
branch = "master" branch = "master"
name = "golang.org/x/net" name = "golang.org/x/net"
@ -16,6 +22,6 @@
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"
analyzer-version = 1 analyzer-version = 1
inputs-digest = "bb1b87ff840d0a559073f327d2c493d6ca2c37ed971b13eef6796925f3980a7a" inputs-digest = "b579eb59bf4c9ca68a34a1b121d49796b87e80c2514576b22c53c4507f129c87"
solver-name = "gps-cdcl" solver-name = "gps-cdcl"
solver-version = 1 solver-version = 1

View File

@ -24,3 +24,7 @@
[[constraint]] [[constraint]]
branch = "master" branch = "master"
name = "github.com/armon/go-socks5" name = "github.com/armon/go-socks5"
[[constraint]]
branch = "master"
name = "github.com/caarlos0/env"

View File

@ -1,34 +1,47 @@
package main package main
import ( import (
"fmt"
"log" "log"
"os" "os"
"github.com/armon/go-socks5" "github.com/armon/go-socks5"
"github.com/caarlos0/env"
) )
func main() { type params struct {
User string `env:"PROXY_USER" envDefault:""`
Password string `env:"PROXY_PASSWORD" envDefault:""`
Port string `env:"PROXY_PORT" envDefault:"1080"`
}
conf := &socks5.Config{ func main() {
// Working with app params
cfg := params{}
err := env.Parse(&cfg)
if err != nil {
log.Printf("%+v\n", err)
}
//Initialize socks5 config
socsk5conf := &socks5.Config{
Logger: log.New(os.Stdout, "", log.LstdFlags), Logger: log.New(os.Stdout, "", log.LstdFlags),
} }
if os.Getenv("PROXY_USER")+os.Getenv("PROXY_PASSWORD") != "" { if cfg.User+cfg.Password != "" {
creds := socks5.StaticCredentials{ creds := socks5.StaticCredentials{
os.Getenv("PROXY_USER"): os.Getenv("PROXY_PASSWORD"), os.Getenv("PROXY_USER"): os.Getenv("PROXY_PASSWORD"),
} }
cator := socks5.UserPassAuthenticator{Credentials: creds} cator := socks5.UserPassAuthenticator{Credentials: creds}
conf.AuthMethods = []socks5.Authenticator{cator} socsk5conf.AuthMethods = []socks5.Authenticator{cator}
} }
server, err := socks5.New(conf) server, err := socks5.New(socsk5conf)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
fmt.Println("Start listening ...") log.Printf("Start listening proxy service on port %s\n", cfg.Port)
if err := server.ListenAndServe("tcp", ":1080"); err != nil { if err := server.ListenAndServe("tcp", ":"+cfg.Port); err != nil {
log.Fatal(err) log.Fatal(err)
} }
} }

1
vendor/github.com/caarlos0/env/.gitignore generated vendored Normal file
View File

@ -0,0 +1 @@
coverage.out

2
vendor/github.com/caarlos0/env/.hound.yml generated vendored Normal file
View File

@ -0,0 +1,2 @@
go:
enabled: true

16
vendor/github.com/caarlos0/env/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,16 @@
language: go
go:
- 1.5
- 1.6
- 1.7
- 1.8
- tip
before_install:
- go get github.com/axw/gocov/gocov
- go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover
script:
- go test -v -cover -race -coverprofile=coverage.out
after_script:
- go get github.com/mattn/goveralls
- goveralls -coverprofile=coverage.out -service=travis-ci -repotoken='eCcizKmTdSaJCz8Ih33WDppdqb9kioYwi'

21
vendor/github.com/caarlos0/env/LICENSE.md generated vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015-2016 Carlos Alexandro Becker
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.

115
vendor/github.com/caarlos0/env/README.md generated vendored Normal file
View File

@ -0,0 +1,115 @@
# env [![Build Status](https://travis-ci.org/caarlos0/env.svg?branch=master)](https://travis-ci.org/caarlos0/env) [![Coverage Status](https://coveralls.io/repos/caarlos0/env/badge.svg?branch=master&service=github)](https://coveralls.io/github/caarlos0/env?branch=master) [![](https://godoc.org/github.com/caarlos0/env?status.svg)](http://godoc.org/github.com/caarlos0/env) [![](http://goreportcard.com/badge/caarlos0/env)](http://goreportcard.com/report/caarlos0/env) [![SayThanks.io](https://img.shields.io/badge/SayThanks.io-%E2%98%BC-1EAEDB.svg?style=flat-square)](https://saythanks.io/to/caarlos0)
A KISS way to deal with environment variables in Go.
## Why
At first, it was boring for me to write down an entire function just to
get some `var` from the environment and default to another in case it's missing.
For that manner, I wrote a `GetOr` function in the
[go-idioms](https://github.com/caarlos0/go-idioms) project.
Then, I got pissed about writing `os.Getenv`, `os.Setenv`, `os.Unsetenv`...
it kind of make more sense to me write it as `env.Get`, `env.Set`, `env.Unset`.
So I did.
Then I got a better idea: to use `struct` tags to do all that work for me.
## Example
A very basic example (check the `examples` folder):
```go
package main
import (
"fmt"
"time"
"github.com/caarlos0/env"
)
type config struct {
Home string `env:"HOME"`
Port int `env:"PORT" envDefault:"3000"`
IsProduction bool `env:"PRODUCTION"`
Hosts []string `env:"HOSTS" envSeparator:":"`
Duration time.Duration `env:"DURATION"`
}
func main() {
cfg := config{}
err := env.Parse(&cfg)
if err != nil {
fmt.Printf("%+v\n", err)
}
fmt.Printf("%+v\n", cfg)
}
```
You can run it like this:
```sh
$ PRODUCTION=true HOSTS="host1:host2:host3" DURATION=1s go run examples/first.go
{Home:/your/home Port:3000 IsProduction:true Hosts:[host1 host2 host3] Duration:1s}
```
## Supported types and defaults
The library has built-in support for the following types:
* `string`
* `int`
* `uint`
* `int64`
* `bool`
* `float32`
* `float64`
* `time.Duration`
* `[]string`
* `[]int`
* `[]bool`
* `[]float32`
* `[]float64`
* `[]time.Duration`
* .. or use/define a [custom parser func](#custom-parser-funcs) for any other type
If you set the `envDefault` tag for something, this value will be used in the
case of absence of it in the environment. If you don't do that AND the
environment variable is also not set, the zero-value
of the type will be used: empty for `string`s, `false` for `bool`s
and `0` for `int`s.
By default, slice types will split the environment value on `,`; you can change this behavior by setting the `envSeparator` tag.
## Custom Parser Funcs
If you have a type that is not supported out of the box by the lib, you are able
to use (or define) and pass custom parsers (and their associated `reflect.Type`) to the
`env.ParseWithFuncs()` function.
In addition to accepting a struct pointer (same as `Parse()`), this function also
accepts a `env.CustomParsers` arg that under the covers is a `map[reflect.Type]env.ParserFunc`.
To see what this looks like in practice, take a look at the [commented block in the example](https://github.com/caarlos0/env/blob/master/examples/first.go#L35-L39).
`env` also ships with some pre-built custom parser funcs for common types. You
can check them out [here](parsers/).
## Required fields
The `env` tag option `required` (e.g., `env:"tagKey,required"`) can be added
to ensure that some environment variable is set. In the example above,
an error is returned if the `config` struct is changed to:
```go
type config struct {
Home string `env:"HOME"`
Port int `env:"PORT" envDefault:"3000"`
IsProduction bool `env:"PRODUCTION"`
Hosts []string `env:"HOSTS" envSeparator:":"`
SecretKey string `env:"SECRET_KEY,required"`
}
```

398
vendor/github.com/caarlos0/env/env.go generated vendored Normal file
View File

@ -0,0 +1,398 @@
package env
import (
"errors"
"fmt"
"os"
"reflect"
"strconv"
"strings"
"time"
)
var (
// ErrNotAStructPtr is returned if you pass something that is not a pointer to a
// Struct to Parse
ErrNotAStructPtr = errors.New("Expected a pointer to a Struct")
// ErrUnsupportedType if the struct field type is not supported by env
ErrUnsupportedType = errors.New("Type is not supported")
// ErrUnsupportedSliceType if the slice element type is not supported by env
ErrUnsupportedSliceType = errors.New("Unsupported slice type")
// OnEnvVarSet is an optional convenience callback, such as for logging purposes.
// If not nil, it's called after successfully setting the given field from the given value.
OnEnvVarSet func(reflect.StructField, string)
// Friendly names for reflect types
sliceOfInts = reflect.TypeOf([]int(nil))
sliceOfInt64s = reflect.TypeOf([]int64(nil))
sliceOfUint64s = reflect.TypeOf([]uint64(nil))
sliceOfStrings = reflect.TypeOf([]string(nil))
sliceOfBools = reflect.TypeOf([]bool(nil))
sliceOfFloat32s = reflect.TypeOf([]float32(nil))
sliceOfFloat64s = reflect.TypeOf([]float64(nil))
sliceOfDurations = reflect.TypeOf([]time.Duration(nil))
)
// CustomParsers is a friendly name for the type that `ParseWithFuncs()` accepts
type CustomParsers map[reflect.Type]ParserFunc
// ParserFunc defines the signature of a function that can be used within `CustomParsers`
type ParserFunc func(v string) (interface{}, error)
// Parse parses a struct containing `env` tags and loads its values from
// environment variables.
func Parse(v interface{}) error {
ptrRef := reflect.ValueOf(v)
if ptrRef.Kind() != reflect.Ptr {
return ErrNotAStructPtr
}
ref := ptrRef.Elem()
if ref.Kind() != reflect.Struct {
return ErrNotAStructPtr
}
return doParse(ref, make(map[reflect.Type]ParserFunc, 0))
}
// ParseWithFuncs is the same as `Parse` except it also allows the user to pass
// in custom parsers.
func ParseWithFuncs(v interface{}, funcMap CustomParsers) error {
ptrRef := reflect.ValueOf(v)
if ptrRef.Kind() != reflect.Ptr {
return ErrNotAStructPtr
}
ref := ptrRef.Elem()
if ref.Kind() != reflect.Struct {
return ErrNotAStructPtr
}
return doParse(ref, funcMap)
}
func doParse(ref reflect.Value, funcMap CustomParsers) error {
refType := ref.Type()
var errorList []string
for i := 0; i < refType.NumField(); i++ {
refField := ref.Field(i)
if reflect.Ptr == refField.Kind() && !refField.IsNil() && refField.CanSet() {
err := Parse(refField.Interface())
if nil != err {
return err
}
continue
}
refTypeField := refType.Field(i)
value, err := get(refTypeField)
if err != nil {
errorList = append(errorList, err.Error())
continue
}
if value == "" {
continue
}
if err := set(refField, refTypeField, value, funcMap); err != nil {
errorList = append(errorList, err.Error())
continue
}
if OnEnvVarSet != nil {
OnEnvVarSet(refTypeField, value)
}
}
if len(errorList) == 0 {
return nil
}
return errors.New(strings.Join(errorList, ". "))
}
func get(field reflect.StructField) (string, error) {
var (
val string
err error
)
key, opts := parseKeyForOption(field.Tag.Get("env"))
defaultValue := field.Tag.Get("envDefault")
val = getOr(key, defaultValue)
if len(opts) > 0 {
for _, opt := range opts {
// The only option supported is "required".
switch opt {
case "":
break
case "required":
val, err = getRequired(key)
default:
err = errors.New("Env tag option " + opt + " not supported.")
}
}
}
return val, err
}
// split the env tag's key into the expected key and desired option, if any.
func parseKeyForOption(key string) (string, []string) {
opts := strings.Split(key, ",")
return opts[0], opts[1:]
}
func getRequired(key string) (string, error) {
if value, ok := os.LookupEnv(key); ok {
return value, nil
}
// We do not use fmt.Errorf to avoid another import.
return "", errors.New("Required environment variable " + key + " is not set")
}
func getOr(key, defaultValue string) string {
value, ok := os.LookupEnv(key)
if ok {
return value
}
return defaultValue
}
func set(field reflect.Value, refType reflect.StructField, value string, funcMap CustomParsers) error {
switch field.Kind() {
case reflect.Slice:
separator := refType.Tag.Get("envSeparator")
return handleSlice(field, value, separator)
case reflect.String:
field.SetString(value)
case reflect.Bool:
bvalue, err := strconv.ParseBool(value)
if err != nil {
return err
}
field.SetBool(bvalue)
case reflect.Int:
intValue, err := strconv.ParseInt(value, 10, 32)
if err != nil {
return err
}
field.SetInt(intValue)
case reflect.Uint:
uintValue, err := strconv.ParseUint(value, 10, 32)
if err != nil {
return err
}
field.SetUint(uintValue)
case reflect.Float32:
v, err := strconv.ParseFloat(value, 32)
if err != nil {
return err
}
field.SetFloat(v)
case reflect.Float64:
v, err := strconv.ParseFloat(value, 64)
if err != nil {
return err
}
field.Set(reflect.ValueOf(v))
case reflect.Int64:
if refType.Type.String() == "time.Duration" {
dValue, err := time.ParseDuration(value)
if err != nil {
return err
}
field.Set(reflect.ValueOf(dValue))
} else {
intValue, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return err
}
field.SetInt(intValue)
}
case reflect.Uint64:
uintValue, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
field.SetUint(uintValue)
case reflect.Struct:
return handleStruct(field, refType, value, funcMap)
default:
parserFunc, ok := funcMap[refType.Type]
if !ok {
return ErrUnsupportedType
}
val, err := parserFunc(value)
if err != nil {
return err
}
field.Set(reflect.ValueOf(val))
}
return nil
}
func handleStruct(field reflect.Value, refType reflect.StructField, value string, funcMap CustomParsers) error {
// Does the custom parser func map contain this type?
parserFunc, ok := funcMap[field.Type()]
if !ok {
// Map does not contain a custom parser for this type
return ErrUnsupportedType
}
// Call on the custom parser func
data, err := parserFunc(value)
if err != nil {
return fmt.Errorf("Custom parser error: %v", err)
}
// Set the field to the data returned by the customer parser func
rv := reflect.ValueOf(data)
field.Set(rv)
return nil
}
func handleSlice(field reflect.Value, value, separator string) error {
if separator == "" {
separator = ","
}
splitData := strings.Split(value, separator)
switch field.Type() {
case sliceOfStrings:
field.Set(reflect.ValueOf(splitData))
case sliceOfInts:
intData, err := parseInts(splitData)
if err != nil {
return err
}
field.Set(reflect.ValueOf(intData))
case sliceOfInt64s:
int64Data, err := parseInt64s(splitData)
if err != nil {
return err
}
field.Set(reflect.ValueOf(int64Data))
case sliceOfUint64s:
uint64Data, err := parseUint64s(splitData)
if err != nil {
return err
}
field.Set(reflect.ValueOf(uint64Data))
case sliceOfFloat32s:
data, err := parseFloat32s(splitData)
if err != nil {
return err
}
field.Set(reflect.ValueOf(data))
case sliceOfFloat64s:
data, err := parseFloat64s(splitData)
if err != nil {
return err
}
field.Set(reflect.ValueOf(data))
case sliceOfBools:
boolData, err := parseBools(splitData)
if err != nil {
return err
}
field.Set(reflect.ValueOf(boolData))
case sliceOfDurations:
durationData, err := parseDurations(splitData)
if err != nil {
return err
}
field.Set(reflect.ValueOf(durationData))
default:
return ErrUnsupportedSliceType
}
return nil
}
func parseInts(data []string) ([]int, error) {
intSlice := make([]int, 0, len(data))
for _, v := range data {
intValue, err := strconv.ParseInt(v, 10, 32)
if err != nil {
return nil, err
}
intSlice = append(intSlice, int(intValue))
}
return intSlice, nil
}
func parseInt64s(data []string) ([]int64, error) {
intSlice := make([]int64, 0, len(data))
for _, v := range data {
intValue, err := strconv.ParseInt(v, 10, 64)
if err != nil {
return nil, err
}
intSlice = append(intSlice, int64(intValue))
}
return intSlice, nil
}
func parseUint64s(data []string) ([]uint64, error) {
var uintSlice []uint64
for _, v := range data {
uintValue, err := strconv.ParseUint(v, 10, 64)
if err != nil {
return nil, err
}
uintSlice = append(uintSlice, uint64(uintValue))
}
return uintSlice, nil
}
func parseFloat32s(data []string) ([]float32, error) {
float32Slice := make([]float32, 0, len(data))
for _, v := range data {
data, err := strconv.ParseFloat(v, 32)
if err != nil {
return nil, err
}
float32Slice = append(float32Slice, float32(data))
}
return float32Slice, nil
}
func parseFloat64s(data []string) ([]float64, error) {
float64Slice := make([]float64, 0, len(data))
for _, v := range data {
data, err := strconv.ParseFloat(v, 64)
if err != nil {
return nil, err
}
float64Slice = append(float64Slice, float64(data))
}
return float64Slice, nil
}
func parseBools(data []string) ([]bool, error) {
boolSlice := make([]bool, 0, len(data))
for _, v := range data {
bvalue, err := strconv.ParseBool(v)
if err != nil {
return nil, err
}
boolSlice = append(boolSlice, bvalue)
}
return boolSlice, nil
}
func parseDurations(data []string) ([]time.Duration, error) {
durationSlice := make([]time.Duration, 0, len(data))
for _, v := range data {
dvalue, err := time.ParseDuration(v)
if err != nil {
return nil, err
}
durationSlice = append(durationSlice, dvalue)
}
return durationSlice, nil
}

550
vendor/github.com/caarlos0/env/env_test.go generated vendored Normal file
View File

@ -0,0 +1,550 @@
package env_test
import (
"errors"
"fmt"
"net/http"
"os"
"reflect"
"strconv"
"testing"
"time"
"github.com/caarlos0/env"
"github.com/stretchr/testify/assert"
)
type Config struct {
Some string `env:"somevar"`
Other bool `env:"othervar"`
Port int `env:"PORT"`
Int64Val int64 `env:"INT64VAL"`
UintVal uint `env:"UINTVAL"`
Uint64Val uint64 `env:"UINT64VAL"`
NotAnEnv string
DatabaseURL string `env:"DATABASE_URL" envDefault:"postgres://localhost:5432/db"`
Strings []string `env:"STRINGS"`
SepStrings []string `env:"SEPSTRINGS" envSeparator:":"`
Numbers []int `env:"NUMBERS"`
Numbers64 []int64 `env:"NUMBERS64"`
UNumbers64 []uint64 `env:"UNUMBERS64"`
Bools []bool `env:"BOOLS"`
Duration time.Duration `env:"DURATION"`
Float32 float32 `env:"FLOAT32"`
Float64 float64 `env:"FLOAT64"`
Float32s []float32 `env:"FLOAT32S"`
Float64s []float64 `env:"FLOAT64S"`
Durations []time.Duration `env:"DURATIONS"`
}
type ParentStruct struct {
InnerStruct *InnerStruct
unexported *InnerStruct
Ignored *http.Client
}
type InnerStruct struct {
Inner string `env:"innervar"`
Number uint `env:"innernum"`
}
func TestParsesEnv(t *testing.T) {
os.Setenv("somevar", "somevalue")
os.Setenv("othervar", "true")
os.Setenv("PORT", "8080")
os.Setenv("STRINGS", "string1,string2,string3")
os.Setenv("SEPSTRINGS", "string1:string2:string3")
os.Setenv("NUMBERS", "1,2,3,4")
os.Setenv("NUMBERS64", "1,2,2147483640,-2147483640")
os.Setenv("UNUMBERS64", "1,2,214748364011,9147483641")
os.Setenv("BOOLS", "t,TRUE,0,1")
os.Setenv("DURATION", "1s")
os.Setenv("FLOAT32", "3.40282346638528859811704183484516925440e+38")
os.Setenv("FLOAT64", "1.797693134862315708145274237317043567981e+308")
os.Setenv("FLOAT32S", "1.0,2.0,3.0")
os.Setenv("FLOAT64S", "1.0,2.0,3.0")
os.Setenv("UINTVAL", "44")
os.Setenv("UINT64VAL", "6464")
os.Setenv("INT64VAL", "-7575")
os.Setenv("DURATIONS", "1s,2s,3s")
defer os.Clearenv()
cfg := Config{}
assert.NoError(t, env.Parse(&cfg))
assert.Equal(t, "somevalue", cfg.Some)
assert.Equal(t, true, cfg.Other)
assert.Equal(t, 8080, cfg.Port)
assert.Equal(t, uint(44), cfg.UintVal)
assert.Equal(t, int64(-7575), cfg.Int64Val)
assert.Equal(t, uint64(6464), cfg.Uint64Val)
assert.Equal(t, []string{"string1", "string2", "string3"}, cfg.Strings)
assert.Equal(t, []string{"string1", "string2", "string3"}, cfg.SepStrings)
assert.Equal(t, []int{1, 2, 3, 4}, cfg.Numbers)
assert.Equal(t, []int64{1, 2, 2147483640, -2147483640}, cfg.Numbers64)
assert.Equal(t, []uint64{1, 2, 214748364011, 9147483641}, cfg.UNumbers64)
assert.Equal(t, []bool{true, true, false, true}, cfg.Bools)
d1, _ := time.ParseDuration("1s")
assert.Equal(t, d1, cfg.Duration)
f32 := float32(3.40282346638528859811704183484516925440e+38)
assert.Equal(t, f32, cfg.Float32)
f64 := float64(1.797693134862315708145274237317043567981e+308)
assert.Equal(t, f64, cfg.Float64)
assert.Equal(t, []float32{float32(1.0), float32(2.0), float32(3.0)}, cfg.Float32s)
assert.Equal(t, []float64{float64(1.0), float64(2.0), float64(3.0)}, cfg.Float64s)
d2, _ := time.ParseDuration("2s")
d3, _ := time.ParseDuration("3s")
assert.Equal(t, []time.Duration{d1, d2, d3}, cfg.Durations)
}
func TestParsesEnvInner(t *testing.T) {
os.Setenv("innervar", "someinnervalue")
defer os.Clearenv()
cfg := ParentStruct{
InnerStruct: &InnerStruct{},
unexported: &InnerStruct{},
}
assert.NoError(t, env.Parse(&cfg))
assert.Equal(t, "someinnervalue", cfg.InnerStruct.Inner)
}
func TestParsesEnvInnerNil(t *testing.T) {
os.Setenv("innervar", "someinnervalue")
defer os.Clearenv()
cfg := ParentStruct{}
assert.NoError(t, env.Parse(&cfg))
}
func TestParsesEnvInnerInvalid(t *testing.T) {
os.Setenv("innernum", "-547")
defer os.Clearenv()
cfg := ParentStruct{
InnerStruct: &InnerStruct{},
}
assert.Error(t, env.Parse(&cfg))
}
func TestEmptyVars(t *testing.T) {
cfg := Config{}
assert.NoError(t, env.Parse(&cfg))
assert.Equal(t, "", cfg.Some)
assert.Equal(t, false, cfg.Other)
assert.Equal(t, 0, cfg.Port)
assert.Equal(t, uint(0), cfg.UintVal)
assert.Equal(t, uint64(0), cfg.Uint64Val)
assert.Equal(t, int64(0), cfg.Int64Val)
assert.Equal(t, 0, len(cfg.Strings))
assert.Equal(t, 0, len(cfg.SepStrings))
assert.Equal(t, 0, len(cfg.Numbers))
assert.Equal(t, 0, len(cfg.Bools))
}
func TestPassAnInvalidPtr(t *testing.T) {
var thisShouldBreak int
assert.Error(t, env.Parse(&thisShouldBreak))
}
func TestPassReference(t *testing.T) {
cfg := Config{}
assert.Error(t, env.Parse(cfg))
}
func TestInvalidBool(t *testing.T) {
os.Setenv("othervar", "should-be-a-bool")
defer os.Clearenv()
cfg := Config{}
assert.Error(t, env.Parse(&cfg))
}
func TestInvalidInt(t *testing.T) {
os.Setenv("PORT", "should-be-an-int")
defer os.Clearenv()
cfg := Config{}
assert.Error(t, env.Parse(&cfg))
}
func TestInvalidUint(t *testing.T) {
os.Setenv("UINTVAL", "-44")
defer os.Clearenv()
cfg := Config{}
assert.Error(t, env.Parse(&cfg))
}
func TestInvalidFloat32(t *testing.T) {
os.Setenv("FLOAT32", "AAA")
defer os.Clearenv()
cfg := Config{}
assert.Error(t, env.Parse(&cfg))
}
func TestInvalidFloat64(t *testing.T) {
os.Setenv("FLOAT64", "AAA")
defer os.Clearenv()
cfg := Config{}
assert.Error(t, env.Parse(&cfg))
}
func TestInvalidUint64(t *testing.T) {
os.Setenv("UINT64VAL", "AAA")
defer os.Clearenv()
cfg := Config{}
assert.Error(t, env.Parse(&cfg))
}
func TestInvalidInt64(t *testing.T) {
os.Setenv("INT64VAL", "AAA")
defer os.Clearenv()
cfg := Config{}
assert.Error(t, env.Parse(&cfg))
}
func TestInvalidInt64Slice(t *testing.T) {
type config struct {
BadFloats []int64 `env:"BADINTS"`
}
os.Setenv("BADINTS", "A,2,3")
cfg := &config{}
assert.Error(t, env.Parse(cfg))
}
func TestInvalidUInt64Slice(t *testing.T) {
type config struct {
BadFloats []uint64 `env:"BADINTS"`
}
os.Setenv("BADFLOATS", "A,2,3")
cfg := &config{}
assert.Error(t, env.Parse(cfg))
}
func TestInvalidFloat32Slice(t *testing.T) {
type config struct {
BadFloats []float32 `env:"BADFLOATS"`
}
os.Setenv("BADFLOATS", "A,2.0,3.0")
cfg := &config{}
assert.Error(t, env.Parse(cfg))
}
func TestInvalidFloat64Slice(t *testing.T) {
type config struct {
BadFloats []float64 `env:"BADFLOATS"`
}
os.Setenv("BADFLOATS", "A,2.0,3.0")
cfg := &config{}
assert.Error(t, env.Parse(cfg))
}
func TestInvalidBoolsSlice(t *testing.T) {
type config struct {
BadBools []bool `env:"BADBOOLS"`
}
os.Setenv("BADBOOLS", "t,f,TRUE,faaaalse")
cfg := &config{}
assert.Error(t, env.Parse(cfg))
}
func TestInvalidDuration(t *testing.T) {
os.Setenv("DURATION", "should-be-a-valid-duration")
defer os.Clearenv()
cfg := Config{}
assert.Error(t, env.Parse(&cfg))
}
func TestInvalidDurations(t *testing.T) {
os.Setenv("DURATIONS", "1s,contains-an-invalid-duration,3s")
defer os.Clearenv()
cfg := Config{}
assert.Error(t, env.Parse(&cfg))
}
func TestParsesDefaultConfig(t *testing.T) {
cfg := Config{}
assert.NoError(t, env.Parse(&cfg))
assert.Equal(t, "postgres://localhost:5432/db", cfg.DatabaseURL)
}
func TestParseStructWithoutEnvTag(t *testing.T) {
cfg := Config{}
assert.NoError(t, env.Parse(&cfg))
assert.Empty(t, cfg.NotAnEnv)
}
func TestParseStructWithInvalidFieldKind(t *testing.T) {
type config struct {
WontWorkByte byte `env:"BLAH"`
}
os.Setenv("BLAH", "a")
cfg := config{}
assert.Error(t, env.Parse(&cfg))
}
func TestUnsupportedSliceType(t *testing.T) {
type config struct {
WontWork []map[int]int `env:"WONTWORK"`
}
os.Setenv("WONTWORK", "1,2,3")
defer os.Clearenv()
cfg := &config{}
assert.Error(t, env.Parse(cfg))
}
func TestBadSeparator(t *testing.T) {
type config struct {
WontWork []int `env:"WONTWORK" envSeparator:":"`
}
cfg := &config{}
os.Setenv("WONTWORK", "1,2,3,4")
defer os.Clearenv()
assert.Error(t, env.Parse(cfg))
}
func TestNoErrorRequiredSet(t *testing.T) {
type config struct {
IsRequired string `env:"IS_REQUIRED,required"`
}
cfg := &config{}
os.Setenv("IS_REQUIRED", "val")
defer os.Clearenv()
assert.NoError(t, env.Parse(cfg))
assert.Equal(t, "val", cfg.IsRequired)
}
func TestErrorRequiredNotSet(t *testing.T) {
type config struct {
IsRequired string `env:"IS_REQUIRED,required"`
}
cfg := &config{}
assert.Error(t, env.Parse(cfg))
}
func TestCustomParser(t *testing.T) {
type foo struct {
name string
}
type config struct {
Var foo `env:"VAR"`
}
os.Setenv("VAR", "test")
customParserFunc := func(v string) (interface{}, error) {
return foo{name: v}, nil
}
cfg := &config{}
err := env.ParseWithFuncs(cfg, map[reflect.Type]env.ParserFunc{
reflect.TypeOf(foo{}): customParserFunc,
})
assert.NoError(t, err)
assert.Equal(t, cfg.Var.name, "test")
}
func TestParseWithFuncsNoPtr(t *testing.T) {
type foo struct{}
err := env.ParseWithFuncs(foo{}, nil)
assert.Error(t, err)
assert.Equal(t, err, env.ErrNotAStructPtr)
}
func TestParseWithFuncsInvalidType(t *testing.T) {
var c int
err := env.ParseWithFuncs(&c, nil)
assert.Error(t, err)
assert.Equal(t, err, env.ErrNotAStructPtr)
}
func TestCustomParserError(t *testing.T) {
type foo struct {
name string
}
type config struct {
Var foo `env:"VAR"`
}
os.Setenv("VAR", "test")
customParserFunc := func(v string) (interface{}, error) {
return nil, errors.New("something broke")
}
cfg := &config{}
err := env.ParseWithFuncs(cfg, map[reflect.Type]env.ParserFunc{
reflect.TypeOf(foo{}): customParserFunc,
})
assert.Empty(t, cfg.Var.name, "Var.name should not be filled out when parse errors")
assert.Error(t, err)
assert.Equal(t, err.Error(), "Custom parser error: something broke")
}
func TestCustomParserBasicType(t *testing.T) {
type ConstT int32
type config struct {
Const ConstT `env:"CONST_VAL"`
}
exp := ConstT(123)
os.Setenv("CONST_VAL", fmt.Sprintf("%d", exp))
customParserFunc := func(v string) (interface{}, error) {
i, err := strconv.Atoi(v)
if err != nil {
return nil, err
}
r := ConstT(i)
return r, nil
}
cfg := &config{}
err := env.ParseWithFuncs(cfg, map[reflect.Type]env.ParserFunc{
reflect.TypeOf(ConstT(0)): customParserFunc,
})
assert.NoError(t, err)
assert.Equal(t, exp, cfg.Const)
}
func TypeCustomParserBasicInvalid(t *testing.T) {
type ConstT int32
type config struct {
Const ConstT `env:"CONST_VAL"`
}
os.Setenv("CONST_VAL", "foobar")
expErr := errors.New("Random error")
customParserFunc := func(_ string) (interface{}, error) {
return nil, expErr
}
cfg := &config{}
err := env.ParseWithFuncs(cfg, map[reflect.Type]env.ParserFunc{
reflect.TypeOf(ConstT(0)): customParserFunc,
})
assert.Empty(t, cfg.Const)
assert.Error(t, err)
assert.Equal(t, expErr, err)
}
func TestCustomParserBasicUnsupported(t *testing.T) {
type ConstT int32
type config struct {
Const ConstT `env:"CONST_VAL"`
}
exp := ConstT(123)
os.Setenv("CONST_VAL", fmt.Sprintf("%d", exp))
cfg := &config{}
err := env.Parse(cfg)
assert.Zero(t, cfg.Const)
assert.Error(t, err)
assert.Equal(t, env.ErrUnsupportedType, err)
}
func TestUnsupportedStructType(t *testing.T) {
type config struct {
Foo http.Client `env:"FOO"`
}
os.Setenv("FOO", "foo")
cfg := &config{}
err := env.Parse(cfg)
assert.Error(t, err)
assert.Equal(t, env.ErrUnsupportedType, err)
}
func TestEmptyOption(t *testing.T) {
type config struct {
Var string `env:"VAR,"`
}
cfg := &config{}
os.Setenv("VAR", "val")
defer os.Clearenv()
assert.NoError(t, env.Parse(cfg))
assert.Equal(t, "val", cfg.Var)
}
func TestErrorOptionNotRecognized(t *testing.T) {
type config struct {
Var string `env:"VAR,not_supported!"`
}
cfg := &config{}
assert.Error(t, env.Parse(cfg))
}
func ExampleParse() {
type config struct {
Home string `env:"HOME"`
Port int `env:"PORT" envDefault:"3000"`
IsProduction bool `env:"PRODUCTION"`
}
os.Setenv("HOME", "/tmp/fakehome")
cfg := config{}
env.Parse(&cfg)
fmt.Println(cfg)
// Output: {/tmp/fakehome 3000 false}
}
func ExampleParseRequiredField() {
type config struct {
Home string `env:"HOME"`
Port int `env:"PORT" envDefault:"3000"`
IsProduction bool `env:"PRODUCTION"`
SecretKey string `env:"SECRET_KEY,required"`
}
os.Setenv("HOME", "/tmp/fakehome")
cfg := config{}
err := env.Parse(&cfg)
fmt.Println(err)
// Output: Required environment variable SECRET_KEY is not set
}
func ExampleParseMultipleOptions() {
type config struct {
Home string `env:"HOME"`
Port int `env:"PORT" envDefault:"3000"`
IsProduction bool `env:"PRODUCTION"`
SecretKey string `env:"SECRET_KEY,required,option1"`
}
os.Setenv("HOME", "/tmp/fakehome")
cfg := config{}
err := env.Parse(&cfg)
fmt.Println(err)
// Output: Env tag option option1 not supported.
}

48
vendor/github.com/caarlos0/env/examples/first.go generated vendored Normal file
View File

@ -0,0 +1,48 @@
package main
import (
"fmt"
"log"
"strings"
"time"
"github.com/caarlos0/env"
)
type config struct {
Home string `env:"HOME"`
Port int `env:"PORT" envDefault:"3000"`
IsProduction bool `env:"PRODUCTION"`
Hosts []string `env:"HOSTS" envSeparator:":"`
Duration time.Duration `env:"DURATION"`
ExampleFoo Foo `env:"EXAMPLE_FOO"`
}
type Foo struct {
Name string
}
func main() {
cfg := config{}
// Parse for built-in types
if err := env.Parse(&cfg); err != nil {
log.Fatal("Unable to parse envs: ", err)
}
// OR w/ a custom parser for `Foo`
//
// if err := env.ParseWithFuncs(&cfg, env.CustomParsers{
// reflect.TypeOf(Foo{}): fooParser,
// }); err != nil {
// log.Fatal("Unable to parse envs: ", err)
// }
fmt.Printf("%+v\n", cfg)
}
func fooParser(value string) (interface{}, error) {
return Foo{
Name: strings.ToUpper(value),
}, nil
}

35
vendor/github.com/caarlos0/env/parsers/README.md generated vendored Normal file
View File

@ -0,0 +1,35 @@
parsers
=======
This directory contains pre-built, custom parsers that can be used with `env.ParseWithFuncs`
to facilitate the parsing of envs that are not basic types.
Example Usage:
```golang
package main
import (
"fmt"
"log"
"net/url"
"github.com/caarlos0/env"
"github.com/caarlos0/env/parsers"
)
type config struct {
ExampleURL url.URL `env:"EXAMPLE_URL" envDefault:"https://google.com"`
}
func main() {
cfg := config{}
if err := env.ParseWithFuncs(&cfg, env.CustomParsers{
parsers.URLType: parsers.URLFunc,
}); err != nil {
log.Fatal("Unable to parse envs: ", err)
}
fmt.Printf("Scheme: %v Host: %v\n", cfg.ExampleURL.Scheme, cfg.ExampleURL.Host)
}
```

23
vendor/github.com/caarlos0/env/parsers/parsers.go generated vendored Normal file
View File

@ -0,0 +1,23 @@
// Package parsers contains custom parser funcs for common, non-built-in types
package parsers
import (
"fmt"
"net/url"
"reflect"
)
var (
// URLType is a helper var that represents the `reflect.Type`` of `url.URL`
URLType = reflect.TypeOf(url.URL{})
)
// URLFunc is a basic parser for the url.URL type that should be used with `env.ParseWithFuncs()`
func URLFunc(v string) (interface{}, error) {
u, err := url.Parse(v)
if err != nil {
return nil, fmt.Errorf("Unable to complete URL parse: %v", err)
}
return *u, nil
}