Closes #3908: Adds snapshot tag overrides

This commit adds the ability to configure unique tags on snapshots
that are separate from the tags defined on the AMI. Anything applied
to the AMI will also be applied to the snapshots, but `snapshot_tags`
will override and append tags to the tags already applied to the snapshots
This commit is contained in:
Arthur Burkart 2016-10-16 22:19:55 -04:00
parent 929ca30141
commit 2e65867cba
8 changed files with 55 additions and 18 deletions

View File

@ -262,7 +262,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
ProductCodes: b.config.AMIProductCodes,
},
&awscommon.StepCreateTags{
Tags: b.config.AMITags,
Tags: b.config.AMITags,
SnapshotTags: b.config.SnapshotTags,
},
)

View File

@ -20,6 +20,7 @@ type AMIConfig struct {
AMIEnhancedNetworking bool `mapstructure:"enhanced_networking"`
AMIForceDeregister bool `mapstructure:"force_deregister"`
AMIEncryptBootVolume bool `mapstructure:"encrypt_boot"`
SnapshotTags map[string]string `mapstructure:"snapshot_tags"`
}
func (c *AMIConfig) Prepare(ctx *interpolate.Context) []error {

View File

@ -13,7 +13,8 @@ import (
)
type StepCreateTags struct {
Tags map[string]string
Tags map[string]string
SnapshotTags map[string]string
}
func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
@ -21,21 +22,15 @@ func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
amis := state.Get("amis").(map[string]string)
if len(s.Tags) > 0 {
if len(s.Tags) > 0 || len(s.SnapshotTags) > 0 {
for region, ami := range amis {
ui.Say(fmt.Sprintf("Adding tags to AMI (%s)...", ami))
var ec2Tags []*ec2.Tag
for key, value := range s.Tags {
ui.Message(fmt.Sprintf("Adding tag: \"%s\": \"%s\"", key, value))
ec2Tags = append(ec2Tags, &ec2.Tag{
Key: aws.String(key),
Value: aws.String(value),
})
}
// Convert tags to ec2.Tag format
ec2Tags := ConvertToEC2Tags(s.Tags, ui)
snapshotTags := ConvertToEC2Tags(s.SnapshotTags, ui)
// Declare list of resources to tag
resourceIds := []*string{&ami}
awsConfig := aws.Config{
Credentials: ec2conn.Config.Credentials,
Region: aws.String(region),
@ -47,10 +42,10 @@ func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
ui.Error(err.Error())
return multistep.ActionHalt
}
regionconn := ec2.New(session)
// Retrieve image list for given AMI
resourceIds := []*string{&ami}
imageResp, err := regionconn.DescribeImages(&ec2.DescribeImagesInput{
ImageIds: resourceIds,
})
@ -70,17 +65,20 @@ func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
}
image := imageResp.Images[0]
snapshotIds := []*string{}
// Add only those with a Snapshot ID, i.e. not Ephemeral
for _, device := range image.BlockDeviceMappings {
if device.Ebs != nil && device.Ebs.SnapshotId != nil {
ui.Say(fmt.Sprintf("Tagging snapshot: %s", *device.Ebs.SnapshotId))
resourceIds = append(resourceIds, device.Ebs.SnapshotId)
snapshotIds = append(snapshotIds, device.Ebs.SnapshotId)
}
}
// Retry creating tags for about 2.5 minutes
err = retry.Retry(0.2, 30, 11, func() (bool, error) {
// Tag images and snapshots
_, err := regionconn.CreateTags(&ec2.CreateTagsInput{
Resources: resourceIds,
Tags: ec2Tags,
@ -94,6 +92,20 @@ func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
return false, nil
}
}
// Override tags on snapshots
_, err = regionconn.CreateTags(&ec2.CreateTagsInput{
Resources: snapshotIds,
Tags: snapshotTags,
})
if err == nil {
return true, nil
}
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "InvalidSnapshot.NotFound" {
return false, nil
}
}
return true, err
})
@ -112,3 +124,15 @@ func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
func (s *StepCreateTags) Cleanup(state multistep.StateBag) {
// No cleanup...
}
func ConvertToEC2Tags(tags map[string]string, ui packer.Ui) []*ec2.Tag {
var ec2Tags []*ec2.Tag
for key, value := range tags {
ui.Message(fmt.Sprintf("Adding tag: \"%s\": \"%s\"", key, value))
ec2Tags = append(ec2Tags, &ec2.Tag{
Key: aws.String(key),
Value: aws.String(value),
})
}
return ec2Tags
}

View File

@ -182,7 +182,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
ProductCodes: b.config.AMIProductCodes,
},
&awscommon.StepCreateTags{
Tags: b.config.AMITags,
Tags: b.config.AMITags,
SnapshotTags: b.config.SnapshotTags,
},
}

View File

@ -263,7 +263,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
ProductCodes: b.config.AMIProductCodes,
},
&awscommon.StepCreateTags{
Tags: b.config.AMITags,
Tags: b.config.AMITags,
SnapshotTags: b.config.SnapshotTags,
},
}

View File

@ -200,6 +200,9 @@ each category, the available configuration keys are alphabetized.
- `most_recent` (bool) - Selects the newest created image when true.
This is most useful for selecting a daily distro build.
- `snapshot_tags` (object of key/value strings) - Tags to apply to snapshot.
They will override AMI tags if already applied to snapshot.
- `tags` (object of key/value strings) - Tags applied to the AMI.
## Basic Example

View File

@ -203,6 +203,9 @@ builder.
- `most_recent` (bool) - Selects the newest created image when true.
This is most useful for selecting a daily distro build.
- `snapshot_tags` (object of key/value strings) - Tags to apply to snapshot.
They will override AMI tags if already applied to snapshot.
- `spot_price` (string) - The maximum hourly price to pay for a spot instance
to create the AMI. Spot instances are a type of instance that EC2 starts
when the current spot price is less than the maximum price you specify. Spot

View File

@ -219,6 +219,9 @@ builder.
- `most_recent` (bool) - Selects the newest created image when true.
This is most useful for selecting a daily distro build.
- `snapshot_tags` (object of key/value strings) - Tags to apply to snapshot.
They will override AMI tags if already applied to snapshot.
- `spot_price` (string) - The maximum hourly price to launch a spot instance
to create the AMI. It is a type of instances that EC2 starts when the
maximum price that you specify exceeds the current spot price. Spot price