Commit 0e9c0eda authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

Error if unknown root level key in template [GH-180]

parent 3b4ef72e
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
"sort"
) )
// The rawTemplate struct represents the structure of a template read // The rawTemplate struct represents the structure of a template read
...@@ -15,7 +16,7 @@ type rawTemplate struct { ...@@ -15,7 +16,7 @@ type rawTemplate struct {
Builders []map[string]interface{} Builders []map[string]interface{}
Hooks map[string][]string Hooks map[string][]string
Provisioners []map[string]interface{} Provisioners []map[string]interface{}
PostProcessors []interface{} `json:"post-processors"` PostProcessors []interface{} `mapstructure:"post-processors"`
} }
// The Template struct represents a parsed template, parsed into the most // The Template struct represents a parsed template, parsed into the most
...@@ -63,8 +64,8 @@ type rawProvisionerConfig struct { ...@@ -63,8 +64,8 @@ type rawProvisionerConfig struct {
// and checking for this can be useful, if you wish to format it in a certain // and checking for this can be useful, if you wish to format it in a certain
// way. // way.
func ParseTemplate(data []byte) (t *Template, err error) { func ParseTemplate(data []byte) (t *Template, err error) {
var rawTpl rawTemplate var rawTplInterface interface{}
err = json.Unmarshal(data, &rawTpl) err = json.Unmarshal(data, &rawTplInterface)
if err != nil { if err != nil {
syntaxErr, ok := err.(*json.SyntaxError) syntaxErr, ok := err.(*json.SyntaxError)
if !ok { if !ok {
...@@ -92,14 +93,41 @@ func ParseTemplate(data []byte) (t *Template, err error) { ...@@ -92,14 +93,41 @@ func ParseTemplate(data []byte) (t *Template, err error) {
return return
} }
// Decode the raw template interface into the actual rawTemplate
// structure, checking for any extranneous keys along the way.
var md mapstructure.Metadata
var rawTpl rawTemplate
decoderConfig := &mapstructure.DecoderConfig{
Metadata: &md,
Result: &rawTpl,
}
decoder, err := mapstructure.NewDecoder(decoderConfig)
if err != nil {
return
}
err = decoder.Decode(rawTplInterface)
if err != nil {
return
}
errors := make([]error, 0)
if len(md.Unused) > 0 {
sort.Strings(md.Unused)
for _, unused := range md.Unused {
errors = append(
errors, fmt.Errorf("Unknown root level key in template: '%s'", unused))
}
}
t = &Template{} t = &Template{}
t.Builders = make(map[string]rawBuilderConfig) t.Builders = make(map[string]rawBuilderConfig)
t.Hooks = rawTpl.Hooks t.Hooks = rawTpl.Hooks
t.PostProcessors = make([][]rawPostProcessorConfig, len(rawTpl.PostProcessors)) t.PostProcessors = make([][]rawPostProcessorConfig, len(rawTpl.PostProcessors))
t.Provisioners = make([]rawProvisionerConfig, len(rawTpl.Provisioners)) t.Provisioners = make([]rawProvisionerConfig, len(rawTpl.Provisioners))
errors := make([]error, 0)
// Gather all the builders // Gather all the builders
for i, v := range rawTpl.Builders { for i, v := range rawTpl.Builders {
var raw rawBuilderConfig var raw rawBuilderConfig
...@@ -203,6 +231,7 @@ func ParseTemplate(data []byte) (t *Template, err error) { ...@@ -203,6 +231,7 @@ func ParseTemplate(data []byte) (t *Template, err error) {
// If there were errors, we put it into a MultiError and return // If there were errors, we put it into a MultiError and return
if len(errors) > 0 { if len(errors) > 0 {
err = &MultiError{errors} err = &MultiError{errors}
t = nil
return return
} }
......
...@@ -37,6 +37,23 @@ func TestParseTemplate_Invalid(t *testing.T) { ...@@ -37,6 +37,23 @@ func TestParseTemplate_Invalid(t *testing.T) {
assert.Nil(result, "should have no result") assert.Nil(result, "should have no result")
} }
func TestParseTemplate_InvalidKeys(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
// Note there is an extra comma below for a purposeful
// syntax error in the JSON.
data := `
{
"builders": [{"type": "foo"}],
"what is this": ""
}
`
result, err := ParseTemplate([]byte(data))
assert.NotNil(err, "should have an error")
assert.Nil(result, "should have no result")
}
func TestParseTemplate_BuilderWithoutType(t *testing.T) { func TestParseTemplate_BuilderWithoutType(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true) assert := asserts.NewTestingAsserts(t, true)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment