Init Viper for config with defaulting
This commit is contained in:
parent
180765e8a6
commit
cf3eac0242
99
Gopkg.lock
generated
99
Gopkg.lock
generated
@ -68,6 +68,14 @@
|
||||
revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
|
||||
version = "v3.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:eb53021a8aa3f599d29c7102e65026242bdedce998a54837dc67f14b6a97c5fd"
|
||||
name = "github.com/fsnotify/fsnotify"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
|
||||
version = "v1.4.7"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:8c7410dae63c74bd92db09bf33af7e0698b635ab6a397fd8e9e10dfcce3138ac"
|
||||
name = "github.com/go-redis/redis"
|
||||
@ -103,6 +111,25 @@
|
||||
revision = "9c11da706d9b7902c6da69c592f75637793fe121"
|
||||
version = "v2.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:d14365c51dd1d34d5c79833ec91413bfbb166be978724f15701e17080dc06dec"
|
||||
name = "github.com/hashicorp/hcl"
|
||||
packages = [
|
||||
".",
|
||||
"hcl/ast",
|
||||
"hcl/parser",
|
||||
"hcl/printer",
|
||||
"hcl/scanner",
|
||||
"hcl/strconv",
|
||||
"hcl/token",
|
||||
"json/parser",
|
||||
"json/scanner",
|
||||
"json/token",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:b3c5b95e56c06f5aa72cb2500e6ee5f44fcd122872d4fec2023a488e561218bc"
|
||||
name = "github.com/hpcloud/tail"
|
||||
@ -117,6 +144,14 @@
|
||||
revision = "a30252cb686a21eb2d0b98132633053ec2f7f1e5"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ae39921edb7f801f7ce1b6b5484f9715a1dd2b52cb645daef095cd10fd6ee774"
|
||||
name = "github.com/magiconair/properties"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "de8848e004dd33dc07a2947b3d76f618a7fc7ef1"
|
||||
version = "v1.8.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:af67386ca553c04c6222f7b5b2f17bc97a5dfb3b81b706882c7fd8c72c30cf8f"
|
||||
name = "github.com/mbland/hmacauth"
|
||||
@ -125,6 +160,14 @@
|
||||
revision = "107c17adcc5eccc9935cd67d9bc2feaf5255d2cb"
|
||||
version = "1.0.2"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:bcc46a0fbd9e933087bef394871256b5c60269575bb661935874729c65bbbf60"
|
||||
name = "github.com/mitchellh/mapstructure"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe"
|
||||
version = "v1.1.2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:15c0562bca5d78ac087fb39c211071dc124e79fb18f8b7c3f8a0bc7ffcb2a38e"
|
||||
@ -181,6 +224,14 @@
|
||||
revision = "90e289841c1ed79b7a598a7cd9959750cb5e89e2"
|
||||
version = "v1.5.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:3d2c33720d4255686b9f4a7e4d3b94938ee36063f14705c5eb0f73347ed4c496"
|
||||
name = "github.com/pelletier/go-toml"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "728039f679cbcd4f6a54e080d2219a4c4928c546"
|
||||
version = "v1.4.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:256484dbbcd271f9ecebc6795b2df8cad4c458dd0f5fd82a8c2fa0c29f233411"
|
||||
name = "github.com/pmezard/go-difflib"
|
||||
@ -200,6 +251,49 @@
|
||||
pruneopts = ""
|
||||
revision = "0dec1b30a0215bb68605dfc568e8855066c9202d"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:956f655c87b7255c6b1ae6c203ebb0af98cf2a13ef2507e34c9bf1c0332ac0f5"
|
||||
name = "github.com/spf13/afero"
|
||||
packages = [
|
||||
".",
|
||||
"mem",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "588a75ec4f32903aa5e39a2619ba6a4631e28424"
|
||||
version = "v1.2.2"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ae3493c780092be9d576a1f746ab967293ec165e8473425631f06658b6212afc"
|
||||
name = "github.com/spf13/cast"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "8c9545af88b134710ab1cd196795e7f2388358d7"
|
||||
version = "v1.3.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:cc15ae4fbdb02ce31f3392361a70ac041f4f02e0485de8ffac92bd8033e3d26e"
|
||||
name = "github.com/spf13/jwalterweatherman"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "94f6ae3ed3bceceafa716478c5fbf8d29ca601a1"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:cbaf13cdbfef0e4734ed8a7504f57fe893d471d62a35b982bf6fb3f036449a66"
|
||||
name = "github.com/spf13/pflag"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "298182f68c66c05229eb03ac171abe6e309ee79a"
|
||||
version = "v1.0.3"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:9b483544fcf4fc0155e566f49ee669c205fba12a36eb18e7f80510796a606db4"
|
||||
name = "github.com/spf13/viper"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "b5bf975e5823809fb22c7644d008757f78a4259e"
|
||||
version = "v1.4.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:3926a4ec9a4ff1a072458451aa2d9b98acd059a45b38f7335d31e06c3d6a0159"
|
||||
name = "github.com/stretchr/testify"
|
||||
@ -300,11 +394,14 @@
|
||||
"internal/language",
|
||||
"internal/language/compact",
|
||||
"internal/tag",
|
||||
"internal/triegen",
|
||||
"internal/ucd",
|
||||
"internal/utf8internal",
|
||||
"language",
|
||||
"runes",
|
||||
"transform",
|
||||
"unicode/cldr",
|
||||
"unicode/norm",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "342b2e1fbaa52c93f31447ad2c6abc048c63e475"
|
||||
@ -409,6 +506,8 @@
|
||||
"github.com/mreiferson/go-options",
|
||||
"github.com/onsi/ginkgo",
|
||||
"github.com/onsi/gomega",
|
||||
"github.com/spf13/pflag",
|
||||
"github.com/spf13/viper",
|
||||
"github.com/stretchr/testify/assert",
|
||||
"github.com/stretchr/testify/require",
|
||||
"github.com/yhat/wsutil",
|
||||
|
@ -1,15 +1,33 @@
|
||||
package options
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
|
||||
flag "github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
// CookieOptions contains configuration options relating to Cookie configuration
|
||||
type CookieOptions struct {
|
||||
CookieName string `flag:"cookie-name" cfg:"cookie_name" env:"OAUTH2_PROXY_COOKIE_NAME"`
|
||||
CookieSecret string `flag:"cookie-secret" cfg:"cookie_secret" env:"OAUTH2_PROXY_COOKIE_SECRET"`
|
||||
CookieDomain string `flag:"cookie-domain" cfg:"cookie_domain" env:"OAUTH2_PROXY_COOKIE_DOMAIN"`
|
||||
CookiePath string `flag:"cookie-path" cfg:"cookie_path" env:"OAUTH2_PROXY_COOKIE_PATH"`
|
||||
CookieExpire time.Duration `flag:"cookie-expire" cfg:"cookie_expire" env:"OAUTH2_PROXY_COOKIE_EXPIRE"`
|
||||
CookieRefresh time.Duration `flag:"cookie-refresh" cfg:"cookie_refresh" env:"OAUTH2_PROXY_COOKIE_REFRESH"`
|
||||
CookieSecure bool `flag:"cookie-secure" cfg:"cookie_secure" env:"OAUTH2_PROXY_COOKIE_SECURE"`
|
||||
CookieHTTPOnly bool `flag:"cookie-httponly" cfg:"cookie_httponly" env:"OAUTH2_PROXY_COOKIE_HTTPONLY"`
|
||||
Name string `flag:"cookie-name" cfg:"cookie_name" env:"OAUTH2_PROXY_COOKIE_NAME" default:"_oauth2_proxy"`
|
||||
Secret string `flag:"cookie-secret" cfg:"cookie_secret" env:"OAUTH2_PROXY_COOKIE_SECRET"`
|
||||
Domain string `flag:"cookie-domain" cfg:"cookie_domain" env:"OAUTH2_PROXY_COOKIE_DOMAIN"`
|
||||
Path string `flag:"cookie-path" cfg:"cookie_path" env:"OAUTH2_PROXY_COOKIE_PATH" default:"/"`
|
||||
Expire time.Duration `flag:"cookie-expire" cfg:"cookie_expire" env:"OAUTH2_PROXY_COOKIE_EXPIRE" default:"604800000000000"`
|
||||
Refresh time.Duration `flag:"cookie-refresh" cfg:"cookie_refresh" env:"OAUTH2_PROXY_COOKIE_REFRESH" default:"0"`
|
||||
Secure bool `flag:"cookie-secure" cfg:"cookie_secure" env:"OAUTH2_PROXY_COOKIE_SECURE" default:"true"`
|
||||
HTTPOnly bool `flag:"cookie-httponly" cfg:"cookie_httponly" env:"OAUTH2_PROXY_COOKIE_HTTPONLY" default:"true"`
|
||||
}
|
||||
|
||||
// cookieFlagSet contains flags related to Cookie configuration
|
||||
var cookieFlagSet = flag.NewFlagSet("cookie", flag.ExitOnError)
|
||||
|
||||
func init() {
|
||||
cookieFlagSet.String("cookie-name", "_oauth2_proxy", "the name of the cookie that the oauth_proxy creates")
|
||||
cookieFlagSet.String("cookie-secret", "", "the seed string for secure cookies (optionally base64 encoded)")
|
||||
cookieFlagSet.String("cookie-domain", "", "an optional cookie domain to force cookies to (ie: .yourcompany.com)*")
|
||||
cookieFlagSet.String("cookie-path", "/", "an optional cookie path to force cookies to (ie: /poc/)*")
|
||||
cookieFlagSet.Duration("cookie-expire", time.Duration(168)*time.Hour, "expire timeframe for cookie")
|
||||
cookieFlagSet.Duration("cookie-refresh", time.Duration(0), "refresh the cookie after this duration; 0 to disable")
|
||||
cookieFlagSet.Bool("cookie-secure", true, "set secure (HTTPS) cookie flag")
|
||||
cookieFlagSet.Bool("cookie-httponly", true, "set HttpOnly cookie flag")
|
||||
}
|
||||
|
68
pkg/apis/options/default.go
Normal file
68
pkg/apis/options/default.go
Normal file
@ -0,0 +1,68 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func defaultStruct(s interface{}) error {
|
||||
val := reflect.ValueOf(s)
|
||||
var typ reflect.Type
|
||||
if val.Kind() == reflect.Ptr {
|
||||
typ = val.Elem().Type()
|
||||
} else {
|
||||
typ = val.Type()
|
||||
}
|
||||
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
fieldV := reflect.Indirect(val).Field(i)
|
||||
|
||||
// If the field is a ptr, recurse
|
||||
if field.Type.Kind() == reflect.Ptr {
|
||||
if fieldV.IsNil() {
|
||||
fieldV.Set(reflect.New(fieldV.Type().Elem()))
|
||||
}
|
||||
err := defaultStruct(fieldV.Interface())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error defaulting %s: %v", field.Name, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// If the field is a ptr, recurse
|
||||
if field.Type.Kind() == reflect.Struct {
|
||||
newField := reflect.New(reflect.TypeOf(fieldV.Interface()))
|
||||
err := defaultStruct(newField.Interface())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error defaulting %s: %v", field.Name, err)
|
||||
}
|
||||
fieldV.Set(newField.Elem())
|
||||
continue
|
||||
}
|
||||
|
||||
defaultVal := field.Tag.Get("default")
|
||||
|
||||
if defaultVal != "" {
|
||||
switch fieldV.Kind() {
|
||||
case reflect.String:
|
||||
fieldV.Set(reflect.ValueOf(defaultVal).Convert(field.Type))
|
||||
case reflect.Bool:
|
||||
boolVal, err := strconv.ParseBool(defaultVal)
|
||||
if err != nil {
|
||||
return fmt.Errorf("expected default value (%s) to be bool: %v", defaultVal, err)
|
||||
}
|
||||
fieldV.Set(reflect.ValueOf(boolVal).Convert(field.Type))
|
||||
case reflect.Int64:
|
||||
intVal, err := strconv.ParseInt(defaultVal, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("expected default value (%s) to be int: %v", defaultVal, err)
|
||||
}
|
||||
fieldV.Set(reflect.ValueOf(intVal).Convert(field.Type))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
60
pkg/apis/options/default_test.go
Normal file
60
pkg/apis/options/default_test.go
Normal file
@ -0,0 +1,60 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("defaultStruct", func() {
|
||||
type InnerTest struct {
|
||||
TestString string `default:"bar"`
|
||||
}
|
||||
|
||||
type Test struct {
|
||||
TestString string `default:"foo"`
|
||||
TestBool bool `default:"true"`
|
||||
TestDuration time.Duration `default:"60000000000"`
|
||||
TestPtrStruct *InnerTest
|
||||
TestStruct InnerTest
|
||||
}
|
||||
|
||||
var testStruct *Test
|
||||
|
||||
BeforeEach(func() {
|
||||
testStruct = &Test{}
|
||||
err := defaultStruct(testStruct)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
Context("with a string field", func() {
|
||||
It("reads the correct default", func() {
|
||||
Expect(testStruct.TestString).To(Equal("foo"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("with a bool field", func() {
|
||||
It("reads the correct default", func() {
|
||||
Expect(testStruct.TestBool).To(Equal(true))
|
||||
})
|
||||
})
|
||||
|
||||
Context("with a duration field", func() {
|
||||
It("reads the correct default", func() {
|
||||
Expect(testStruct.TestDuration).To(Equal(time.Minute))
|
||||
})
|
||||
})
|
||||
|
||||
Context("with a pointer struct field", func() {
|
||||
It("defaults the values in the struct", func() {
|
||||
Expect(testStruct.TestPtrStruct.TestString).To(Equal("bar"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("with a struct field", func() {
|
||||
It("defaults the values in the struct", func() {
|
||||
Expect(testStruct.TestStruct.TestString).To(Equal("bar"))
|
||||
})
|
||||
})
|
||||
})
|
66
pkg/apis/options/options.go
Normal file
66
pkg/apis/options/options.go
Normal file
@ -0,0 +1,66 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
flag "github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// Options contains configuration options for the OAuth2 Proxy
|
||||
type Options struct {
|
||||
Cookie *CookieOptions
|
||||
}
|
||||
|
||||
// New creates a new deafulted copy of the Options struct
|
||||
func New() *Options {
|
||||
options := &Options{}
|
||||
err := defaultStruct(options)
|
||||
if err != nil {
|
||||
// If we get an error here, there must be a code error
|
||||
panic(err)
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
// Load reads a config file, flag arguments and the environment to set the
|
||||
// correct configuration options
|
||||
func Load(config io.Reader, configType string, args []string) (*Options, error) {
|
||||
flagSet := flag.NewFlagSet("oauth2-proxy", flag.ExitOnError)
|
||||
|
||||
// Add FlagSets to main flagSet
|
||||
flagSet.AddFlagSet(cookieFlagSet)
|
||||
|
||||
flagSet.Parse(args)
|
||||
|
||||
// Create a viper for binding config
|
||||
v := viper.New()
|
||||
|
||||
// Bind flags to viper
|
||||
err := v.BindPFlags(flagSet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Configure loading of environment variables
|
||||
// All flag options are prefixed by the EnvPrefix
|
||||
v.SetEnvPrefix("OAUTH2_PROXY_")
|
||||
// Substitute "-" for "_" so `FOO_BAR` matches the flag `foo-bar`
|
||||
v.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
||||
v.AutomaticEnv()
|
||||
|
||||
// Read the configuration file
|
||||
if config != nil {
|
||||
v.ReadConfig(config)
|
||||
}
|
||||
|
||||
// Unmarhsal the config into the Options struct
|
||||
options := New()
|
||||
err = v.Unmarshal(&options)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error unmarshalling config: %v", err)
|
||||
}
|
||||
return options, nil
|
||||
}
|
37
pkg/apis/options/options_test.go
Normal file
37
pkg/apis/options/options_test.go
Normal file
@ -0,0 +1,37 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestOptions(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Options")
|
||||
}
|
||||
|
||||
var _ = Describe("Load", func() {
|
||||
var opts *Options
|
||||
var err error
|
||||
var config io.Reader
|
||||
var configType string
|
||||
var args []string
|
||||
|
||||
JustBeforeEach(func() {
|
||||
opts, err = Load(config, configType, args)
|
||||
})
|
||||
|
||||
Context("with no configuration", func() {
|
||||
It("returns no error", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("returns the default configuration", func() {
|
||||
defaultOpts := New()
|
||||
Expect(opts).To(Equal(defaultOpts))
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue
Block a user