Adds `force_delete_snapshot` flag

This PR adds the ability for Packer to clean up snapshots in addition to
deregistering AMIs at build time.

To test this, I used the following `test.json` file:

```json
{
  "builders": [
    {
      "type": "amazon-ebs",
      "region": "us-east-1",
      "source_ami": "ami-fce3c696",
      "ami_name": "packer-test",
      "instance_type": "m3.medium",
      "ssh_username": "ubuntu",
      "vpc_id": "some-vpc-id",
      "subnet_id": "some-subnet-routed-through-igw",
      "security_group_id": "some-security-group-with-port-22-access",
      "force_delete_snapshot": true
    }
  ],
  "provisioners": [
    {
      "type": "shell-local",
      "command": "echo 'hello'"
    }
  ]
}

```

I appreciate any constructive feedbakc that can be given. Cheers!
This commit is contained in:
Arthur Burkart 2016-11-29 23:16:01 -05:00
parent be4086f5fc
commit 5b59f56cdb
9 changed files with 80 additions and 12 deletions

View File

@ -244,8 +244,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&StepEarlyCleanup{},
&StepSnapshot{},
&awscommon.StepDeregisterAMI{
ForceDeregister: b.config.AMIForceDeregister,
AMIName: b.config.AMIName,
ForceDeregister: b.config.AMIForceDeregister,
ForceDeleteSnapshot: b.config.AMIForceDeleteSnapshot,
AMIName: b.config.AMIName,
},
&StepRegisterAMI{
RootVolumeSize: b.config.RootVolumeSize,

View File

@ -19,6 +19,7 @@ type AMIConfig struct {
AMITags map[string]string `mapstructure:"tags"`
AMIEnhancedNetworking bool `mapstructure:"enhanced_networking"`
AMIForceDeregister bool `mapstructure:"force_deregister"`
AMIForceDeleteSnapshot bool `mapstructure:"force_delete_snapshot"`
AMIEncryptBootVolume bool `mapstructure:"encrypt_boot"`
SnapshotTags map[string]string `mapstructure:"snapshot_tags"`
}

View File

@ -10,15 +10,16 @@ import (
)
type StepDeregisterAMI struct {
ForceDeregister bool
AMIName string
ForceDeregister bool
ForceDeleteSnapshot 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
// Check for force deregister
if s.ForceDeregister {
resp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{
Filters: []*ec2.Filter{{
@ -33,7 +34,7 @@ func (s *StepDeregisterAMI) Run(state multistep.StateBag) multistep.StepAction {
return multistep.ActionHalt
}
// deregister image(s) by that name
// Deregister image(s) by name
for _, i := range resp.Images {
_, err := ec2conn.DeregisterImage(&ec2.DeregisterImageInput{
ImageId: i.ImageId,
@ -46,6 +47,25 @@ func (s *StepDeregisterAMI) Run(state multistep.StateBag) multistep.StepAction {
return multistep.ActionHalt
}
ui.Say(fmt.Sprintf("Deregistered AMI %s, id: %s", s.AMIName, *i.ImageId))
// Delete snapshot(s) by image
if s.ForceDeleteSnapshot {
for _, b := range i.BlockDeviceMappings {
if b.Ebs != nil {
_, err := ec2conn.DeleteSnapshot(&ec2.DeleteSnapshotInput{
SnapshotId: b.Ebs.SnapshotId,
})
if err != nil {
err := fmt.Errorf("Error deleting existing snapshot: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
ui.Say(fmt.Sprintf("Deleted snapshot: %s", *b.Ebs.SnapshotId))
}
}
}
}
}

View File

@ -165,8 +165,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
EnableEnhancedNetworking: b.config.AMIEnhancedNetworking,
},
&awscommon.StepDeregisterAMI{
ForceDeregister: b.config.AMIForceDeregister,
AMIName: b.config.AMIName,
ForceDeregister: b.config.AMIForceDeregister,
ForceDeleteSnapshot: b.config.AMIForceDeleteSnapshot,
AMIName: b.config.AMIName,
},
&stepCreateAMI{},
&stepCreateEncryptedAMICopy{},

View File

@ -46,6 +46,22 @@ func TestBuilderAcc_forceDeregister(t *testing.T) {
})
}
func TestBuilderAcc_forceDeleteSnapshot(t *testing.T) {
// Build the same AMI name twice, with force_delete_snapshot on the second run
builderT.Test(t, builderT.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Builder: &Builder{},
Template: buildForceDeleteSnapshotConfig("false", "dereg"),
SkipArtifactTeardown: true,
})
builderT.Test(t, builderT.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Builder: &Builder{},
Template: buildForceDeleteSnapshotConfig("true", "dereg"),
})
}
func TestBuilderAcc_amiSharing(t *testing.T) {
builderT.Test(t, builderT.TestCase{
PreCheck: func() { testAccPreCheck(t) },
@ -254,6 +270,21 @@ const testBuilderAccForceDeregister = `
}
`
const testBuilderAccForceDeleteSnapshot = `
{
"builders": [{
"type": "test",
"region": "us-east-1",
"instance_type": "m3.medium",
"source_ami": "ami-76b2a71e",
"ssh_username": "ubuntu",
"force_deregister": "%s",
"force_delete_snapshot": "%s",
"ami_name": "packer-test-%s"
}]
}
`
// share with catsby
const testBuilderAccSharing = `
{
@ -284,6 +315,10 @@ const testBuilderAccEncrypted = `
}
`
func buildForceDeregisterConfig(name, flag string) string {
return fmt.Sprintf(testBuilderAccForceDeregister, name, flag)
func buildForceDeregisterConfig(val, name string) string {
return fmt.Sprintf(testBuilderAccForceDeregister, val, name)
}
func buildForceDeleteSnapshotConfig(val, name string) string {
return fmt.Sprintf(testBuilderAccForceDeleteSnapshot, val, val, name)
}

View File

@ -247,8 +247,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
Debug: b.config.PackerDebug,
},
&awscommon.StepDeregisterAMI{
ForceDeregister: b.config.AMIForceDeregister,
AMIName: b.config.AMIName,
ForceDeregister: b.config.AMIForceDeregister,
ForceDeleteSnapshot: b.config.AMIForceDeleteSnapshot,
AMIName: b.config.AMIName,
},
&StepRegisterAMI{},
&awscommon.StepAMIRegionCopy{

View File

@ -124,6 +124,9 @@ each category, the available configuration keys are alphabetized.
- `force_deregister` (boolean) - Force Packer to first deregister an existing
AMI if one with the same name already exists. Default `false`.
- `force_delete_snapshot` (boolean) - Force Packer to delete snapshots associated with
AMIs, which have been deregistered by `force_deregister`. Default `false`.
- `from_scratch` (boolean) - Build a new volume instead of starting from an
existing AMI root volume snapshot. Default `false`. If true, `source_ami` is
no longer used and the following options become required:

View File

@ -155,6 +155,9 @@ builder.
- `force_deregister` (boolean) - Force Packer to first deregister an existing
AMI if one with the same name already exists. Default `false`.
- `force_delete_snapshot` (boolean) - Force Packer to delete snapshots associated with
AMIs, which have been deregistered by `force_deregister`. Default `false`.
- `encrypt_boot` (boolean) - Instruct packer to automatically create a copy of the
AMI with an encrypted boot volume (discarding the initial unencrypted AMI in the
process). Default `false`.

View File

@ -179,6 +179,9 @@ builder.
- `force_deregister` (boolean) - Force Packer to first deregister an existing
AMI if one with the same name already exists. Default `false`.
- `force_delete_snapshot` (boolean) - Force Packer to delete snapshots associated with
AMIs, which have been deregistered by `force_deregister`. Default `false`.
- `iam_instance_profile` (string) - The name of an [IAM instance
profile](https://docs.aws.amazon.com/IAM/latest/UserGuide/instance-profiles.html)
to launch the EC2 instance with.