101 lines
2.8 KiB
Go
101 lines
2.8 KiB
Go
|
package manifest
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"io/ioutil"
|
||
|
"os"
|
||
|
"time"
|
||
|
|
||
|
"github.com/mitchellh/packer/common"
|
||
|
"github.com/mitchellh/packer/helper/config"
|
||
|
"github.com/mitchellh/packer/packer"
|
||
|
"github.com/mitchellh/packer/template/interpolate"
|
||
|
)
|
||
|
|
||
|
// The artifact-override post-processor allows you to specify arbitrary files as
|
||
|
// artifacts. These will override any other artifacts created by the builder.
|
||
|
// This allows you to use a builder and provisioner to create some file, such as
|
||
|
// a compiled binary or tarball, extract it from the builder (VM or container)
|
||
|
// and then save that binary or tarball and throw away the builder.
|
||
|
|
||
|
type Config struct {
|
||
|
common.PackerConfig `mapstructure:",squash"`
|
||
|
|
||
|
Filename string `mapstructure:"filename"`
|
||
|
|
||
|
ctx interpolate.Context
|
||
|
}
|
||
|
|
||
|
type PostProcessor struct {
|
||
|
config Config
|
||
|
}
|
||
|
|
||
|
type ManifestFile struct {
|
||
|
Builds []Artifact `json:"builds"`
|
||
|
}
|
||
|
|
||
|
func (p *PostProcessor) Configure(raws ...interface{}) error {
|
||
|
err := config.Decode(&p.config, &config.DecodeOpts{
|
||
|
Interpolate: true,
|
||
|
InterpolateContext: &p.config.ctx,
|
||
|
InterpolateFilter: &interpolate.RenderFilter{
|
||
|
Exclude: []string{},
|
||
|
},
|
||
|
}, raws...)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if p.config.Filename == "" {
|
||
|
p.config.Filename = "packer-manifest.json"
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (p *PostProcessor) PostProcess(ui packer.Ui, source packer.Artifact) (packer.Artifact, bool, error) {
|
||
|
artifact := &Artifact{}
|
||
|
|
||
|
// Create the current artifact.
|
||
|
artifact.BuildFiles = source.Files()
|
||
|
artifact.BuildId = source.Id()
|
||
|
artifact.BuildName = source.BuilderId()
|
||
|
artifact.BuildTime = time.Now().Unix()
|
||
|
artifact.Description = source.String()
|
||
|
|
||
|
// Create a lock file with exclusive access. If this fails we will retry
|
||
|
// after a delay
|
||
|
lockFilename := p.config.Filename + ".lock"
|
||
|
_, err := os.OpenFile(lockFilename, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
|
||
|
defer os.Remove(lockFilename)
|
||
|
|
||
|
// Read the current manifest file from disk
|
||
|
contents := []byte{}
|
||
|
if contents, err = ioutil.ReadFile(p.config.Filename); err != nil && !os.IsNotExist(err) {
|
||
|
return nil, true, fmt.Errorf("Unable to open %s for reading: %s", p.config.Filename, err)
|
||
|
}
|
||
|
|
||
|
// Parse the manifest file JSON, if we have some
|
||
|
manifestFile := &ManifestFile{}
|
||
|
if len(contents) > 0 {
|
||
|
if err = json.Unmarshal(contents, manifestFile); err != nil {
|
||
|
return nil, true, fmt.Errorf("Unable to parse content from %s: %s", p.config.Filename, err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Add the current artifact to the manifest file
|
||
|
manifestFile.Builds = append(manifestFile.Builds, *artifact)
|
||
|
|
||
|
// Write JSON to disk
|
||
|
if out, err := json.Marshal(manifestFile); err == nil {
|
||
|
if err := ioutil.WriteFile(p.config.Filename, out, 0664); err != nil {
|
||
|
return nil, true, fmt.Errorf("Unable to write %s: %s", p.config.Filename, err)
|
||
|
}
|
||
|
} else {
|
||
|
return nil, true, fmt.Errorf("Unable to marshal JSON %s", err)
|
||
|
}
|
||
|
|
||
|
return artifact, true, err
|
||
|
}
|