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

builder/amazon: Add force_deregister option
This commit is contained in:
Clint 2015-06-15 10:02:30 -05:00
commit 8fca582278
11 changed files with 142 additions and 9 deletions

View File

@ -147,6 +147,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
// Build the steps // Build the steps
steps := []multistep.Step{ steps := []multistep.Step{
&awscommon.StepPreValidate{
DestAmiName: b.config.AMIName,
ForceDeregister: b.config.AMIForceDeregister,
},
&StepInstanceInfo{}, &StepInstanceInfo{},
&awscommon.StepSourceAMIInfo{ &awscommon.StepSourceAMIInfo{
SourceAmi: b.config.SourceAmi, SourceAmi: b.config.SourceAmi,
@ -164,6 +168,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&StepChrootProvision{}, &StepChrootProvision{},
&StepEarlyCleanup{}, &StepEarlyCleanup{},
&StepSnapshot{}, &StepSnapshot{},
&awscommon.StepDeregisterAMI{
ForceDeregister: b.config.AMIForceDeregister,
AMIName: b.config.AMIName,
},
&StepRegisterAMI{}, &StepRegisterAMI{},
&awscommon.StepAMIRegionCopy{ &awscommon.StepAMIRegionCopy{
AccessConfig: &b.config.AccessConfig, AccessConfig: &b.config.AccessConfig,

View File

@ -17,6 +17,7 @@ type AMIConfig struct {
AMIRegions []string `mapstructure:"ami_regions"` AMIRegions []string `mapstructure:"ami_regions"`
AMITags map[string]string `mapstructure:"tags"` AMITags map[string]string `mapstructure:"tags"`
AMIEnhancedNetworking bool `mapstructure:"enhanced_networking"` AMIEnhancedNetworking bool `mapstructure:"enhanced_networking"`
AMIForceDeregister bool `mapstructure:"force_deregister"`
} }
func (c *AMIConfig) Prepare(ctx *interpolate.Context) []error { func (c *AMIConfig) Prepare(ctx *interpolate.Context) []error {

View File

@ -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) {
}

View File

@ -14,11 +14,17 @@ import (
// //
type StepPreValidate struct { type StepPreValidate struct {
DestAmiName string DestAmiName string
ForceDeregister bool
} }
func (s *StepPreValidate) Run(state multistep.StateBag) multistep.StepAction { func (s *StepPreValidate) Run(state multistep.StateBag) multistep.StepAction {
ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui) 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...") ui.Say("Prevalidating AMI Name...")
resp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ resp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{

View File

@ -81,6 +81,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
steps := []multistep.Step{ steps := []multistep.Step{
&awscommon.StepPreValidate{ &awscommon.StepPreValidate{
DestAmiName: b.config.AMIName, DestAmiName: b.config.AMIName,
ForceDeregister: b.config.AMIForceDeregister,
}, },
&awscommon.StepSourceAMIInfo{ &awscommon.StepSourceAMIInfo{
SourceAmi: b.config.SourceAmi, 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}, &stepStopInstance{SpotPrice: b.config.SpotPrice},
// TODO(mitchellh): verify works with spots // TODO(mitchellh): verify works with spots
&stepModifyInstance{}, &stepModifyInstance{},
&awscommon.StepDeregisterAMI{
ForceDeregister: b.config.AMIForceDeregister,
AMIName: b.config.AMIName,
},
&stepCreateAMI{}, &stepCreateAMI{},
&awscommon.StepAMIRegionCopy{ &awscommon.StepAMIRegionCopy{
AccessConfig: &b.config.AccessConfig, AccessConfig: &b.config.AccessConfig,

View File

@ -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 { func checkRegionCopy(regions []string) builderT.TestCheckFunc {
return func(artifacts []packer.Artifact) error { return func(artifacts []packer.Artifact) error {
if len(artifacts) > 1 { 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)
}

View File

@ -168,6 +168,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
// Build the steps // Build the steps
steps := []multistep.Step{ steps := []multistep.Step{
&awscommon.StepPreValidate{
DestAmiName: b.config.AMIName,
ForceDeregister: b.config.AMIForceDeregister,
},
&awscommon.StepSourceAMIInfo{ &awscommon.StepSourceAMIInfo{
SourceAmi: b.config.SourceAmi, SourceAmi: b.config.SourceAmi,
EnhancedNetworking: b.config.AMIEnhancedNetworking, EnhancedNetworking: b.config.AMIEnhancedNetworking,
@ -218,6 +222,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&StepUploadBundle{ &StepUploadBundle{
Debug: b.config.PackerDebug, Debug: b.config.PackerDebug,
}, },
&awscommon.StepDeregisterAMI{
ForceDeregister: b.config.AMIForceDeregister,
AMIName: b.config.AMIName,
},
&StepRegisterAMI{}, &StepRegisterAMI{},
&awscommon.StepAMIRegionCopy{ &awscommon.StepAMIRegionCopy{
AccessConfig: &b.config.AccessConfig, AccessConfig: &b.config.AccessConfig,

View File

@ -41,6 +41,10 @@ type TestCase struct {
// in the case that the test can't guarantee all resources were // in the case that the test can't guarantee all resources were
// properly cleaned up. // properly cleaned up.
Teardown TestTeardownFunc 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. // TestCheckFunc is the callback used for Check in TestStep.
@ -163,6 +167,7 @@ func Test(t TestT, c TestCase) {
} }
TEARDOWN: TEARDOWN:
if !c.SkipArtifactTeardown {
// Delete all artifacts // Delete all artifacts
for _, a := range artifacts { for _, a := range artifacts {
if err := a.Destroy(); err != nil { if err := a.Destroy(); err != nil {
@ -171,6 +176,7 @@ TEARDOWN:
a.String(), err)) a.String(), err))
} }
} }
}
// Teardown // Teardown
if c.Teardown != nil { if c.Teardown != nil {

View File

@ -124,6 +124,9 @@ each category, the available configuration keys are alphabetized.
* `enhanced_networking` (boolean) - Enable enhanced networking (SriovNetSupport) on * `enhanced_networking` (boolean) - Enable enhanced networking (SriovNetSupport) on
HVM-compatible AMIs. If true, add `ec2:ModifyInstanceAttribute` to your AWS IAM policy. 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 * `mount_path` (string) - The path where the volume will be mounted. This is
where the chroot environment will be. This defaults to where the chroot environment will be. This defaults to
`packer-amazon-chroot-volumes/{{.Device}}`. This is a configuration `packer-amazon-chroot-volumes/{{.Device}}`. This is a configuration

View File

@ -96,6 +96,9 @@ each category, the available configuration keys are alphabetized.
* `enhanced_networking` (boolean) - Enable enhanced networking (SriovNetSupport) on * `enhanced_networking` (boolean) - Enable enhanced networking (SriovNetSupport) on
HVM-compatible AMIs. If true, add `ec2:ModifyInstanceAttribute` to your AWS IAM policy. 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` (string) - The name of an
[IAM instance profile](http://docs.aws.amazon.com/IAM/latest/UserGuide/instance-profiles.html) [IAM instance profile](http://docs.aws.amazon.com/IAM/latest/UserGuide/instance-profiles.html)
to launch the EC2 instance with. to launch the EC2 instance with.

View File

@ -136,6 +136,9 @@ each category, the available configuration keys are alphabetized.
* `enhanced_networking` (boolean) - Enable enhanced networking (SriovNetSupport) on * `enhanced_networking` (boolean) - Enable enhanced networking (SriovNetSupport) on
HVM-compatible AMIs. If true, add `ec2:ModifyInstanceAttribute` to your AWS IAM policy. 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` (string) - The name of an
[IAM instance profile](http://docs.aws.amazon.com/IAM/latest/UserGuide/instance-profiles.html) [IAM instance profile](http://docs.aws.amazon.com/IAM/latest/UserGuide/instance-profiles.html)
to launch the EC2 instance with. to launch the EC2 instance with.