Commit 839784b0 authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

template: parse post-processors

parent fbda5b11
......@@ -17,11 +17,11 @@ type rawTemplate struct {
MinVersion string `mapstructure:"min_packer_version"`
Description string
Builders []map[string]interface{}
Push map[string]interface{}
PostProcesors []interface{} `mapstructure:"post-processors"`
Provisioners []map[string]interface{}
Variables map[string]interface{}
Builders []map[string]interface{}
Push map[string]interface{}
PostProcessors []interface{} `mapstructure:"post-processors"`
Provisioners []map[string]interface{}
Variables map[string]interface{}
}
// Template returns the actual Template object built from this raw
......@@ -94,6 +94,49 @@ func (r *rawTemplate) Template() (*Template, error) {
result.Builders[b.Name] = &b
}
// Gather all the post-processors
if len(r.PostProcessors) > 0 {
result.PostProcessors = make([][]*PostProcessor, 0, len(r.PostProcessors))
}
for i, v := range r.PostProcessors {
// Parse the configurations. We need to do this because post-processors
// can take three different formats.
configs, err := r.parsePostProcessor(i, v)
if err != nil {
errs = multierror.Append(errs, err)
continue
}
// Parse the PostProcessors out of the configs
pps := make([]*PostProcessor, 0, len(configs))
for j, c := range configs {
var pp PostProcessor
if err := r.decoder(&pp, nil).Decode(c); err != nil {
errs = multierror.Append(errs, fmt.Errorf(
"post-processor %d.%d: %s", i+1, j+1, err))
continue
}
// Type is required
if pp.Type == "" {
errs = multierror.Append(errs, fmt.Errorf(
"post-processor %d.%d: type is required", i+1, j+1))
continue
}
// Set the configuration
delete(c, "keep_input_artifact")
delete(c, "type")
if len(c) > 0 {
pp.Config = c
}
pps = append(pps, &pp)
}
result.PostProcessors = append(result.PostProcessors, pps)
}
// Gather all the provisioners
if len(r.Provisioners) > 0 {
result.Provisioners = make([]*Provisioner, 0, len(r.Provisioners))
......@@ -152,6 +195,45 @@ func (r *rawTemplate) decoder(
return d
}
func (r *rawTemplate) parsePostProcessor(
i int, raw interface{}) ([]map[string]interface{}, error) {
switch v := raw.(type) {
case string:
return []map[string]interface{}{
{"type": v},
}, nil
case map[string]interface{}:
return []map[string]interface{}{v}, nil
case []interface{}:
var err error
result := make([]map[string]interface{}, len(v))
for j, innerRaw := range v {
switch innerV := innerRaw.(type) {
case string:
result[j] = map[string]interface{}{"type": innerV}
case map[string]interface{}:
result[j] = innerV
case []interface{}:
err = multierror.Append(err, fmt.Errorf(
"post-processor %d.%d: sequence not allowed to be nested in a sequence",
i+1, j+1))
default:
err = multierror.Append(err, fmt.Errorf(
"post-processor %d.%d: unknown format",
i+1, j+1))
}
}
if err != nil {
return nil, err
}
return result, nil
default:
return nil, fmt.Errorf("post-processor %d: bad format", i+1)
}
}
// Parse takes the given io.Reader and parses a Template object out of it.
func Parse(r io.Reader) (*Template, error) {
// First, decode the object into an interface{}. We do this instead of
......
......@@ -141,6 +141,108 @@ func TestParse(t *testing.T) {
},
false,
},
{
"parse-pp-basic.json",
&Template{
PostProcessors: [][]*PostProcessor{
[]*PostProcessor{
&PostProcessor{
Type: "foo",
Config: map[string]interface{}{
"foo": "bar",
},
},
},
},
},
false,
},
{
"parse-pp-keep.json",
&Template{
PostProcessors: [][]*PostProcessor{
[]*PostProcessor{
&PostProcessor{
Type: "foo",
KeepInputArtifact: true,
},
},
},
},
false,
},
{
"parse-pp-string.json",
&Template{
PostProcessors: [][]*PostProcessor{
[]*PostProcessor{
&PostProcessor{
Type: "foo",
},
},
},
},
false,
},
{
"parse-pp-map.json",
&Template{
PostProcessors: [][]*PostProcessor{
[]*PostProcessor{
&PostProcessor{
Type: "foo",
},
},
},
},
false,
},
{
"parse-pp-slice.json",
&Template{
PostProcessors: [][]*PostProcessor{
[]*PostProcessor{
&PostProcessor{
Type: "foo",
},
},
[]*PostProcessor{
&PostProcessor{
Type: "bar",
},
},
},
},
false,
},
{
"parse-pp-multi.json",
&Template{
PostProcessors: [][]*PostProcessor{
[]*PostProcessor{
&PostProcessor{
Type: "foo",
},
&PostProcessor{
Type: "bar",
},
},
},
},
false,
},
{
"parse-pp-no-type.json",
nil,
true,
},
}
for _, tc := range cases {
......
......@@ -27,10 +27,10 @@ type Builder struct {
// PostProcessor represents a post-processor within the template.
type PostProcessor struct {
OnlyExcept
OnlyExcept `mapstructure:",squash"`
Type string
KeepInputArtifact bool
KeepInputArtifact bool `mapstructure:"keep_input_artifact"`
Config map[string]interface{}
}
......@@ -79,3 +79,11 @@ func (b *Builder) GoString() string {
func (p *Provisioner) GoString() string {
return fmt.Sprintf("*%#v", *p)
}
func (p *PostProcessor) GoString() string {
return fmt.Sprintf("*%#v", *p)
}
func (v *Variable) GoString() string {
return fmt.Sprintf("*%#v", *v)
}
{
"post-processors": [{
"type": "foo",
"foo": "bar"
}]
}
{
"post-processors": [{
"type": "foo",
"keep_input_artifact": true
}]
}
{
"post-processors": [{
"type": "foo"
}]
}
{
"post-processors": [[{
"type": "foo"
}, "bar"]]
}
{
"post-processors": [{
"keep_input_artifact": true
}]
}
{
"post-processors": [{
"type": "foo"
}, "bar"]
}
{
"post-processors": ["foo"]
}
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