packer: PostProcessor API change so they can keep artifacts [GH-55]

This commit is contained in:
Mitchell Hashimoto 2013-07-01 11:30:39 -07:00
parent 8eb30482f8
commit f78cbb45a6
13 changed files with 93 additions and 60 deletions

View File

@ -13,6 +13,7 @@ IMPROVEMENTS:
BUG FIXES:
* core: More plugin server fixes that avoid hangs on OS X 10.7 [GH-87]
* vagrant: AWS boxes will keep the AMI artifact around [GH-55]
* virtualbox: More robust version parsing for uploading guest additions. [GH-69]
* virtualbox: Output dir and VM name defaults depend on build name,
avoiding collisions. [GH-91]

View File

@ -194,7 +194,7 @@ PostProcessorRunSeqLoop:
}
builderUi.Say(fmt.Sprintf("Running post-processor: %s", corePP.processorType))
artifact, err := corePP.processor.PostProcess(ppUi, priorArtifact)
artifact, keep, err := corePP.processor.PostProcess(ppUi, priorArtifact)
if err != nil {
errors = append(errors, fmt.Errorf("Post-processor failed: %s", err))
continue PostProcessorRunSeqLoop
@ -205,11 +205,12 @@ PostProcessorRunSeqLoop:
continue PostProcessorRunSeqLoop
}
keep = keep || corePP.keepInputArtifact
if i == 0 {
// This is the first post-processor. We handle deleting
// previous artifacts a bit different because multiple
// post-processors may be using the original and need it.
if !keepOriginalArtifact && corePP.keepInputArtifact {
if !keepOriginalArtifact && keep {
log.Printf(
"Flagging to keep original artifact from post-processor '%s'",
corePP.processorType)
@ -218,7 +219,7 @@ PostProcessorRunSeqLoop:
} else {
// We have a prior artifact. If we want to keep it, we append
// it to the results list. Otherwise, we destroy it.
if corePP.keepInputArtifact {
if keep {
artifacts = append(artifacts, priorArtifact)
} else {
log.Printf("Deleting prior artifact from post-processor '%s'", corePP.processorType)

View File

@ -245,6 +245,33 @@ func TestBuild_Run_Artifacts(t *testing.T) {
if !reflect.DeepEqual(artifactIds, expectedIds) {
t.Fatalf("unexpected ids: %#v", artifactIds)
}
// Test case: Test that with a single post-processor that forcibly
// keeps inputs, that the artifacts are kept.
build = testBuild()
build.postProcessors = [][]coreBuildPostProcessor{
[]coreBuildPostProcessor{
coreBuildPostProcessor{
&TestPostProcessor{artifactId: "pp", keep: true}, "pp", 42, false,
},
},
}
build.Prepare()
artifacts, err = build.Run(ui, cache)
if err != nil {
t.Fatalf("err: %s", err)
}
expectedIds = []string{"b", "pp"}
artifactIds = make([]string, len(artifacts))
for i, artifact := range artifacts {
artifactIds[i] = artifact.Id()
}
if !reflect.DeepEqual(artifactIds, expectedIds) {
t.Fatalf("unexpected ids: %#v", artifactIds)
}
}
func TestBuild_RunBeforePrepare(t *testing.T) {

View File

@ -19,7 +19,7 @@ func (c *cmdPostProcessor) Configure(config interface{}) error {
return c.p.Configure(config)
}
func (c *cmdPostProcessor) PostProcess(ui packer.Ui, a packer.Artifact) (packer.Artifact, error) {
func (c *cmdPostProcessor) PostProcess(ui packer.Ui, a packer.Artifact) (packer.Artifact, bool, error) {
defer func() {
r := recover()
c.checkExit(r, nil)

View File

@ -12,8 +12,8 @@ func (helperPostProcessor) Configure(interface{}) error {
return nil
}
func (helperPostProcessor) PostProcess(packer.Ui, packer.Artifact) (packer.Artifact, error) {
return nil, nil
func (helperPostProcessor) PostProcess(packer.Ui, packer.Artifact) (packer.Artifact, bool, error) {
return nil, false, nil
}
func TestPostProcessor_NoExist(t *testing.T) {

View File

@ -12,6 +12,7 @@ type PostProcessor interface {
Configure(interface{}) error
// PostProcess takes a previously created Artifact and produces another
// Artifact. If an error occurs, it should return that error.
PostProcess(Ui, Artifact) (Artifact, error)
// Artifact. If an error occurs, it should return that error. If `keep`
// is to true, then the previous artifact is forcibly kept.
PostProcess(Ui, Artifact) (a Artifact, keep bool, err error)
}

View File

@ -2,6 +2,7 @@ package packer
type TestPostProcessor struct {
artifactId string
keep bool
configCalled bool
configVal interface{}
ppCalled bool
@ -15,9 +16,9 @@ func (pp *TestPostProcessor) Configure(v interface{}) error {
return nil
}
func (pp *TestPostProcessor) PostProcess(ui Ui, a Artifact) (Artifact, error) {
func (pp *TestPostProcessor) PostProcess(ui Ui, a Artifact) (Artifact, bool, error) {
pp.ppCalled = true
pp.ppArtifact = a
pp.ppUi = ui
return &TestArtifact{id: pp.artifactId}, nil
return &TestArtifact{id: pp.artifactId}, pp.keep, nil
}

View File

@ -19,6 +19,7 @@ type PostProcessorServer struct {
type PostProcessorProcessResponse struct {
Err error
Keep bool
RPCAddress string
}
@ -33,30 +34,30 @@ func (p *postProcessor) Configure(raw interface{}) (err error) {
return
}
func (p *postProcessor) PostProcess(ui packer.Ui, a packer.Artifact) (packer.Artifact, error) {
func (p *postProcessor) PostProcess(ui packer.Ui, a packer.Artifact) (packer.Artifact, bool, error) {
server := rpc.NewServer()
RegisterArtifact(server, a)
RegisterUi(server, ui)
var response PostProcessorProcessResponse
if err := p.client.Call("PostProcessor.PostProcess", serveSingleConn(server), &response); err != nil {
return nil, err
return nil, false, err
}
if response.Err != nil {
return nil, response.Err
return nil, false, response.Err
}
if response.RPCAddress == "" {
return nil, nil
return nil, false, nil
}
client, err := rpc.Dial("tcp", response.RPCAddress)
if err != nil {
return nil, err
return nil, false, err
}
return Artifact(client), nil
return Artifact(client), response.Keep, nil
}
func (p *PostProcessorServer) Configure(raw *interface{}, reply *error) error {
@ -76,7 +77,7 @@ func (p *PostProcessorServer) PostProcess(address string, reply *PostProcessorPr
responseAddress := ""
artifact, err := p.p.PostProcess(&Ui{client}, Artifact(client))
artifact, keep, err := p.p.PostProcess(&Ui{client}, Artifact(client))
if err == nil && artifact != nil {
server := rpc.NewServer()
RegisterArtifact(server, artifact)
@ -89,6 +90,7 @@ func (p *PostProcessorServer) PostProcess(address string, reply *PostProcessorPr
*reply = PostProcessorProcessResponse{
Err: err,
Keep: keep,
RPCAddress: responseAddress,
}

View File

@ -22,11 +22,11 @@ func (pp *TestPostProcessor) Configure(v interface{}) error {
return nil
}
func (pp *TestPostProcessor) PostProcess(ui packer.Ui, a packer.Artifact) (packer.Artifact, error) {
func (pp *TestPostProcessor) PostProcess(ui packer.Ui, a packer.Artifact) (packer.Artifact, bool, error) {
pp.ppCalled = true
pp.ppArtifact = a
pp.ppUi = ui
return testPostProcessorArtifact, nil
return testPostProcessorArtifact, false, nil
}
func TestPostProcessorRPC(t *testing.T) {
@ -63,7 +63,7 @@ func TestPostProcessorRPC(t *testing.T) {
// Test PostProcess
a := new(testArtifact)
ui := new(testUi)
artifact, err := pClient.PostProcess(ui, a)
artifact, _, err := pClient.PostProcess(ui, a)
if err != nil {
t.Fatalf("err: %s", err)
}

View File

@ -33,7 +33,7 @@ func (p *AWSBoxPostProcessor) Configure(raw interface{}) error {
return nil
}
func (p *AWSBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, error) {
func (p *AWSBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
// Determine the regions...
tplData := &AWSVagrantfileTemplate{
Images: make(map[string]string),
@ -42,7 +42,7 @@ func (p *AWSBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact
for _, regions := range strings.Split(artifact.Id(), ",") {
parts := strings.Split(regions, ":")
if len(parts) != 2 {
return nil, fmt.Errorf("Poorly formatted artifact ID: %s", artifact.Id())
return nil, false, fmt.Errorf("Poorly formatted artifact ID: %s", artifact.Id())
}
tplData.Images[parts[0]] = parts[1]
@ -51,20 +51,20 @@ func (p *AWSBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact
// Compile the output path
outputPath, err := ProcessOutputPath(p.config.OutputPath, "aws", artifact)
if err != nil {
return nil, err
return nil, false, err
}
// Create a temporary directory for us to build the contents of the box in
dir, err := ioutil.TempDir("", "packer")
if err != nil {
return nil, err
return nil, false, err
}
defer os.RemoveAll(dir)
// Create the Vagrantfile from the template
vf, err := os.Create(filepath.Join(dir, "Vagrantfile"))
if err != nil {
return nil, err
return nil, false, err
}
defer vf.Close()
@ -72,13 +72,13 @@ func (p *AWSBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact
if p.config.VagrantfileTemplate != "" {
f, err := os.Open(p.config.VagrantfileTemplate)
if err != nil {
return nil, err
return nil, false, err
}
defer f.Close()
contents, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
return nil, false, err
}
vagrantfileContents = string(contents)
@ -91,15 +91,15 @@ func (p *AWSBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact
// Create the metadata
metadata := map[string]string{"provider": "aws"}
if err := WriteMetadata(dir, metadata); err != nil {
return nil, err
return nil, false, err
}
// Compress the directory to the given output path
if err := DirToBox(outputPath, dir); err != nil {
return nil, err
return nil, false, err
}
return NewArtifact("aws", outputPath), nil
return NewArtifact("aws", outputPath), true, nil
}
var defaultAWSVagrantfile = `

View File

@ -68,10 +68,10 @@ func (p *PostProcessor) Configure(raw interface{}) error {
return nil
}
func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, error) {
func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
ppName, ok := builtins[artifact.BuilderId()]
if !ok {
return nil, fmt.Errorf("Unknown artifact type, can't build box: %s", artifact.BuilderId())
return nil, false, fmt.Errorf("Unknown artifact type, can't build box: %s", artifact.BuilderId())
}
// Use the premade PostProcessor if we have one. Otherwise, we
@ -81,12 +81,12 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac
log.Printf("Premade post-processor for '%s' not found. Creating.", ppName)
pp = keyToPostProcessor(ppName)
if pp == nil {
return nil, fmt.Errorf("Vagrant box post-processor not found: %s", ppName)
return nil, false, fmt.Errorf("Vagrant box post-processor not found: %s", ppName)
}
config := map[string]string{"output": p.config.OutputPath}
if err := pp.Configure(config); err != nil {
return nil, err
return nil, false, err
}
}

View File

@ -37,24 +37,24 @@ func (p *VBoxBoxPostProcessor) Configure(raw interface{}) error {
return nil
}
func (p *VBoxBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, error) {
func (p *VBoxBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
var err error
tplData := &VBoxVagrantfileTemplate{}
tplData.BaseMacAddress, err = p.findBaseMacAddress(artifact)
if err != nil {
return nil, err
return nil, false, err
}
// Compile the output path
outputPath, err := ProcessOutputPath(p.config.OutputPath, "virtualbox", artifact)
if err != nil {
return nil, err
return nil, false, err
}
// Create a temporary directory for us to build the contents of the box in
dir, err := ioutil.TempDir("", "packer")
if err != nil {
return nil, err
return nil, false, err
}
defer os.RemoveAll(dir)
@ -63,25 +63,25 @@ func (p *VBoxBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifac
ui.Message(fmt.Sprintf("Copying: %s", path))
src, err := os.Open(path)
if err != nil {
return nil, err
return nil, false, err
}
defer src.Close()
dst, err := os.Create(filepath.Join(dir, filepath.Base(path)))
if err != nil {
return nil, err
return nil, false, err
}
defer dst.Close()
if _, err := io.Copy(dst, src); err != nil {
return nil, err
return nil, false, err
}
}
// Create the Vagrantfile from the template
vf, err := os.Create(filepath.Join(dir, "Vagrantfile"))
if err != nil {
return nil, err
return nil, false, err
}
defer vf.Close()
@ -89,13 +89,13 @@ func (p *VBoxBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifac
if p.config.VagrantfileTemplate != "" {
f, err := os.Open(p.config.VagrantfileTemplate)
if err != nil {
return nil, err
return nil, false, err
}
defer f.Close()
contents, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
return nil, false, err
}
vagrantfileContents = string(contents)
@ -108,22 +108,22 @@ func (p *VBoxBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifac
// Create the metadata
metadata := map[string]string{"provider": "virtualbox"}
if err := WriteMetadata(dir, metadata); err != nil {
return nil, err
return nil, false, err
}
// Rename the OVF file to box.ovf, as required by Vagrant
ui.Message("Renaming the OVF to box.ovf...")
if err := p.renameOVF(dir); err != nil {
return nil, err
return nil, false, err
}
// Compress the directory to the given output path
ui.Message(fmt.Sprintf("Compressing box..."))
if err := DirToBox(outputPath, dir); err != nil {
return nil, err
return nil, false, err
}
return NewArtifact("virtualbox", outputPath), nil
return NewArtifact("virtualbox", outputPath), false, nil
}
func (p *VBoxBoxPostProcessor) findBaseMacAddress(a packer.Artifact) (string, error) {

View File

@ -29,17 +29,17 @@ func (p *VMwareBoxPostProcessor) Configure(raw interface{}) error {
return nil
}
func (p *VMwareBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, error) {
func (p *VMwareBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
// Compile the output path
outputPath, err := ProcessOutputPath(p.config.OutputPath, "vmware", artifact)
if err != nil {
return nil, err
return nil, false, err
}
// Create a temporary directory for us to build the contents of the box in
dir, err := ioutil.TempDir("", "packer")
if err != nil {
return nil, err
return nil, false, err
}
defer os.RemoveAll(dir)
@ -48,37 +48,37 @@ func (p *VMwareBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artif
ui.Message(fmt.Sprintf("Copying: %s", path))
src, err := os.Open(path)
if err != nil {
return nil, err
return nil, false, err
}
defer src.Close()
dst, err := os.Create(filepath.Join(dir, filepath.Base(path)))
if err != nil {
return nil, err
return nil, false, err
}
defer dst.Close()
if _, err := io.Copy(dst, src); err != nil {
return nil, err
return nil, false, err
}
}
if p.config.VagrantfileTemplate != "" {
f, err := os.Open(p.config.VagrantfileTemplate)
if err != nil {
return nil, err
return nil, false, err
}
defer f.Close()
contents, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
return nil, false, err
}
// Create the Vagrantfile from the template
vf, err := os.Create(filepath.Join(dir, "Vagrantfile"))
if err != nil {
return nil, err
return nil, false, err
}
defer vf.Close()
@ -90,14 +90,14 @@ func (p *VMwareBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artif
// Create the metadata
metadata := map[string]string{"provider": "vmware_desktop"}
if err := WriteMetadata(dir, metadata); err != nil {
return nil, err
return nil, false, err
}
// Compress the directory to the given output path
ui.Message(fmt.Sprintf("Compressing box..."))
if err := DirToBox(outputPath, dir); err != nil {
return nil, err
return nil, false, err
}
return NewArtifact("vmware", outputPath), nil
return NewArtifact("vmware", outputPath), false, nil
}