Skip to content

Commit

Permalink
Return a warning when encountering unknown config file fields
Browse files Browse the repository at this point in the history
This will cause veneur to report an error on startup if it encounters
any unknown/deprecated fields.
  • Loading branch information
asf-stripe committed Jan 12, 2018
1 parent c2e7d1d commit 094ac5f
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 13 deletions.
63 changes: 50 additions & 13 deletions config_parse.go
Expand Up @@ -20,25 +20,44 @@ func ReadProxyConfig(path string) (c ProxyConfig, err error) {
return c, err
}
defer f.Close()
return readProxyConfig(f)
}

bts, err := ioutil.ReadAll(f)
func readProxyConfig(r io.Reader) (ProxyConfig, error) {
var c ProxyConfig
bts, err := ioutil.ReadAll(r)
if err != nil {
return
return c, err
}
err = yaml.Unmarshal(bts, &c)
if err != nil {
return
unmarshalErr := unmarshalSemiStrictly(bts, &c)
if unmarshalErr != nil {
if _, ok := err.(*UnknownConfigKeys); !ok {
return c, unmarshalErr
}
}

err = envconfig.Process("veneur", &c)
if err != nil {
return c, err
}

return c, nil
return c, unmarshalErr
}

// UnknownConfigKeys represents a failure to strictly parse a
// configuration YAML file has failed, indicating that the file
// contains unknown keys.
type UnknownConfigKeys struct {
err error
}

func (e *UnknownConfigKeys) Error() string {
return e.err.Error()
}

// ReadConfig unmarshals the config file and slurps in it's data.
// ReadConfig unmarshals the config file and slurps in its
// data. ReadConfig can return an error of type *UnknownConfigKeys,
// which means that the file is usable, but contains unknown fields.
func ReadConfig(path string) (c Config, err error) {
f, err := os.Open(path)
if err != nil {
Expand All @@ -48,18 +67,34 @@ func ReadConfig(path string) (c Config, err error) {
return readConfig(f)
}

func readConfig(r io.Reader) (c Config, err error) {
func unmarshalSemiStrictly(bts []byte, into interface{}) error {
strictErr := yaml.UnmarshalStrict(bts, into)
if strictErr == nil {
return nil
}

looseErr := yaml.Unmarshal(bts, into)
if looseErr != nil {
return looseErr
}
return &UnknownConfigKeys{strictErr}
}

func readConfig(r io.Reader) (Config, error) {
var c Config
// Unfortunately the YAML package does not
// support reader inputs
// TODO(aditya) convert this when the
// upstream PR lands
bts, err := ioutil.ReadAll(r)
if err != nil {
return
return c, err
}
err = yaml.Unmarshal(bts, &c)
if err != nil {
return
unmarshalErr := unmarshalSemiStrictly(bts, &c)
if unmarshalErr != nil {
if _, ok := err.(*UnknownConfigKeys); !ok {
return c, unmarshalErr
}
}

err = envconfig.Process("veneur", &c)
Expand All @@ -74,7 +109,9 @@ func readConfig(r io.Reader) (c Config, err error) {
if c.ReadBufferSizeBytes == 0 {
c.ReadBufferSizeBytes = defaultBufferSizeBytes
}
return c, nil

// pass back an error about any unknown fields:
return c, unmarshalErr
}

// ParseInterval handles parsing the flush interval as a time.Duration
Expand Down
28 changes: 28 additions & 0 deletions config_test.go
Expand Up @@ -39,6 +39,34 @@ func TestReadBadConfig(t *testing.T) {
assert.Equal(t, c, Config{}, "Parsing invalid config file should return zero struct")
}

func TestReadUnknownKeysConfig(t *testing.T) {
const config = `---
no_such_key: 1
hostname: foobar
`
r := strings.NewReader(config)
c, err := readConfig(r)
assert.Error(t, err)
_, ok := err.(*UnknownConfigKeys)
t.Log(err)
assert.True(t, ok, "Returned error should indicate a strictness error")
assert.Equal(t, "foobar", c.Hostname)
}

func TestReadUnknownKeysProxyConfig(t *testing.T) {
const config = `---
no_such_key: 1
debug: true
`
r := strings.NewReader(config)
c, err := readProxyConfig(r)
assert.Error(t, err)
_, ok := err.(*UnknownConfigKeys)
t.Log(err)
assert.True(t, ok, "Returned error should indicate a strictness error")
assert.Equal(t, true, c.Debug)
}

func TestHostname(t *testing.T) {
const hostnameConfig = "hostname: foo"
r := strings.NewReader(hostnameConfig)
Expand Down

0 comments on commit 094ac5f

Please sign in to comment.