diff --git a/go.mod b/go.mod index 74c4b86..6cc6b4d 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.23.3 require ( github.com/hashicorp/memberlist v0.5.0 - github.com/kelseyhightower/envconfig v1.3.0 + github.com/kelseyhightower/envconfig v1.4.0 github.com/mitchellh/goamz v0.0.0-20150317174335-caaaea8b30ee github.com/prometheus/client_golang v1.11.1 github.com/thraxil/randwalk v0.0.0-20170713144412-615038b03cd6 diff --git a/go.sum b/go.sum index b9212cf..6e902b5 100644 --- a/go.sum +++ b/go.sum @@ -85,8 +85,8 @@ github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/ github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kelseyhightower/envconfig v1.3.0 h1:IvRS4f2VcIQy6j4ORGIf9145T/AsUB+oY8LyvN8BXNM= -github.com/kelseyhightower/envconfig v1.3.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= +github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= +github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= diff --git a/vendor/github.com/kelseyhightower/envconfig/.travis.yml b/vendor/github.com/kelseyhightower/envconfig/.travis.yml index e15301a..04b97ae 100644 --- a/vendor/github.com/kelseyhightower/envconfig/.travis.yml +++ b/vendor/github.com/kelseyhightower/envconfig/.travis.yml @@ -1,7 +1,13 @@ language: go go: - - 1.4 - - 1.5 - - 1.6 + - 1.4.x + - 1.5.x + - 1.6.x + - 1.7.x + - 1.8.x + - 1.9.x + - 1.10.x + - 1.11.x + - 1.12.x - tip diff --git a/vendor/github.com/kelseyhightower/envconfig/README.md b/vendor/github.com/kelseyhightower/envconfig/README.md index b6c65a8..33408d6 100644 --- a/vendor/github.com/kelseyhightower/envconfig/README.md +++ b/vendor/github.com/kelseyhightower/envconfig/README.md @@ -1,6 +1,6 @@ # envconfig -[![Build Status](https://travis-ci.org/kelseyhightower/envconfig.png)](https://travis-ci.org/kelseyhightower/envconfig) +[![Build Status](https://travis-ci.org/kelseyhightower/envconfig.svg)](https://travis-ci.org/kelseyhightower/envconfig) ```Go import "github.com/kelseyhightower/envconfig" @@ -54,7 +54,7 @@ func main() { 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) + _, err = fmt.Printf(format, s.Debug, s.Port, s.User, s.Rate, s.Timeout) if err != nil { log.Fatal(err.Error()) } @@ -103,6 +103,7 @@ type Specification struct { RequiredVar string `required:"true"` IgnoredVar string `ignored:"true"` AutoSplitVar string `split_words:"true"` + RequiredAndAutoSplitVar string `required:"true" split_words:"true"` } ``` @@ -128,7 +129,8 @@ 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. +it will return an error when asked to process the struct. If +`MYAPP_REQUIREDVAR` is present but empty, envconfig will not return an error. 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 @@ -150,7 +152,7 @@ environment variable is set. ## Supported Struct Field Types -envconfig supports supports these struct field types: +envconfig supports these struct field types: * string * int8, int16, int32, int64 @@ -159,6 +161,8 @@ envconfig supports supports these struct field types: * slices of any supported type * maps (keys and values of any supported type) * [encoding.TextUnmarshaler](https://golang.org/pkg/encoding/#TextUnmarshaler) + * [encoding.BinaryUnmarshaler](https://golang.org/pkg/encoding/#BinaryUnmarshaler) + * [time.Duration](https://golang.org/pkg/time/#Duration) Embedded structs using these fields are also supported. diff --git a/vendor/github.com/kelseyhightower/envconfig/env_os.go b/vendor/github.com/kelseyhightower/envconfig/env_os.go index a6a014a..eba07a6 100644 --- a/vendor/github.com/kelseyhightower/envconfig/env_os.go +++ b/vendor/github.com/kelseyhightower/envconfig/env_os.go @@ -1,4 +1,4 @@ -// +build appengine +// +build appengine go1.5 package envconfig diff --git a/vendor/github.com/kelseyhightower/envconfig/env_syscall.go b/vendor/github.com/kelseyhightower/envconfig/env_syscall.go index 9d98085..4254540 100644 --- a/vendor/github.com/kelseyhightower/envconfig/env_syscall.go +++ b/vendor/github.com/kelseyhightower/envconfig/env_syscall.go @@ -1,4 +1,4 @@ -// +build !appengine +// +build !appengine,!go1.5 package envconfig diff --git a/vendor/github.com/kelseyhightower/envconfig/envconfig.go b/vendor/github.com/kelseyhightower/envconfig/envconfig.go index 892d746..3f16108 100644 --- a/vendor/github.com/kelseyhightower/envconfig/envconfig.go +++ b/vendor/github.com/kelseyhightower/envconfig/envconfig.go @@ -8,6 +8,7 @@ import ( "encoding" "errors" "fmt" + "os" "reflect" "regexp" "strconv" @@ -18,6 +19,9 @@ import ( // ErrInvalidSpecification indicates that a specification is of the wrong type. var ErrInvalidSpecification = errors.New("specification must be a struct pointer") +var gatherRegexp = regexp.MustCompile("([^A-Z]+|[A-Z]+[^A-Z]+|[A-Z]+)") +var acronymRegexp = regexp.MustCompile("([A-Z]+)([A-Z][^A-Z]+)") + // A ParseError occurs when an environment variable cannot be converted to // the type required by a struct field during assignment. type ParseError struct { @@ -55,7 +59,6 @@ type varInfo struct { // GatherInfo gathers information about the specified struct func gatherInfo(prefix string, spec interface{}) ([]varInfo, error) { - expr := regexp.MustCompile("([^A-Z]+|[A-Z][^A-Z]+|[A-Z]+)") s := reflect.ValueOf(spec) if s.Kind() != reflect.Ptr { @@ -72,7 +75,7 @@ func gatherInfo(prefix string, spec interface{}) ([]varInfo, error) { for i := 0; i < s.NumField(); i++ { f := s.Field(i) ftype := typeOfSpec.Field(i) - if !f.CanSet() || ftype.Tag.Get("ignored") == "true" { + if !f.CanSet() || isTrue(ftype.Tag.Get("ignored")) { continue } @@ -100,12 +103,16 @@ func gatherInfo(prefix string, spec interface{}) ([]varInfo, error) { info.Key = info.Name // Best effort to un-pick camel casing as separate words - if ftype.Tag.Get("split_words") == "true" { - words := expr.FindAllStringSubmatch(ftype.Name, -1) + if isTrue(ftype.Tag.Get("split_words")) { + words := gatherRegexp.FindAllStringSubmatch(ftype.Name, -1) if len(words) > 0 { var name []string for _, words := range words { - name = append(name, words[0]) + if m := acronymRegexp.FindStringSubmatch(words[0]); len(m) == 3 { + name = append(name, m[1], m[2]) + } else { + name = append(name, words[0]) + } } info.Key = strings.Join(name, "_") @@ -122,7 +129,7 @@ func gatherInfo(prefix string, spec interface{}) ([]varInfo, error) { if f.Kind() == reflect.Struct { // honor Decode if present - if decoderFrom(f) == nil && setterFrom(f) == nil && textUnmarshaler(f) == nil { + if decoderFrom(f) == nil && setterFrom(f) == nil && textUnmarshaler(f) == nil && binaryUnmarshaler(f) == nil { innerPrefix := prefix if !ftype.Anonymous { innerPrefix = info.Key @@ -142,6 +149,37 @@ func gatherInfo(prefix string, spec interface{}) ([]varInfo, error) { return infos, nil } +// CheckDisallowed checks that no environment variables with the prefix are set +// that we don't know how or want to parse. This is likely only meaningful with +// a non-empty prefix. +func CheckDisallowed(prefix string, spec interface{}) error { + infos, err := gatherInfo(prefix, spec) + if err != nil { + return err + } + + vars := make(map[string]struct{}) + for _, info := range infos { + vars[info.Key] = struct{}{} + } + + if prefix != "" { + prefix = strings.ToUpper(prefix) + "_" + } + + for _, env := range os.Environ() { + if !strings.HasPrefix(env, prefix) { + continue + } + v := strings.SplitN(env, "=", 2)[0] + if _, found := vars[v]; !found { + return fmt.Errorf("unknown environment variable %s", v) + } + } + + return nil +} + // Process populates the specified struct based on environment variables func Process(prefix string, spec interface{}) error { infos, err := gatherInfo(prefix, spec) @@ -164,13 +202,17 @@ func Process(prefix string, spec interface{}) error { req := info.Tags.Get("required") if !ok && def == "" { - if req == "true" { - return fmt.Errorf("required key %s missing value", info.Key) + if isTrue(req) { + key := info.Key + if info.Alt != "" { + key = info.Alt + } + return fmt.Errorf("required key %s missing value", key) } continue } - err := processField(value, info.Field) + err = processField(value, info.Field) if err != nil { return &ParseError{ KeyName: info.Key, @@ -209,6 +251,10 @@ func processField(value string, field reflect.Value) error { return t.UnmarshalText([]byte(value)) } + if b := binaryUnmarshaler(field); b != nil { + return b.UnmarshalBinary([]byte(value)) + } + if typ.Kind() == reflect.Ptr { typ = typ.Elem() if field.IsNil() { @@ -256,34 +302,41 @@ func processField(value string, field reflect.Value) error { } 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 + sl := reflect.MakeSlice(typ, 0, 0) + if typ.Elem().Kind() == reflect.Uint8 { + sl = reflect.ValueOf([]byte(value)) + } else if len(strings.TrimSpace(value)) != 0 { + 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) case reflect.Map: - pairs := strings.Split(value, ",") mp := reflect.MakeMap(typ) - for _, pair := range pairs { - kvpair := strings.Split(pair, ":") - if len(kvpair) != 2 { - return fmt.Errorf("invalid map item: %q", pair) - } - k := reflect.New(typ.Key()).Elem() - err := processField(kvpair[0], k) - if err != nil { - return err - } - v := reflect.New(typ.Elem()).Elem() - err = processField(kvpair[1], v) - if err != nil { - return err + if len(strings.TrimSpace(value)) != 0 { + pairs := strings.Split(value, ",") + for _, pair := range pairs { + kvpair := strings.Split(pair, ":") + if len(kvpair) != 2 { + return fmt.Errorf("invalid map item: %q", pair) + } + k := reflect.New(typ.Key()).Elem() + err := processField(kvpair[0], k) + if err != nil { + return err + } + v := reflect.New(typ.Elem()).Elem() + err = processField(kvpair[1], v) + if err != nil { + return err + } + mp.SetMapIndex(k, v) } - mp.SetMapIndex(k, v) } field.Set(mp) } @@ -317,3 +370,13 @@ func textUnmarshaler(field reflect.Value) (t encoding.TextUnmarshaler) { interfaceFrom(field, func(v interface{}, ok *bool) { t, *ok = v.(encoding.TextUnmarshaler) }) return t } + +func binaryUnmarshaler(field reflect.Value) (b encoding.BinaryUnmarshaler) { + interfaceFrom(field, func(v interface{}, ok *bool) { b, *ok = v.(encoding.BinaryUnmarshaler) }) + return b +} + +func isTrue(s string) bool { + b, _ := strconv.ParseBool(s) + return b +} diff --git a/vendor/github.com/kelseyhightower/envconfig/usage.go b/vendor/github.com/kelseyhightower/envconfig/usage.go index 1846353..1e6d0a8 100644 --- a/vendor/github.com/kelseyhightower/envconfig/usage.go +++ b/vendor/github.com/kelseyhightower/envconfig/usage.go @@ -27,7 +27,7 @@ variables can be used: [default] {{usage_default .}} [required] {{usage_required .}}{{end}} ` - // DefaultTableFormat constant to use to display usage in a tabluar format + // DefaultTableFormat constant to use to display usage in a tabular format DefaultTableFormat = `This application is configured via the environment. The following environment variables can be used: @@ -37,9 +37,10 @@ KEY TYPE DEFAULT REQUIRED DESCRIPTION ) var ( - decoderType = reflect.TypeOf((*Decoder)(nil)).Elem() - setterType = reflect.TypeOf((*Setter)(nil)).Elem() - unmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() + decoderType = reflect.TypeOf((*Decoder)(nil)).Elem() + setterType = reflect.TypeOf((*Setter)(nil)).Elem() + textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() + binaryUnmarshalerType = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem() ) func implementsInterface(t reflect.Type) bool { @@ -47,14 +48,19 @@ func implementsInterface(t reflect.Type) bool { reflect.PtrTo(t).Implements(decoderType) || t.Implements(setterType) || reflect.PtrTo(t).Implements(setterType) || - t.Implements(unmarshalerType) || - reflect.PtrTo(t).Implements(unmarshalerType) + t.Implements(textUnmarshalerType) || + reflect.PtrTo(t).Implements(textUnmarshalerType) || + t.Implements(binaryUnmarshalerType) || + reflect.PtrTo(t).Implements(binaryUnmarshalerType) } // toTypeDescription converts Go types into a human readable description func toTypeDescription(t reflect.Type) string { switch t.Kind() { case reflect.Array, reflect.Slice: + if t.Elem().Kind() == reflect.Uint8 { + return "String" + } return fmt.Sprintf("Comma-separated list of %s", toTypeDescription(t.Elem())) case reflect.Map: return fmt.Sprintf( @@ -103,7 +109,7 @@ func toTypeDescription(t reflect.Type) string { return fmt.Sprintf("%+v", t) } -// Usage writes usage information to stderr using the default header and table format +// Usage writes usage information to stdout using the default header and table format func Usage(prefix string, spec interface{}) error { // The default is to output the usage information as a table // Create tabwriter instance to support table output diff --git a/vendor/modules.txt b/vendor/modules.txt index 02ed050..8d6b316 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -42,7 +42,7 @@ github.com/hashicorp/golang-lru/simplelru # github.com/hashicorp/memberlist v0.5.0 ## explicit; go 1.12 github.com/hashicorp/memberlist -# github.com/kelseyhightower/envconfig v1.3.0 +# github.com/kelseyhightower/envconfig v1.4.0 ## explicit github.com/kelseyhightower/envconfig # github.com/matttproud/golang_protobuf_extensions v1.0.1