diff --git a/fix/fixer.go b/fix/fixer.go index 78f58f546..382cd25b8 100644 --- a/fix/fixer.go +++ b/fix/fixer.go @@ -29,6 +29,7 @@ func init() { "parallels-headless": new(FixerParallelsHeadless), "parallels-deprecations": new(FixerParallelsDeprecations), "sshkeypath": new(FixerSSHKeyPath), + "manifest-filename": new(FixerManifestFilename), } FixerOrder = []string{ @@ -41,5 +42,6 @@ func init() { "parallels-headless", "parallels-deprecations", "sshkeypath", + "manifest-filename", } } diff --git a/fix/fixer_pp_manifest_filename.go b/fix/fixer_pp_manifest_filename.go new file mode 100644 index 000000000..50b3e5e16 --- /dev/null +++ b/fix/fixer_pp_manifest_filename.go @@ -0,0 +1,69 @@ +package fix + +import ( + "github.com/mitchellh/mapstructure" +) + +// FixerManifestFilename renames any Filename to Output +type FixerManifestFilename struct{} + +func (FixerManifestFilename) Fix(input map[string]interface{}) (map[string]interface{}, error) { + + // Our template type we'll use for this fixer only + type template struct { + PostProcessors []interface{} `mapstructure:"post-processors"` + } + + // Decode the input into our structure, if we can + var tpl template + if err := mapstructure.Decode(input, &tpl); err != nil { + return nil, err + } + + // Go through each post-processor and get out all the complex configs + pps := make([]map[string]interface{}, 0, len(tpl.PostProcessors)) + for _, rawPP := range tpl.PostProcessors { + switch pp := rawPP.(type) { + case string: + case map[string]interface{}: + pps = append(pps, pp) + case []interface{}: + for _, innerRawPP := range pp { + if innerPP, ok := innerRawPP.(map[string]interface{}); ok { + pps = append(pps, innerPP) + } + } + } + } + + for _, pp := range pps { + ppTypeRaw, ok := pp["type"] + if !ok { + continue + } + + if ppType, ok := ppTypeRaw.(string); !ok { + continue + } else if ppType != "manifest" { + continue + } + + filenameRaw, ok := pp["filename"] + if !ok { + continue + } + + if filename, ok := filenameRaw.(string); ok { + delete(pp, "filename") + pp["output"] = filename + } + + } + + input["post-processors"] = tpl.PostProcessors + return input, nil +} + +func (FixerManifestFilename) Synopsis() string { + return `Updates "manifest" post-processor so any "filename" field is renamed to "output".` +} diff --git a/fix/fixer_pp_manifest_filename_test.go b/fix/fixer_pp_manifest_filename_test.go new file mode 100644 index 000000000..907d41260 --- /dev/null +++ b/fix/fixer_pp_manifest_filename_test.go @@ -0,0 +1,53 @@ +package fix + +import ( + "reflect" + "testing" +) + +func TestFixerManifestPPFilename_Impl(t *testing.T) { + var _ Fixer = new(FixerVagrantPPOverride) +} + +func TestFixerManifestPPFilename_Fix(t *testing.T) { + var f FixerManifestFilename + + input := map[string]interface{}{ + "post-processors": []interface{}{ + map[string]interface{}{ + "type": "manifest", + "filename": "foo", + }, + []interface{}{ + map[string]interface{}{ + "type": "manifest", + "filename": "foo", + }, + }, + }, + } + + expected := map[string]interface{}{ + "post-processors": []interface{}{ + map[string]interface{}{ + "type": "manifest", + "output": "foo", + }, + []interface{}{ + map[string]interface{}{ + "type": "manifest", + "output": "foo", + }, + }, + }, + } + + output, err := f.Fix(input) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !reflect.DeepEqual(output, expected) { + t.Fatalf("unexpected: %#v\nexpected: %#v\n", output, expected) + } +} diff --git a/post-processor/manifest/post-processor.go b/post-processor/manifest/post-processor.go index 7b4507cf6..5a75bd34c 100644 --- a/post-processor/manifest/post-processor.go +++ b/post-processor/manifest/post-processor.go @@ -18,9 +18,9 @@ import ( type Config struct { common.PackerConfig `mapstructure:",squash"` - Filename string `mapstructure:"filename"` - StripPath bool `mapstructure:"strip_path"` - ctx interpolate.Context + OutputPath string `mapstructure:"output"` + StripPath bool `mapstructure:"strip_path"` + ctx interpolate.Context } type PostProcessor struct { @@ -44,8 +44,12 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { return err } - if p.config.Filename == "" { - p.config.Filename = "packer-manifest.json" + if p.config.OutputPath == "" { + p.config.OutputPath = "packer-manifest.json" + } + + if err = interpolate.Validate(p.config.OutputPath, &p.config.ctx); err != nil { + return fmt.Errorf("Error parsing target template: %s", err) } return nil @@ -85,7 +89,7 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, source packer.Artifact) (packe // Create a lock file with exclusive access. If this fails we will retry // after a delay. - lockFilename := p.config.Filename + ".lock" + lockFilename := p.config.OutputPath + ".lock" for i := 0; i < 3; i++ { // The file should not be locked for very long so we'll keep this short. time.Sleep((time.Duration(i) * 200 * time.Millisecond)) @@ -97,20 +101,17 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, source packer.Artifact) (packe } defer os.Remove(lockFilename) - // TODO fix error on first run: - // * Post-processor failed: open packer-manifest.json: no such file or directory - // // Read the current manifest file from disk contents := []byte{} - if contents, err = ioutil.ReadFile(p.config.Filename); err != nil && !os.IsNotExist(err) { - return source, true, fmt.Errorf("Unable to open %s for reading: %s", p.config.Filename, err) + if contents, err = ioutil.ReadFile(p.config.OutputPath); err != nil && !os.IsNotExist(err) { + return source, true, fmt.Errorf("Unable to open %s for reading: %s", p.config.OutputPath, err) } // Parse the manifest file JSON, if we have one manifestFile := &ManifestFile{} if len(contents) > 0 { if err = json.Unmarshal(contents, manifestFile); err != nil { - return source, true, fmt.Errorf("Unable to parse content from %s: %s", p.config.Filename, err) + return source, true, fmt.Errorf("Unable to parse content from %s: %s", p.config.OutputPath, err) } } @@ -126,8 +127,8 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, source packer.Artifact) (packe // Write JSON to disk if out, err := json.MarshalIndent(manifestFile, "", " "); err == nil { - if err = ioutil.WriteFile(p.config.Filename, out, 0664); err != nil { - return source, true, fmt.Errorf("Unable to write %s: %s", p.config.Filename, err) + if err = ioutil.WriteFile(p.config.OutputPath, out, 0664); err != nil { + return source, true, fmt.Errorf("Unable to write %s: %s", p.config.OutputPath, err) } } else { return source, true, fmt.Errorf("Unable to marshal JSON %s", err) diff --git a/website/source/docs/post-processors/manifest.html.md b/website/source/docs/post-processors/manifest.html.md index 2130c53a4..7de60c0a7 100644 --- a/website/source/docs/post-processors/manifest.html.md +++ b/website/source/docs/post-processors/manifest.html.md @@ -21,7 +21,7 @@ You can specify manifest more than once and write each build to its own file, or ### Optional: -- `filename` (string) The manifest will be written to this file. This defaults to `packer-manifest.json`. +- `output` (string) The manifest will be written to this file. This defaults to `packer-manifest.json`. - `strip_path` (bool) Write only filename without the path to the manifest file. This defaults to false. ### Example Configuration @@ -33,7 +33,7 @@ You can simply add `{"type":"manifest"}` to your post-processor section. Below i "post-processors": [ { "type": "manifest", - "filename": "manifest.json", + "output": "manifest.json", "strip_path": true } ]