diff --git a/packer/template.go b/packer/template.go index 2a1c34f99..8e5ee8188 100644 --- a/packer/template.go +++ b/packer/template.go @@ -20,9 +20,10 @@ type rawTemplate struct { // The Template struct represents a parsed template, parsed into the most // completed form it can be without additional processing by the caller. type Template struct { - Name string - Builders map[string]rawBuilderConfig - Hooks map[string][]string + Name string + Builders map[string]rawBuilderConfig + Hooks map[string][]string + Provisioners []rawProvisionerConfig } // The rawBuilderConfig struct represents a raw, unprocessed builder @@ -34,6 +35,14 @@ type rawBuilderConfig struct { 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 + rawConfig interface{} +} + // ParseTemplate takes a byte slice and parses a Template from it, returning // the template and possibly errors while loading the template. The error // could potentially be a MultiError, representing multiple errors. Knowing @@ -50,6 +59,7 @@ func ParseTemplate(data []byte) (t *Template, err error) { t.Name = rawTpl.Name t.Builders = make(map[string]rawBuilderConfig) t.Hooks = rawTpl.Hooks + t.Provisioners = make([]rawProvisionerConfig, len(rawTpl.Provisioners)) errors := make([]error, 0) @@ -94,6 +104,26 @@ func ParseTemplate(data []byte) (t *Template, err error) { } } + // 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 + } + + typeName, ok := rawType.(string) + if !ok { + errors = append(errors, fmt.Errorf("provisioner %d: type must be a string", i+1)) + continue + } + + t.Provisioners[i] = rawProvisionerConfig{ + typeName, + v, + } + } + // If there were errors, we put it into a MultiError and return if len(errors) > 0 { err = &MultiError{errors} diff --git a/packer/template_test.go b/packer/template_test.go index 00754b0e3..de9c89381 100644 --- a/packer/template_test.go +++ b/packer/template_test.go @@ -165,6 +165,57 @@ func TestParseTemplate_Hooks(t *testing.T) { assert.Equal(hooks, []string{"foo", "bar"}, "hooks should be correct") } +func TestParseTemplate_ProvisionerWithoutType(t *testing.T) { + assert := asserts.NewTestingAsserts(t, true) + + data := ` + { + "name": "my-image", + "provisioners": [{}] + } + ` + + _, err := ParseTemplate([]byte(data)) + assert.NotNil(err, "should have error") +} + +func TestParseTemplate_ProvisionerWithNonStringType(t *testing.T) { + assert := asserts.NewTestingAsserts(t, true) + + data := ` + { + "name": "my-image", + "provisioners": [{ + "type": 42 + }] + } + ` + + _, err := ParseTemplate([]byte(data)) + assert.NotNil(err, "should have error") +} + +func TestParseTemplate_Provisioners(t *testing.T) { + assert := asserts.NewTestingAsserts(t, true) + + data := ` + { + "name": "my-image", + "provisioners": [ + { + "type": "shell" + } + ] + } + ` + + result, err := ParseTemplate([]byte(data)) + 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") +} + func TestTemplate_BuildNames(t *testing.T) { assert := asserts.NewTestingAsserts(t, true)