diff --git a/packer/template.go b/packer/template.go index 0afa7341c..1a48b40e5 100644 --- a/packer/template.go +++ b/packer/template.go @@ -3,6 +3,7 @@ package packer import ( "encoding/json" "fmt" + "github.com/mitchellh/mapstructure" ) // The rawTemplate struct represents the structure of a template read @@ -31,15 +32,19 @@ type Template struct { // raw configuration. If requested, this is used to compile into a full // builder configuration at some point. type rawBuilderConfig struct { - builderType string - rawConfig interface{} + Name string + Type string + + rawConfig interface{} } // rawProvisionerConfig represents a raw, unprocessed provisioner configuration. // It contains the type of the provisioner as well as the raw configuration // that is handed to the provisioner for it to process. type rawProvisionerConfig struct { - pType string + Type string + Override map[string]interface{} + rawConfig interface{} } @@ -65,8 +70,20 @@ func ParseTemplate(data []byte) (t *Template, err error) { // Gather all the builders for i, v := range rawTpl.Builders { - rawType, ok := v["type"] - if !ok { + var raw rawBuilderConfig + if err := mapstructure.Decode(v, &raw); err != nil { + if merr, ok := err.(*mapstructure.Error); ok { + for _, err := range merr.Errors { + errors = append(errors, fmt.Errorf("builder %d: %s", i+1, err)) + } + } else { + errors = append(errors, fmt.Errorf("builder %d: %s", i+1, err)) + } + + continue + } + + if raw.Type == "" { errors = append(errors, fmt.Errorf("builder %d: missing 'type'", i+1)) continue } @@ -74,54 +91,42 @@ func ParseTemplate(data []byte) (t *Template, err error) { // Attempt to get the name of the builder. If the "name" key // missing, use the "type" field, which is guaranteed to exist // at this point. - rawName, ok := v["name"] - if !ok { - rawName = v["type"] - } - - // Attempt to convert the name/type to strings, but error if we can't - name, ok := rawName.(string) - if !ok { - errors = append(errors, fmt.Errorf("builder %d: name must be a string", i+1)) - continue - } - - typeName, ok := rawType.(string) - if !ok { - errors = append(errors, fmt.Errorf("builder %d: type must be a string", i+1)) - continue + if raw.Name == "" { + raw.Name = raw.Type } // Check if we already have a builder with this name and error if so - if _, ok := t.Builders[name]; ok { - errors = append(errors, fmt.Errorf("builder with name '%s' already exists", name)) + if _, ok := t.Builders[raw.Name]; ok { + errors = append(errors, fmt.Errorf("builder with name '%s' already exists", raw.Name)) continue } - t.Builders[name] = rawBuilderConfig{ - typeName, - v, - } + raw.rawConfig = v + + t.Builders[raw.Name] = raw } // Gather all the provisioners for i, v := range rawTpl.Provisioners { - rawType, ok := v["type"] - if !ok { - errors = append(errors, fmt.Errorf("provisioner %d: missing 'type'", i+1)) + raw := &t.Provisioners[i] + if err := mapstructure.Decode(v, raw); err != nil { + if merr, ok := err.(*mapstructure.Error); ok { + for _, err := range merr.Errors { + errors = append(errors, fmt.Errorf("provisioner %d: %s", i+1, err)) + } + } else { + errors = append(errors, fmt.Errorf("provisioner %d: %s", i+1, err)) + } + continue } - typeName, ok := rawType.(string) - if !ok { - errors = append(errors, fmt.Errorf("provisioner %d: type must be a string", i+1)) + if raw.Type == "" { + errors = append(errors, fmt.Errorf("provisioner %d: missing 'type'", i+1)) continue } - t.Provisioners[i] = rawProvisionerConfig{ - typeName, - v, - } + raw.rawConfig = v } // If there were errors, we put it into a MultiError and return @@ -168,13 +173,13 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err panic("no provisioner function") } - builder, err := components.Builder(builderConfig.builderType) + builder, err := components.Builder(builderConfig.Type) if err != nil { return } if builder == nil { - err = fmt.Errorf("Builder type not found: %s", builderConfig.builderType) + err = fmt.Errorf("Builder type not found: %s", builderConfig.Type) return } @@ -205,13 +210,13 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err provisioners := make([]coreBuildProvisioner, 0, len(t.Provisioners)) for _, rawProvisioner := range t.Provisioners { var provisioner Provisioner - provisioner, err = components.Provisioner(rawProvisioner.pType) + provisioner, err = components.Provisioner(rawProvisioner.Type) if err != nil { return } if provisioner == nil { - err = fmt.Errorf("Provisioner type not found: %s", rawProvisioner.pType) + err = fmt.Errorf("Provisioner type not found: %s", rawProvisioner.Type) return } diff --git a/packer/template_test.go b/packer/template_test.go index c6889878b..91265e68f 100644 --- a/packer/template_test.go +++ b/packer/template_test.go @@ -91,7 +91,7 @@ func TestParseTemplate_BuilderWithoutName(t *testing.T) { builder, ok := result.Builders["amazon-ebs"] assert.True(ok, "should have amazon-ebs builder") - assert.Equal(builder.builderType, "amazon-ebs", "builder should be amazon-ebs") + assert.Equal(builder.Type, "amazon-ebs", "builder should be amazon-ebs") } func TestParseTemplate_BuilderWithName(t *testing.T) { @@ -116,7 +116,7 @@ func TestParseTemplate_BuilderWithName(t *testing.T) { builder, ok := result.Builders["bob"] assert.True(ok, "should have bob builder") - assert.Equal(builder.builderType, "amazon-ebs", "builder should be amazon-ebs") + assert.Equal(builder.Type, "amazon-ebs", "builder should be amazon-ebs") } func TestParseTemplate_BuilderWithConflictingName(t *testing.T) { @@ -213,7 +213,8 @@ func TestParseTemplate_Provisioners(t *testing.T) { assert.Nil(err, "should not error") assert.NotNil(result, "template should not be nil") assert.Length(result.Provisioners, 1, "should have one provisioner") - assert.Equal(result.Provisioners[0].pType, "shell", "provisioner should be shell") + assert.Equal(result.Provisioners[0].Type, "shell", "provisioner should be shell") + assert.NotNil(result.Provisioners[0].rawConfig, "should have raw config") } func TestTemplate_BuildNames(t *testing.T) {