builder/amazon: Add force_deregister option, to automatically deregister artifacts with name conflicts
This commit is contained in:
parent
35b8df1816
commit
bec59b535d
|
@ -147,6 +147,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
|
||||
// Build the steps
|
||||
steps := []multistep.Step{
|
||||
&awscommon.StepPreValidate{
|
||||
DestAmiName: b.config.AMIName,
|
||||
ForceDeregister: b.config.AMIForceDeregister,
|
||||
},
|
||||
&StepInstanceInfo{},
|
||||
&awscommon.StepSourceAMIInfo{
|
||||
SourceAmi: b.config.SourceAmi,
|
||||
|
@ -164,6 +168,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
&StepChrootProvision{},
|
||||
&StepEarlyCleanup{},
|
||||
&StepSnapshot{},
|
||||
&awscommon.StepDeregisterAMI{
|
||||
ForceDeregister: b.config.AMIForceDeregister,
|
||||
AMIName: b.config.AMIName,
|
||||
},
|
||||
&StepRegisterAMI{},
|
||||
&awscommon.StepAMIRegionCopy{
|
||||
AccessConfig: &b.config.AccessConfig,
|
||||
|
|
|
@ -17,6 +17,7 @@ type AMIConfig struct {
|
|||
AMIRegions []string `mapstructure:"ami_regions"`
|
||||
AMITags map[string]string `mapstructure:"tags"`
|
||||
AMIEnhancedNetworking bool `mapstructure:"enhanced_networking"`
|
||||
AMIForceDeregister bool `mapstructure:"force_deregister"`
|
||||
}
|
||||
|
||||
func (c *AMIConfig) Prepare(ctx *interpolate.Context) []error {
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
)
|
||||
|
||||
type StepDeregisterAMI struct {
|
||||
ForceDeregister bool
|
||||
AMIName string
|
||||
}
|
||||
|
||||
func (s *StepDeregisterAMI) Run(state multistep.StateBag) multistep.StepAction {
|
||||
ec2conn := state.Get("ec2").(*ec2.EC2)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
// check for force deregister
|
||||
if s.ForceDeregister {
|
||||
resp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{
|
||||
Filters: []*ec2.Filter{&ec2.Filter{
|
||||
Name: aws.String("name"),
|
||||
Values: []*string{aws.String(s.AMIName)},
|
||||
}}})
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating AMI: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// deregister image(s) by that name
|
||||
for _, i := range resp.Images {
|
||||
_, err := ec2conn.DeregisterImage(&ec2.DeregisterImageInput{
|
||||
ImageID: i.ImageID,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error deregistering existing AMI: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
ui.Say(fmt.Sprintf("Deregistered AMI %s, id: %s", s.AMIName, *i.ImageID))
|
||||
}
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepDeregisterAMI) Cleanup(state multistep.StateBag) {
|
||||
}
|
|
@ -13,12 +13,18 @@ import (
|
|||
// the build before actually doing any time consuming work
|
||||
//
|
||||
type StepPreValidate struct {
|
||||
DestAmiName string
|
||||
DestAmiName string
|
||||
ForceDeregister bool
|
||||
}
|
||||
|
||||
func (s *StepPreValidate) Run(state multistep.StateBag) multistep.StepAction {
|
||||
ec2conn := state.Get("ec2").(*ec2.EC2)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
if s.ForceDeregister {
|
||||
ui.Say("Force Deregister flag found, skipping prevalidating AMI Name")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
ec2conn := state.Get("ec2").(*ec2.EC2)
|
||||
|
||||
ui.Say("Prevalidating AMI Name...")
|
||||
resp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{
|
||||
|
|
|
@ -79,7 +79,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
// Build the steps
|
||||
steps := []multistep.Step{
|
||||
&awscommon.StepPreValidate{
|
||||
DestAmiName: b.config.AMIName,
|
||||
DestAmiName: b.config.AMIName,
|
||||
ForceDeregister: b.config.AMIForceDeregister,
|
||||
},
|
||||
&awscommon.StepSourceAMIInfo{
|
||||
SourceAmi: b.config.SourceAmi,
|
||||
|
@ -122,6 +123,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
&stepStopInstance{SpotPrice: b.config.SpotPrice},
|
||||
// TODO(mitchellh): verify works with spots
|
||||
&stepModifyInstance{},
|
||||
&awscommon.StepDeregisterAMI{
|
||||
ForceDeregister: b.config.AMIForceDeregister,
|
||||
AMIName: b.config.AMIName,
|
||||
},
|
||||
&stepCreateAMI{},
|
||||
&awscommon.StepAMIRegionCopy{
|
||||
AccessConfig: &b.config.AccessConfig,
|
||||
|
|
|
@ -28,6 +28,22 @@ func TestBuilderAcc_regionCopy(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestBuilderAcc_forceDeregister(t *testing.T) {
|
||||
// Build the same AMI name twice, with force_deregister on the second run
|
||||
builderT.Test(t, builderT.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Builder: &Builder{},
|
||||
Template: buildForceDeregisterConfig("false", "dereg"),
|
||||
SkipArtifactTeardown: true,
|
||||
})
|
||||
|
||||
builderT.Test(t, builderT.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Builder: &Builder{},
|
||||
Template: buildForceDeregisterConfig("true", "dereg"),
|
||||
})
|
||||
}
|
||||
|
||||
func checkRegionCopy(regions []string) builderT.TestCheckFunc {
|
||||
return func(artifacts []packer.Artifact) error {
|
||||
if len(artifacts) > 1 {
|
||||
|
@ -107,3 +123,21 @@ const testBuilderAccRegionCopy = `
|
|||
}]
|
||||
}
|
||||
`
|
||||
|
||||
const testBuilderAccForceDeregister = `
|
||||
{
|
||||
"builders": [{
|
||||
"type": "test",
|
||||
"region": "us-east-1",
|
||||
"instance_type": "m3.medium",
|
||||
"source_ami": "ami-76b2a71e",
|
||||
"ssh_username": "ubuntu",
|
||||
"force_deregister": "%s",
|
||||
"ami_name": "packer-test-%s"
|
||||
}]
|
||||
}
|
||||
`
|
||||
|
||||
func buildForceDeregisterConfig(name, flag string) string {
|
||||
return fmt.Sprintf(testBuilderAccForceDeregister, name, flag)
|
||||
}
|
||||
|
|
|
@ -167,6 +167,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
|
||||
// Build the steps
|
||||
steps := []multistep.Step{
|
||||
&awscommon.StepPreValidate{
|
||||
DestAmiName: b.config.AMIName,
|
||||
ForceDeregister: b.config.AMIForceDeregister,
|
||||
},
|
||||
&awscommon.StepSourceAMIInfo{
|
||||
SourceAmi: b.config.SourceAmi,
|
||||
EnhancedNetworking: b.config.AMIEnhancedNetworking,
|
||||
|
@ -211,6 +215,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
&StepUploadBundle{
|
||||
Debug: b.config.PackerDebug,
|
||||
},
|
||||
&awscommon.StepDeregisterAMI{
|
||||
ForceDeregister: b.config.AMIForceDeregister,
|
||||
AMIName: b.config.AMIName,
|
||||
},
|
||||
&StepRegisterAMI{},
|
||||
&awscommon.StepAMIRegionCopy{
|
||||
AccessConfig: &b.config.AccessConfig,
|
||||
|
|
|
@ -41,6 +41,10 @@ type TestCase struct {
|
|||
// in the case that the test can't guarantee all resources were
|
||||
// properly cleaned up.
|
||||
Teardown TestTeardownFunc
|
||||
|
||||
// If SkipArtifactTeardown is true, we will not attempt to destroy the
|
||||
// artifact created in this test run.
|
||||
SkipArtifactTeardown bool
|
||||
}
|
||||
|
||||
// TestCheckFunc is the callback used for Check in TestStep.
|
||||
|
@ -163,12 +167,14 @@ func Test(t TestT, c TestCase) {
|
|||
}
|
||||
|
||||
TEARDOWN:
|
||||
// Delete all artifacts
|
||||
for _, a := range artifacts {
|
||||
if err := a.Destroy(); err != nil {
|
||||
t.Error(fmt.Sprintf(
|
||||
"!!! ERROR REMOVING ARTIFACT '%s': %s !!!",
|
||||
a.String(), err))
|
||||
if !c.SkipArtifactTeardown {
|
||||
// Delete all artifacts
|
||||
for _, a := range artifacts {
|
||||
if err := a.Destroy(); err != nil {
|
||||
t.Error(fmt.Sprintf(
|
||||
"!!! ERROR REMOVING ARTIFACT '%s': %s !!!",
|
||||
a.String(), err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue