Commit 5638cecd authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

packer: Use mapstructure for templates, save lots of lines

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