Commit 7a07802e authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

packer: Parse post-processors in templates

This includes parsing for the simple, detailed, and sequential
processors.
parent 886ac536
......@@ -14,15 +14,16 @@ type rawTemplate struct {
Builders []map[string]interface{}
Hooks map[string][]string
Provisioners []map[string]interface{}
PostProcessors []map[string]interface{} `json:"post-processors"`
PostProcessors []interface{} `json:"post-processors"`
}
// 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 {
Builders map[string]rawBuilderConfig
Hooks map[string][]string
Provisioners []rawProvisionerConfig
Builders map[string]rawBuilderConfig
Hooks map[string][]string
PostProcessors [][]rawPostProcessorConfig
Provisioners []rawProvisionerConfig
}
// The rawBuilderConfig struct represents a raw, unprocessed builder
......@@ -36,6 +37,14 @@ type rawBuilderConfig struct {
rawConfig interface{}
}
// rawPostProcessorConfig represents a raw, unprocessed post-processor
// configuration. It contains the type of the post processor as well as the
// raw configuration that is handed to the post-processor for it to process.
type rawPostProcessorConfig struct {
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.
......@@ -61,6 +70,7 @@ func ParseTemplate(data []byte) (t *Template, err error) {
t = &Template{}
t.Builders = make(map[string]rawBuilderConfig)
t.Hooks = rawTpl.Hooks
t.PostProcessors = make([][]rawPostProcessorConfig, len(rawTpl.PostProcessors))
t.Provisioners = make([]rawProvisionerConfig, len(rawTpl.Provisioners))
errors := make([]error, 0)
......@@ -103,6 +113,43 @@ func ParseTemplate(data []byte) (t *Template, err error) {
t.Builders[raw.Name] = raw
}
// Gather all the post-processors. This is a complicated process since there
// are actually three different formats that the user can use to define
// a post-processor.
for i, rawV := range rawTpl.PostProcessors {
rawPP, err := parsePostProvisioner(i, rawV)
if err != nil {
errors = append(errors, err...)
continue
}
t.PostProcessors[i] = make([]rawPostProcessorConfig, len(rawPP))
configs := t.PostProcessors[i]
for j, pp := range rawPP {
var config rawPostProcessorConfig
if err := mapstructure.Decode(pp, &config); err != nil {
if merr, ok := err.(*mapstructure.Error); ok {
for _, err := range merr.Errors {
errors = append(errors, fmt.Errorf("Post-processor #%d.%d: %s", i+1, j+1, err))
}
} else {
errors = append(errors, fmt.Errorf("Post-processor %d.%d: %s", i+1, j+1, err))
}
continue
}
if config.Type == "" {
errors = append(errors, fmt.Errorf("Post-processor %d.%d: missing 'type'", i+1, j+1))
continue
}
config.rawConfig = pp
configs[j] = config
}
}
// Gather all the provisioners
for i, v := range rawTpl.Provisioners {
raw := &t.Provisioners[i]
......@@ -135,6 +182,43 @@ func ParseTemplate(data []byte) (t *Template, err error) {
return
}
func parsePostProvisioner(i int, rawV interface{}) (result []map[string]interface{}, errors []error) {
switch v := rawV.(type) {
case string:
result = []map[string]interface{}{
{"type": v},
}
case map[string]interface{}:
result = []map[string]interface{}{v}
case []interface{}:
result = make([]map[string]interface{}, len(v))
errors = make([]error, 0)
for j, innerRawV := range v {
switch innerV := innerRawV.(type) {
case string:
result[j] = map[string]interface{}{"type": innerV}
case map[string]interface{}:
result[j] = innerV
case []interface{}:
errors = append(
errors,
fmt.Errorf("Post-processor %d.%d: sequences not allowed to be nested in sequences", i+1, j+1))
default:
errors = append(errors, fmt.Errorf("Post-processor %d.%d is in a bad format.", i+1, j+1))
}
}
if len(errors) == 0 {
errors = nil
}
default:
result = nil
errors = []error{fmt.Errorf("Post-processor %d is in a bad format.", i+1)}
}
return
}
// BuildNames returns a slice of the available names of builds that
// this template represents.
func (t *Template) BuildNames() []string {
......
......@@ -28,8 +28,7 @@ func TestParseTemplate_Invalid(t *testing.T) {
// syntax error in the JSON.
data := `
{
"name": "my-image",,
"builders": []
"builders": [],
}
`
......@@ -43,7 +42,6 @@ func TestParseTemplate_BuilderWithoutType(t *testing.T) {
data := `
{
"name": "my-image",
"builders": [{}]
}
`
......@@ -57,7 +55,6 @@ func TestParseTemplate_BuilderWithNonStringType(t *testing.T) {
data := `
{
"name": "my-image",
"builders": [{
"type": 42
}]
......@@ -73,7 +70,6 @@ func TestParseTemplate_BuilderWithoutName(t *testing.T) {
data := `
{
"name": "my-image",
"builders": [
{
"type": "amazon-ebs"
......@@ -97,7 +93,6 @@ func TestParseTemplate_BuilderWithName(t *testing.T) {
data := `
{
"name": "my-image",
"builders": [
{
"name": "bob",
......@@ -122,7 +117,6 @@ func TestParseTemplate_BuilderWithConflictingName(t *testing.T) {
data := `
{
"name": "my-image",
"builders": [
{
"name": "bob",
......@@ -145,7 +139,6 @@ func TestParseTemplate_Hooks(t *testing.T) {
data := `
{
"name": "my-image",
"hooks": {
"event": ["foo", "bar"]
......@@ -163,12 +156,65 @@ func TestParseTemplate_Hooks(t *testing.T) {
assert.Equal(hooks, []string{"foo", "bar"}, "hooks should be correct")
}
func TestParseTemplate_PostProcessors(t *testing.T) {
data := `
{
"post-processors": [
"simple",
{ "type": "detailed" },
[ "foo", { "type": "bar" } ]
]
}
`
tpl, err := ParseTemplate([]byte(data))
if err != nil {
t.Fatalf("error parsing: %s", err)
}
if len(tpl.PostProcessors) != 3 {
t.Fatalf("bad number of post-processors: %d", len(tpl.PostProcessors))
}
pp := tpl.PostProcessors[0]
if len(pp) != 1 {
t.Fatalf("wrong number of configs in simple: %d", len(pp))
}
if pp[0].Type != "simple" {
t.Fatalf("wrong type for simple: %s", pp[0].Type)
}
pp = tpl.PostProcessors[1]
if len(pp) != 1 {
t.Fatalf("wrong number of configs in detailed: %d", len(pp))
}
if pp[0].Type != "detailed" {
t.Fatalf("wrong type for detailed: %s", pp[0].Type)
}
pp = tpl.PostProcessors[2]
if len(pp) != 2 {
t.Fatalf("wrong number of configs for sequence: %d", len(pp))
}
if pp[0].Type != "foo" {
t.Fatalf("wrong type for sequence 0: %s", pp[0].Type)
}
if pp[1].Type != "bar" {
t.Fatalf("wrong type for sequence 1: %s", pp[1].Type)
}
}
func TestParseTemplate_ProvisionerWithoutType(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
data := `
{
"name": "my-image",
"provisioners": [{}]
}
`
......@@ -182,7 +228,6 @@ func TestParseTemplate_ProvisionerWithNonStringType(t *testing.T) {
data := `
{
"name": "my-image",
"provisioners": [{
"type": 42
}]
......@@ -198,7 +243,6 @@ func TestParseTemplate_Provisioners(t *testing.T) {
data := `
{
"name": "my-image",
"provisioners": [
{
"type": "shell"
......@@ -220,7 +264,6 @@ func TestTemplate_BuildNames(t *testing.T) {
data := `
{
"name": "my-image",
"builders": [
{
"name": "bob",
......@@ -247,7 +290,6 @@ func TestTemplate_BuildUnknown(t *testing.T) {
data := `
{
"name": "my-image",
"builders": [
{
"name": "test1",
......@@ -270,7 +312,6 @@ func TestTemplate_BuildUnknownBuilder(t *testing.T) {
data := `
{
"name": "my-image",
"builders": [
{
"name": "test1",
......@@ -295,7 +336,6 @@ func TestTemplate_Build_NilBuilderFunc(t *testing.T) {
data := `
{
"name": "my-image",
"builders": [
{
"name": "test1",
......@@ -331,7 +371,6 @@ func TestTemplate_Build_NilProvisionerFunc(t *testing.T) {
data := `
{
"name": "my-image",
"builders": [
{
"name": "test1",
......@@ -369,7 +408,6 @@ func TestTemplate_Build_NilProvisionerFunc_WithNoProvisioners(t *testing.T) {
data := `
{
"name": "my-image",
"builders": [
{
"name": "test1",
......@@ -394,7 +432,6 @@ func TestTemplate_Build(t *testing.T) {
data := `
{
"name": "my-image",
"builders": [
{
"name": "test1",
......@@ -452,7 +489,6 @@ func TestTemplate_Build_ProvisionerOverride(t *testing.T) {
data := `
{
"name": "my-image",
"builders": [
{
"name": "test1",
......
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