Commit 8fca5822 authored by Clint's avatar Clint

Merge pull request #2221 from mitchellh/f-amazon-force-deregister-artifacts

builder/amazon: Add force_deregister option
parents 7fc69828 c3e79c62
......@@ -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 {
......
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{
......
......@@ -80,7 +80,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,
......@@ -129,6 +130,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)
}
......@@ -168,6 +168,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,
......@@ -218,6 +222,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))
}
}
}
......
......@@ -124,6 +124,9 @@ each category, the available configuration keys are alphabetized.
* `enhanced_networking` (boolean) - Enable enhanced networking (SriovNetSupport) on
HVM-compatible AMIs. If true, add `ec2:ModifyInstanceAttribute` to your AWS IAM policy.
* `force_deregister` (boolean) – Force Packer to first deregister an existing
AMI if one with the same name already exists. Default `false`.
* `mount_path` (string) - The path where the volume will be mounted. This is
where the chroot environment will be. This defaults to
`packer-amazon-chroot-volumes/{{.Device}}`. This is a configuration
......
......@@ -96,6 +96,9 @@ each category, the available configuration keys are alphabetized.
* `enhanced_networking` (boolean) - Enable enhanced networking (SriovNetSupport) on
HVM-compatible AMIs. If true, add `ec2:ModifyInstanceAttribute` to your AWS IAM policy.
* `force_deregister` (boolean) – Force Packer to first deregister an existing
AMI if one with the same name already exists. Default `false`.
* `iam_instance_profile` (string) - The name of an
[IAM instance profile](http://docs.aws.amazon.com/IAM/latest/UserGuide/instance-profiles.html)
to launch the EC2 instance with.
......
......@@ -136,6 +136,9 @@ each category, the available configuration keys are alphabetized.
* `enhanced_networking` (boolean) - Enable enhanced networking (SriovNetSupport) on
HVM-compatible AMIs. If true, add `ec2:ModifyInstanceAttribute` to your AWS IAM policy.
* `force_deregister` (boolean) – Force Packer to first deregister an existing
AMI if one with the same name already exists. Default `false`.
* `iam_instance_profile` (string) - The name of an
[IAM instance profile](http://docs.aws.amazon.com/IAM/latest/UserGuide/instance-profiles.html)
to launch the EC2 instance with.
......
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