From b8740b62daca01b4a7f7c1d136752b90e81fd280 Mon Sep 17 00:00:00 2001 From: Ryan Uber Date: Mon, 5 Jan 2015 12:30:03 -0800 Subject: [PATCH 1/2] agent: reject config with invalid options --- command/agent/config.go | 12 ++++++++++-- command/agent/config_test.go | 9 +++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/command/agent/config.go b/command/agent/config.go index 707cd43ade..cecaf7c4a1 100644 --- a/command/agent/config.go +++ b/command/agent/config.go @@ -335,6 +335,13 @@ type Config struct { // WatchPlans contains the compiled watches WatchPlans []*watch.WatchPlan `mapstructure:"-" json:"-"` + + // Allow the following fields to be present in configuration files without + // mapstructure erroring on them. + _ interface{} `mapstructure:"services"` + _ interface{} `mapstructure:"checks"` + _ interface{} `mapstructure:"service"` + _ interface{} `mapstructure:"check"` } type dirEnts []os.FileInfo @@ -463,8 +470,9 @@ func DecodeConfig(r io.Reader) (*Config, error) { // Decode var md mapstructure.Metadata msdec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ - Metadata: &md, - Result: &result, + Metadata: &md, + Result: &result, + ErrorUnused: true, }) if err != nil { return nil, err diff --git a/command/agent/config_test.go b/command/agent/config_test.go index 13e8634ce4..f0bbd9e61c 100644 --- a/command/agent/config_test.go +++ b/command/agent/config_test.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" "reflect" + "strings" "testing" "time" ) @@ -591,6 +592,14 @@ func TestDecodeConfig(t *testing.T) { } } +func TestDecodeConfig_invalidKeys(t *testing.T) { + input := `{"bad": "no way jose"}` + _, err := DecodeConfig(bytes.NewReader([]byte(input))) + if err == nil || !strings.Contains(err.Error(), "invalid keys") { + t.Fatalf("should have rejected invalid config keys") + } +} + func TestDecodeConfig_Services(t *testing.T) { input := `{ "services": [ From 42ace3a6b5fae1f0af5a6e7bbb63d4a432c6f229 Mon Sep 17 00:00:00 2001 From: Ryan Uber Date: Mon, 5 Jan 2015 14:41:19 -0800 Subject: [PATCH 2/2] agent: use mapstructure's Metadata.Unused to detect extraneous config --- command/agent/config.go | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/command/agent/config.go b/command/agent/config.go index cecaf7c4a1..e984678664 100644 --- a/command/agent/config.go +++ b/command/agent/config.go @@ -335,13 +335,6 @@ type Config struct { // WatchPlans contains the compiled watches WatchPlans []*watch.WatchPlan `mapstructure:"-" json:"-"` - - // Allow the following fields to be present in configuration files without - // mapstructure erroring on them. - _ interface{} `mapstructure:"services"` - _ interface{} `mapstructure:"checks"` - _ interface{} `mapstructure:"service"` - _ interface{} `mapstructure:"check"` } type dirEnts []os.FileInfo @@ -470,9 +463,8 @@ func DecodeConfig(r io.Reader) (*Config, error) { // Decode var md mapstructure.Metadata msdec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ - Metadata: &md, - Result: &result, - ErrorUnused: true, + Metadata: &md, + Result: &result, }) if err != nil { return nil, err @@ -482,6 +474,20 @@ func DecodeConfig(r io.Reader) (*Config, error) { return nil, err } + // Check unused fields and verify that no bad configuration options were + // passed to Consul. There are a few additional fields which don't directly + // use mapstructure decoding, so we need to account for those as well. + allowedKeys := []string{"service", "services", "check", "checks"} + var unused []string + for _, field := range md.Unused { + if !strContains(allowedKeys, field) { + unused = append(unused, field) + } + } + if len(unused) > 0 { + return nil, fmt.Errorf("Config has invalid keys: %s", strings.Join(unused, ",")) + } + // Handle time conversions if raw := result.DNSConfig.NodeTTLRaw; raw != "" { dur, err := time.ParseDuration(raw)