git.iko.soy/typeable/oauth2-proxy
on master help sign in
Introduce alpha configuration loading
commitf36dfbb494e3e28021784a75247d479ad56af47d authorJoel Speed <joel.speed@hotmail.co.uk> dateMon 9 Nov 2020, 20:17 (6 years ago) parent5b003a5
10 files changed+550−21
go.mod+1 go.sum+3 main.go+78−18 main_suite_test.go+16 main_test.go+215 pkg/apis/options/alpha_options.go+8 pkg/apis/options/legacy_options.go+9 pkg/apis/options/load.go+28 pkg/apis/options/load_test.go+192−1 pkg/apis/options/options.go−2
go.mod+1
@@ -11,6 +11,7 @@ require (
1111 github.com/dgrijalva/jwt-go v3.2.0+incompatible
1212 github.com/frankban/quicktest v1.10.0 // indirect
1313 github.com/fsnotify/fsnotify v1.4.9
14+ github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32
1415 github.com/go-redis/redis/v8 v8.2.3
1516 github.com/justinas/alice v1.2.0
1617 github.com/mbland/hmacauth v0.0.0-20170912233209-44256dfd4bfa
go.sum+3
@@ -64,7 +64,10 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
6464github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
6565github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
6666github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
67+github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
6768github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
69+github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew=
70+github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
6871github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
6972github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
7073github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
main.go+78−18
@@ -12,36 +12,26 @@ import (
1212 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
1313 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
1414 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/validation"
15+ "github.com/spf13/pflag"
1516)
1617
1718func main() {
1819 logger.SetFlags(logger.Lshortfile)
19− flagSet := options.NewFlagSet()
2020
21− config := flagSet.String("config", "", "path to config file")
22− showVersion := flagSet.Bool("version", false, "print version string")
23−
24− err := flagSet.Parse(os.Args[1:])
25− if err != nil {
26− logger.Printf("ERROR: Failed to parse flags: %v", err)
27− os.Exit(1)
28− }
21+ configFlagSet := pflag.NewFlagSet("oauth2-proxy", pflag.ContinueOnError)
22+ config := configFlagSet.String("config", "", "path to config file")
23+ alphaConfig := configFlagSet.String("alpha-config", "", "path to alpha config file (use at your own risk - the structure in this config file may change between minor releases)")
24+ showVersion := configFlagSet.Bool("version", false, "print version string")
25+ configFlagSet.Parse(os.Args[1:])
2926
3027 if *showVersion {
3128 fmt.Printf("oauth2-proxy %s (built with %s)\n", VERSION, runtime.Version())
3229 return
3330 }
3431
35− legacyOpts := options.NewLegacyOptions()
36− err = options.Load(*config, flagSet, legacyOpts)
37− if err != nil {
38− logger.Errorf("ERROR: Failed to load config: %v", err)
39− os.Exit(1)
40− }
41−
42− opts, err := legacyOpts.ToOptions()
32+ opts, err := loadConfiguration(*config, *alphaConfig, configFlagSet, os.Args[1:])
4333 if err != nil {
44− logger.Errorf("ERROR: Failed to convert config: %v", err)
34+ logger.Printf("ERROR: %v", err)
4535 os.Exit(1)
4636 }
4737
@@ -74,3 +64,73 @@ func main() {
7464 }()
7565 s.ListenAndServe()
7666}
67+
68+// loadConfiguration will load in the user's configuration.
69+// It will either load the alpha configuration (if alphaConfig is given)
70+// or the legacy configuration.
71+func loadConfiguration(config, alphaConfig string, extraFlags *pflag.FlagSet, args []string) (*options.Options, error) {
72+ if alphaConfig != "" {
73+ logger.Printf("WARNING: You are using alpha configuration. The structure in this configuration file may change without notice. You MUST remove conflicting options from your existing configuration.")
74+ return loadAlphaOptions(config, alphaConfig, extraFlags, args)
75+ }
76+ return loadLegacyOptions(config, extraFlags, args)
77+}
78+
79+// loadLegacyOptions loads the old toml options using the legacy flagset
80+// and legacy options struct.
81+func loadLegacyOptions(config string, extraFlags *pflag.FlagSet, args []string) (*options.Options, error) {
82+ optionsFlagSet := options.NewLegacyFlagSet()
83+ optionsFlagSet.AddFlagSet(extraFlags)
84+ if err := optionsFlagSet.Parse(args); err != nil {
85+ return nil, fmt.Errorf("failed to parse flags: %v", err)
86+ }
87+
88+ legacyOpts := options.NewLegacyOptions()
89+ if err := options.Load(config, optionsFlagSet, legacyOpts); err != nil {
90+ return nil, fmt.Errorf("failed to load config: %v", err)
91+ }
92+
93+ opts, err := legacyOpts.ToOptions()
94+ if err != nil {
95+ return nil, fmt.Errorf("failed to convert config: %v", err)
96+ }
97+
98+ return opts, nil
99+}
100+
101+// loadAlphaOptions loads the old style config excluding options converted to
102+// the new alpha format, then merges the alpha options, loaded from YAML,
103+// into the core configuration.
104+func loadAlphaOptions(config, alphaConfig string, extraFlags *pflag.FlagSet, args []string) (*options.Options, error) {
105+ opts, err := loadOptions(config, extraFlags, args)
106+ if err != nil {
107+ return nil, fmt.Errorf("failed to load core options: %v", err)
108+ }
109+
110+ alphaOpts := &options.AlphaOptions{}
111+ if err := options.LoadYAML(alphaConfig, alphaOpts); err != nil {
112+ return nil, fmt.Errorf("failed to load alpha options: %v", err)
113+ }
114+
115+ alphaOpts.MergeInto(opts)
116+ return opts, nil
117+}
118+
119+// loadOptions loads the configuration using the old style format into the
120+// core options.Options struct.
121+// This means that none of the options that have been converted to alpha config
122+// will be loaded using this method.
123+func loadOptions(config string, extraFlags *pflag.FlagSet, args []string) (*options.Options, error) {
124+ optionsFlagSet := options.NewFlagSet()
125+ optionsFlagSet.AddFlagSet(extraFlags)
126+ if err := optionsFlagSet.Parse(args); err != nil {
127+ return nil, fmt.Errorf("failed to parse flags: %v", err)
128+ }
129+
130+ opts := options.NewOptions()
131+ if err := options.Load(config, optionsFlagSet, opts); err != nil {
132+ return nil, fmt.Errorf("failed to load config: %v", err)
133+ }
134+
135+ return opts, nil
136+}
main_suite_test.gonew file+16
@@ -0,0 +1,16 @@
1+package main
2+
3+import (
4+ "testing"
5+
6+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
7+ . "github.com/onsi/ginkgo"
8+ . "github.com/onsi/gomega"
9+)
10+
11+func TestMainSuite(t *testing.T) {
12+ logger.SetOutput(GinkgoWriter)
13+
14+ RegisterFailHandler(Fail)
15+ RunSpecs(t, "Main Suite")
16+}
main_test.gonew file+215
@@ -0,0 +1,215 @@
1+package main
2+
3+import (
4+ "errors"
5+ "io/ioutil"
6+ "os"
7+ "time"
8+
9+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
10+ . "github.com/onsi/ginkgo"
11+ . "github.com/onsi/ginkgo/extensions/table"
12+ . "github.com/onsi/gomega"
13+ "github.com/spf13/pflag"
14+)
15+
16+var _ = Describe("Configuration Loading Suite", func() {
17+ const testLegacyConfig = `
18+upstreams="http://httpbin"
19+set_basic_auth="true"
20+basic_auth_password="super-secret-password"
21+`
22+
23+ const testAlphaConfig = `
24+upstreams:
25+ - id: /
26+ path: /
27+ uri: http://httpbin
28+ flushInterval: 1s
29+ passHostHeader: true
30+ proxyWebSockets: true
31+injectRequestHeaders:
32+- name: Authorization
33+ values:
34+ - claim: user
35+ prefix: "Basic "
36+ basicAuthPassword:
37+ value: c3VwZXItc2VjcmV0LXBhc3N3b3Jk
38+- name: X-Forwarded-Groups
39+ values:
40+ - claim: groups
41+- name: X-Forwarded-User
42+ values:
43+ - claim: user
44+- name: X-Forwarded-Email
45+ values:
46+ - claim: email
47+- name: X-Forwarded-Preferred-Username
48+ values:
49+ - claim: preferred_username
50+injectResponseHeaders:
51+- name: Authorization
52+ values:
53+ - claim: user
54+ prefix: "Basic "
55+ basicAuthPassword:
56+ value: c3VwZXItc2VjcmV0LXBhc3N3b3Jk
57+`
58+
59+ const testCoreConfig = `
60+http_address="0.0.0.0:4180"
61+cookie_secret="OQINaROshtE9TcZkNAm-5Zs2Pv3xaWytBmc5W7sPX7w="
62+provider="oidc"
63+email_domains="example.com"
64+oidc_issuer_url="http://dex.localhost:4190/dex"
65+client_secret="b2F1dGgyLXByb3h5LWNsaWVudC1zZWNyZXQK"
66+client_id="oauth2-proxy"
67+cookie_secure="false"
68+
69+redirect_url="http://localhost:4180/oauth2/callback"
70+`
71+
72+ boolPtr := func(b bool) *bool {
73+ return &b
74+ }
75+
76+ durationPtr := func(d time.Duration) *options.Duration {
77+ du := options.Duration(d)
78+ return &du
79+ }
80+
81+ testExpectedOptions := func() *options.Options {
82+ opts, err := options.NewLegacyOptions().ToOptions()
83+ Expect(err).ToNot(HaveOccurred())
84+
85+ opts.HTTPAddress = "0.0.0.0:4180"
86+ opts.Cookie.Secret = "OQINaROshtE9TcZkNAm-5Zs2Pv3xaWytBmc5W7sPX7w="
87+ opts.ProviderType = "oidc"
88+ opts.EmailDomains = []string{"example.com"}
89+ opts.OIDCIssuerURL = "http://dex.localhost:4190/dex"
90+ opts.ClientSecret = "b2F1dGgyLXByb3h5LWNsaWVudC1zZWNyZXQK"
91+ opts.ClientID = "oauth2-proxy"
92+ opts.Cookie.Secure = false
93+ opts.RawRedirectURL = "http://localhost:4180/oauth2/callback"
94+
95+ opts.UpstreamServers = options.Upstreams{
96+ {
97+ ID: "/",
98+ Path: "/",
99+ URI: "http://httpbin",
100+ FlushInterval: durationPtr(options.DefaultUpstreamFlushInterval),
101+ PassHostHeader: boolPtr(true),
102+ ProxyWebSockets: boolPtr(true),
103+ },
104+ }
105+
106+ authHeader := options.Header{
107+ Name: "Authorization",
108+ Values: []options.HeaderValue{
109+ {
110+ ClaimSource: &options.ClaimSource{
111+ Claim: "user",
112+ Prefix: "Basic ",
113+ BasicAuthPassword: &options.SecretSource{
114+ Value: []byte("super-secret-password"),
115+ },
116+ },
117+ },
118+ },
119+ }
120+
121+ opts.InjectRequestHeaders = append([]options.Header{authHeader}, opts.InjectRequestHeaders...)
122+ opts.InjectResponseHeaders = append(opts.InjectResponseHeaders, authHeader)
123+ return opts
124+ }
125+
126+ type loadConfigurationTableInput struct {
127+ configContent string
128+ alphaConfigContent string
129+ args []string
130+ extraFlags func() *pflag.FlagSet
131+ expectedOptions func() *options.Options
132+ expectedErr error
133+ }
134+
135+ DescribeTable("LoadConfiguration",
136+ func(in loadConfigurationTableInput) {
137+ var configFileName, alphaConfigFileName string
138+
139+ defer func() {
140+ if configFileName != "" {
141+ Expect(os.Remove(configFileName)).To(Succeed())
142+ }
143+ if alphaConfigFileName != "" {
144+ Expect(os.Remove(alphaConfigFileName)).To(Succeed())
145+ }
146+ }()
147+
148+ if in.configContent != "" {
149+ By("Writing the config to a temporary file", func() {
150+ file, err := ioutil.TempFile("", "oauth2-proxy-test-config-XXXX.cfg")
151+ Expect(err).ToNot(HaveOccurred())
152+ defer file.Close()
153+
154+ configFileName = file.Name()
155+
156+ _, err = file.WriteString(in.configContent)
157+ Expect(err).ToNot(HaveOccurred())
158+ })
159+ }
160+
161+ if in.alphaConfigContent != "" {
162+ By("Writing the config to a temporary file", func() {
163+ file, err := ioutil.TempFile("", "oauth2-proxy-test-alpha-config-XXXX.yaml")
164+ Expect(err).ToNot(HaveOccurred())
165+ defer file.Close()
166+
167+ alphaConfigFileName = file.Name()
168+
169+ _, err = file.WriteString(in.alphaConfigContent)
170+ Expect(err).ToNot(HaveOccurred())
171+ })
172+ }
173+
174+ extraFlags := pflag.NewFlagSet("test-flagset", pflag.ExitOnError)
175+ if in.extraFlags != nil {
176+ extraFlags = in.extraFlags()
177+ }
178+
179+ opts, err := loadConfiguration(configFileName, alphaConfigFileName, extraFlags, in.args)
180+ if in.expectedErr != nil {
181+ Expect(err).To(MatchError(in.expectedErr.Error()))
182+ } else {
183+ Expect(err).ToNot(HaveOccurred())
184+ }
185+ Expect(in.expectedOptions).ToNot(BeNil())
186+ Expect(opts).To(Equal(in.expectedOptions()))
187+ },
188+ Entry("with legacy configuration", loadConfigurationTableInput{
189+ configContent: testCoreConfig + testLegacyConfig,
190+ expectedOptions: testExpectedOptions,
191+ }),
192+ Entry("with alpha configuration", loadConfigurationTableInput{
193+ configContent: testCoreConfig,
194+ alphaConfigContent: testAlphaConfig,
195+ expectedOptions: testExpectedOptions,
196+ }),
197+ Entry("with bad legacy configuration", loadConfigurationTableInput{
198+ configContent: testCoreConfig + "unknown_field=\"something\"",
199+ expectedOptions: func() *options.Options { return nil },
200+ expectedErr: errors.New("failed to load config: error unmarshalling config: 1 error(s) decoding:\n\n* '' has invalid keys: unknown_field"),
201+ }),
202+ Entry("with bad alpha configuration", loadConfigurationTableInput{
203+ configContent: testCoreConfig,
204+ alphaConfigContent: testAlphaConfig + ":",
205+ expectedOptions: func() *options.Options { return nil },
206+ expectedErr: errors.New("failed to load alpha options: error unmarshalling config: error converting YAML to JSON: yaml: line 34: did not find expected key"),
207+ }),
208+ Entry("with alpha configuration and bad core configuration", loadConfigurationTableInput{
209+ configContent: testCoreConfig + "unknown_field=\"something\"",
210+ alphaConfigContent: testAlphaConfig,
211+ expectedOptions: func() *options.Options { return nil },
212+ expectedErr: errors.New("failed to load core options: failed to load config: error unmarshalling config: 1 error(s) decoding:\n\n* '' has invalid keys: unknown_field"),
213+ }),
214+ )
215+})
pkg/apis/options/alpha_options.go+8
@@ -29,3 +29,11 @@ type AlphaOptions struct {
2929 // or from a static secret value.
3030 InjectResponseHeaders []Header `json:"injectResponseHeaders,omitempty"`
3131}
32+
33+// MergeInto replaces alpha options in the Options struct with the values
34+// from the AlphaOptions
35+func (a *AlphaOptions) MergeInto(opts *Options) {
36+ opts.UpstreamServers = a.Upstreams
37+ opts.InjectRequestHeaders = a.InjectRequestHeaders
38+ opts.InjectResponseHeaders = a.InjectResponseHeaders
39+}
pkg/apis/options/legacy_options.go+9
@@ -39,6 +39,15 @@ func NewLegacyOptions() *LegacyOptions {
3939 }
4040}
4141
42+func NewLegacyFlagSet() *pflag.FlagSet {
43+ flagSet := NewFlagSet()
44+
45+ flagSet.AddFlagSet(legacyUpstreamsFlagSet())
46+ flagSet.AddFlagSet(legacyHeadersFlagSet())
47+
48+ return flagSet
49+}
50+
4251func (l *LegacyOptions) ToOptions() (*Options, error) {
4352 upstreams, err := l.LegacyUpstreams.convert()
4453 if err != nil {
pkg/apis/options/load.go+28
@@ -1,10 +1,13 @@
11package options
22
33import (
4+ "errors"
45 "fmt"
6+ "io/ioutil"
57 "reflect"
68 "strings"
79
10+ "github.com/ghodss/yaml"
811 "github.com/mitchellh/mapstructure"
912 "github.com/spf13/pflag"
1013 "github.com/spf13/viper"
@@ -132,3 +135,28 @@ func isUnexported(name string) bool {
132135 first := string(name[0])
133136 return first == strings.ToLower(first)
134137}
138+
139+// LoadYAML will load a YAML based configuration file into the options interface provided.
140+func LoadYAML(configFileName string, into interface{}) error {
141+ v := viper.New()
142+ v.SetConfigFile(configFileName)
143+ v.SetConfigType("yaml")
144+ v.SetTypeByDefaultValue(true)
145+
146+ if configFileName == "" {
147+ return errors.New("no configuration file provided")
148+ }
149+
150+ data, err := ioutil.ReadFile(configFileName)
151+ if err != nil {
152+ return fmt.Errorf("unable to load config file: %w", err)
153+ }
154+
155+ // UnmarshalStrict will return an error if the config includes options that are
156+ // not mapped to felds of the into struct
157+ if err := yaml.UnmarshalStrict(data, into, yaml.DisallowUnknownFields); err != nil {
158+ return fmt.Errorf("error unmarshalling config: %w", err)
159+ }
160+
161+ return nil
162+}
pkg/apis/options/load_test.go+192−1
@@ -1,9 +1,11 @@
11package options
22
33import (
4+ "errors"
45 "fmt"
56 "io/ioutil"
67 "os"
8+ "time"
79
810 . "github.com/onsi/ginkgo"
911 . "github.com/onsi/ginkgo/extensions/table"
@@ -295,10 +297,199 @@ var _ = Describe("Load", func() {
295297 expectedOutput: NewOptions(),
296298 }),
297299 Entry("with an empty LegacyOptions struct, should return default values", &testOptionsTableInput{
298− flagSet: NewFlagSet,
300+ flagSet: NewLegacyFlagSet,
299301 input: &LegacyOptions{},
300302 expectedOutput: NewLegacyOptions(),
301303 }),
302304 )
303305 })
304306})
307+
308+var _ = Describe("LoadYAML", func() {
309+ Context("with a testOptions structure", func() {
310+ type TestOptionSubStruct struct {
311+ StringSliceOption []string `yaml:"stringSliceOption,omitempty"`
312+ }
313+
314+ type TestOptions struct {
315+ StringOption string `yaml:"stringOption,omitempty"`
316+ Sub TestOptionSubStruct `yaml:"sub,omitempty"`
317+
318+ // Check that embedded fields can be unmarshalled
319+ TestOptionSubStruct `yaml:",inline,squash"`
320+ }
321+
322+ var testOptionsConfigBytesFull = []byte(`
323+stringOption: foo
324+stringSliceOption:
325+- a
326+- b
327+- c
328+sub:
329+ stringSliceOption:
330+ - d
331+ - e
332+`)
333+
334+ type loadYAMLTableInput struct {
335+ configFile []byte
336+ input interface{}
337+ expectedErr error
338+ expectedOutput interface{}
339+ }
340+
341+ DescribeTable("LoadYAML",
342+ func(in loadYAMLTableInput) {
343+ var configFileName string
344+
345+ if in.configFile != nil {
346+ By("Creating a config file")
347+ configFile, err := ioutil.TempFile("", "oauth2-proxy-test-config-file")
348+ Expect(err).ToNot(HaveOccurred())
349+ defer configFile.Close()
350+
351+ _, err = configFile.Write(in.configFile)
352+ Expect(err).ToNot(HaveOccurred())
353+ defer os.Remove(configFile.Name())
354+
355+ configFileName = configFile.Name()
356+ }
357+
358+ var input interface{}
359+ if in.input != nil {
360+ input = in.input
361+ } else {
362+ input = &TestOptions{}
363+ }
364+ err := LoadYAML(configFileName, input)
365+ if in.expectedErr != nil {
366+ Expect(err).To(MatchError(in.expectedErr.Error()))
367+ } else {
368+ Expect(err).ToNot(HaveOccurred())
369+ }
370+ Expect(input).To(Equal(in.expectedOutput))
371+ },
372+ Entry("with a valid input", loadYAMLTableInput{
373+ configFile: testOptionsConfigBytesFull,
374+ input: &TestOptions{},
375+ expectedOutput: &TestOptions{
376+ StringOption: "foo",
377+ Sub: TestOptionSubStruct{
378+ StringSliceOption: []string{"d", "e"},
379+ },
380+ TestOptionSubStruct: TestOptionSubStruct{
381+ StringSliceOption: []string{"a", "b", "c"},
382+ },
383+ },
384+ }),
385+ Entry("with no config file", loadYAMLTableInput{
386+ configFile: nil,
387+ input: &TestOptions{},
388+ expectedOutput: &TestOptions{},
389+ expectedErr: errors.New("no configuration file provided"),
390+ }),
391+ Entry("with invalid YAML", loadYAMLTableInput{
392+ configFile: []byte("\tfoo: bar"),
393+ input: &TestOptions{},
394+ expectedOutput: &TestOptions{},
395+ expectedErr: errors.New("error unmarshalling config: error converting YAML to JSON: yaml: found character that cannot start any token"),
396+ }),
397+ Entry("with extra fields in the YAML", loadYAMLTableInput{
398+ configFile: append(testOptionsConfigBytesFull, []byte("foo: bar\n")...),
399+ input: &TestOptions{},
400+ expectedOutput: &TestOptions{
401+ StringOption: "foo",
402+ Sub: TestOptionSubStruct{
403+ StringSliceOption: []string{"d", "e"},
404+ },
405+ TestOptionSubStruct: TestOptionSubStruct{
406+ StringSliceOption: []string{"a", "b", "c"},
407+ },
408+ },
409+ expectedErr: errors.New("error unmarshalling config: error unmarshaling JSON: while decoding JSON: json: unknown field \"foo\""),
410+ }),
411+ Entry("with an incorrect type for a string field", loadYAMLTableInput{
412+ configFile: []byte(`stringOption: ["a", "b"]`),
413+ input: &TestOptions{},
414+ expectedOutput: &TestOptions{},
415+ expectedErr: errors.New("error unmarshalling config: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal array into Go struct field TestOptions.StringOption of type string"),
416+ }),
417+ Entry("with an incorrect type for an array field", loadYAMLTableInput{
418+ configFile: []byte(`stringSliceOption: "a"`),
419+ input: &TestOptions{},
420+ expectedOutput: &TestOptions{},
421+ expectedErr: errors.New("error unmarshalling config: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go struct field TestOptions.StringSliceOption of type []string"),
422+ }),
423+ )
424+ })
425+
426+ It("should load a full example AlphaOptions", func() {
427+ config := []byte(`
428+upstreams:
429+- id: httpbin
430+ path: /
431+ uri: http://httpbin
432+ flushInterval: 500ms
433+injectRequestHeaders:
434+- name: X-Forwarded-User
435+ values:
436+ - claim: user
437+injectResponseHeaders:
438+- name: X-Secret
439+ values:
440+ - value: c2VjcmV0
441+`)
442+
443+ By("Creating a config file")
444+ configFile, err := ioutil.TempFile("", "oauth2-proxy-test-alpha-config-file")
445+ Expect(err).ToNot(HaveOccurred())
446+ defer configFile.Close()
447+
448+ _, err = configFile.Write(config)
449+ Expect(err).ToNot(HaveOccurred())
450+ defer os.Remove(configFile.Name())
451+
452+ configFileName := configFile.Name()
453+
454+ By("Loading the example config")
455+ into := &AlphaOptions{}
456+ Expect(LoadYAML(configFileName, into)).To(Succeed())
457+
458+ flushInterval := Duration(500 * time.Millisecond)
459+
460+ Expect(into).To(Equal(&AlphaOptions{
461+ Upstreams: []Upstream{
462+ {
463+ ID: "httpbin",
464+ Path: "/",
465+ URI: "http://httpbin",
466+ FlushInterval: &flushInterval,
467+ },
468+ },
469+ InjectRequestHeaders: []Header{
470+ {
471+ Name: "X-Forwarded-User",
472+ Values: []HeaderValue{
473+ {
474+ ClaimSource: &ClaimSource{
475+ Claim: "user",
476+ },
477+ },
478+ },
479+ },
480+ },
481+ InjectResponseHeaders: []Header{
482+ {
483+ Name: "X-Secret",
484+ Values: []HeaderValue{
485+ {
486+ SecretSource: &SecretSource{
487+ Value: []byte("secret"),
488+ },
489+ },
490+ },
491+ },
492+ },
493+ }))
494+ })
495+})
pkg/apis/options/options.go−2
@@ -246,8 +246,6 @@ func NewFlagSet() *pflag.FlagSet {
246246
247247 flagSet.AddFlagSet(cookieFlagSet())
248248 flagSet.AddFlagSet(loggingFlagSet())
249− flagSet.AddFlagSet(legacyUpstreamsFlagSet())
250− flagSet.AddFlagSet(legacyHeadersFlagSet())
251249
252250 return flagSet
253251}