@@ -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+})