diff --git a/doc.go b/doc.go index 7f76d07..610f2cd 100644 --- a/doc.go +++ b/doc.go @@ -12,13 +12,12 @@ // // This allows you to Unmarshal JSON or YAML into: // -// type myConfig struct { -// Server config.URL -// TLS config.TLS -// } +// type myConfig struct { +// Server config.URL +// TLS config.TLS +// } // // and have a net/url url.URL value available as cfg.Server.URL, and // a TLS configuration as cfg.TLS.Config, with the config package taking // care of parsing and validation. -// package config diff --git a/env/env.go b/env/env.go index 164ac44..675aadf 100644 --- a/env/env.go +++ b/env/env.go @@ -14,10 +14,10 @@ // Configuration can come from hardcoded defaults, environment, configuration // files, and command line flags. To implement the usual precedence of: // -// 1) built-in defaults (lowest) -// 2) environment parameters -// 3) configuration file parameters -// 4) command line parameters (highest) +// 1. built-in defaults (lowest) +// 2. environment parameters +// 3. configuration file parameters +// 4. command line parameters (highest) // // define defaults and command line bindings (with the flag package) first, // followed by environment bindings with this package. Then load values from @@ -30,6 +30,7 @@ package env import ( + "fmt" "os" "strconv" "time" @@ -155,3 +156,67 @@ func (d *durationValue) Set(s string) error { func DurationVar(d *time.Duration, key string) error { return Var((*durationValue)(d), key) } + +// ErrorHandling has advantage over bool as it can be extended +// to handle additional cases whereas bool cannot +type ErrorHandling int + +const ( + ContinueOnError ErrorHandling = 0 + ExitOnError = 1 +) + +type envConfig struct { + errorHandling ErrorHandling +} + +// NewConfig creates an envConfig object which allows handling of env variable parsing error +func NewConfig(errorHandling ErrorHandling) envConfig { + return envConfig{errorHandling: errorHandling} +} + +// handleError handles envConfig on error behavior +func (e *envConfig) handleError(key string, err error) error { + if err != nil && e.errorHandling == ExitOnError { + fmt.Fprintf(os.Stderr, "%s encountered parsing error: %v\n", key, err) + os.Exit(1) + } + + return err +} + +func (e *envConfig) StringVar(v *string, key string) error { + return e.handleError(key, StringVar(v, key)) +} + +func (e *envConfig) BoolVar(v *bool, key string) error { + return e.handleError(key, BoolVar(v, key)) +} + +func (e *envConfig) IntVar(v *int, key string) error { + return e.handleError(key, IntVar(v, key)) +} + +func (e *envConfig) Int64Var(v *int64, key string) error { + return e.handleError(key, Int64Var(v, key)) +} + +func (e *envConfig) UintVar(v *uint, key string) error { + return e.handleError(key, UintVar(v, key)) +} + +func (e *envConfig) Uint64Var(v *uint64, key string) error { + return e.handleError(key, Uint64Var(v, key)) +} + +func (e *envConfig) Float64Var(v *float64, key string) error { + return e.handleError(key, Float64Var(v, key)) +} + +func (e *envConfig) DurationVar(v *time.Duration, key string) error { + return e.handleError(key, DurationVar(v, key)) +} + +func (e *envConfig) Var(v Value, key string) error { + return e.handleError(key, Var(v, key)) +} diff --git a/env/env_test.go b/env/env_test.go index 9ab76df..38dfb69 100644 --- a/env/env_test.go +++ b/env/env_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2018 Farsight Security, Inc. + * Copyright 2023 Farsight Security, Inc. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -9,6 +9,7 @@ package env import ( + "github.com/farsightsec/go-config" "os" "testing" "time" @@ -59,3 +60,32 @@ func TestEnvMissing(t *testing.T) { checkOK(t, IntVar(&i, "TEST_MISSING"), i == 10) checkOK(t, BoolVar(&b, "TEST_MISSING"), b) } + +func TestEnvConfig(t *testing.T) { + var i int = 0 + var i64 int64 = 0 + var u uint = 0 + var u64 uint64 = 0 + var f64 float64 = 0 + var s string = "" + var b bool = false + var d time.Duration = time.Second + var ss = config.String{} + + ec := NewConfig(ContinueOnError) + + checkOK(t, ec.IntVar(&i, "TEST_NUM"), i == 1048576) + checkOK(t, ec.Int64Var(&i64, "TEST_NUM"), i64 == 1048576) + checkOK(t, ec.UintVar(&u, "TEST_NUM"), u == 1048576) + checkOK(t, ec.Uint64Var(&u64, "TEST_NUM"), u64 == 1048576) + checkOK(t, ec.Float64Var(&f64, "TEST_NUM"), f64 == 1048576) + checkOK(t, ec.StringVar(&s, "TEST_NUM"), s == "1048576") + checkOK(t, ec.Var(&ss, "TEST_NUM"), ss.String() == "1048576") + checkOK(t, ec.DurationVar(&d, "TEST_DURATION"), d == 100*time.Millisecond) + checkOK(t, ec.BoolVar(&b, "TEST_BOOL_TRUE"), b) + checkOK(t, ec.BoolVar(&b, "TEST_BOOL_FALSE"), !b) + + if ec.BoolVar(&b, "TEST_BOOL_INVALID") == nil { + t.Error("Expected parse error") + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..98fbf6c --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module github.com/farsightsec/go-config + +go 1.18 + +require gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..dd0bc19 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/tls.go b/tls.go index 2a29189..47a9b04 100644 --- a/tls.go +++ b/tls.go @@ -21,6 +21,7 @@ import ( // conversion to and from string format. // // Supported string values are: +// // "none": tls.NoClientCert (default) // "request": tls.RequestClientCert // "require": tls.RequireAnyClientCert @@ -49,13 +50,13 @@ func (auth *TLSClientAuth) String() string { type invalidClientAuthType string func (i invalidClientAuthType) Error() string { - return fmt.Sprintf(`Invalid ClientAuthType "%s".`, i) + return fmt.Sprintf(`Invalid ClientAuthType "%s".`, string(i)) } type invalidClientAuthTypeValue tls.ClientAuthType func (i invalidClientAuthTypeValue) Error() string { - return fmt.Sprintf("Invalid ClientAuthType value %v", i) + return fmt.Sprintf("Invalid ClientAuthType value %v", int(i)) } // Set satisfies the flag.Value interface.