Merge pull request #4015 from artburkart:closes_3908
Closes #3908: Adds snapshot tag overrides
This commit is contained in:
commit
a09f20f996
@ -263,6 +263,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||||||
},
|
},
|
||||||
&awscommon.StepCreateTags{
|
&awscommon.StepCreateTags{
|
||||||
Tags: b.config.AMITags,
|
Tags: b.config.AMITags,
|
||||||
|
SnapshotTags: b.config.SnapshotTags,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ type AMIConfig struct {
|
|||||||
AMIEnhancedNetworking bool `mapstructure:"enhanced_networking"`
|
AMIEnhancedNetworking bool `mapstructure:"enhanced_networking"`
|
||||||
AMIForceDeregister bool `mapstructure:"force_deregister"`
|
AMIForceDeregister bool `mapstructure:"force_deregister"`
|
||||||
AMIEncryptBootVolume bool `mapstructure:"encrypt_boot"`
|
AMIEncryptBootVolume bool `mapstructure:"encrypt_boot"`
|
||||||
|
SnapshotTags map[string]string `mapstructure:"snapshot_tags"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AMIConfig) Prepare(ctx *interpolate.Context) []error {
|
func (c *AMIConfig) Prepare(ctx *interpolate.Context) []error {
|
||||||
|
@ -79,6 +79,19 @@ func (a *Artifact) Destroy() error {
|
|||||||
}
|
}
|
||||||
regionConn := ec2.New(session)
|
regionConn := ec2.New(session)
|
||||||
|
|
||||||
|
// Get image metadata
|
||||||
|
imageResp, err := regionConn.DescribeImages(&ec2.DescribeImagesInput{
|
||||||
|
ImageIds: []*string{&imageId},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
if len(imageResp.Images) == 0 {
|
||||||
|
err := fmt.Errorf("Error retrieving details for AMI (%s), no images found", imageId)
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deregister ami
|
||||||
input := &ec2.DeregisterImageInput{
|
input := &ec2.DeregisterImageInput{
|
||||||
ImageId: &imageId,
|
ImageId: &imageId,
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
|
|
||||||
type StepCreateTags struct {
|
type StepCreateTags struct {
|
||||||
Tags map[string]string
|
Tags map[string]string
|
||||||
|
SnapshotTags map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
|
func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
@ -21,21 +22,20 @@ func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
|
|||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
amis := state.Get("amis").(map[string]string)
|
amis := state.Get("amis").(map[string]string)
|
||||||
|
|
||||||
if len(s.Tags) > 0 {
|
if len(s.Tags) == 0 && len(s.SnapshotTags) == 0 {
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds tags to AMIs and snapshots
|
||||||
for region, ami := range amis {
|
for region, ami := range amis {
|
||||||
ui.Say(fmt.Sprintf("Adding tags to AMI (%s)...", ami))
|
ui.Say(fmt.Sprintf("Adding tags to AMI (%s)...", ami))
|
||||||
|
|
||||||
var ec2Tags []*ec2.Tag
|
// Convert tags to ec2.Tag format
|
||||||
for key, value := range s.Tags {
|
amiTags := ConvertToEC2Tags(s.Tags, ui)
|
||||||
ui.Message(fmt.Sprintf("Adding tag: \"%s\": \"%s\"", key, value))
|
ui.Say(fmt.Sprintf("Snapshot tags:"))
|
||||||
ec2Tags = append(ec2Tags, &ec2.Tag{
|
snapshotTags := ConvertToEC2Tags(s.SnapshotTags, ui)
|
||||||
Key: aws.String(key),
|
|
||||||
Value: aws.String(value),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Declare list of resources to tag
|
// Declare list of resources to tag
|
||||||
resourceIds := []*string{&ami}
|
|
||||||
awsConfig := aws.Config{
|
awsConfig := aws.Config{
|
||||||
Credentials: ec2conn.Config.Credentials,
|
Credentials: ec2conn.Config.Credentials,
|
||||||
Region: aws.String(region),
|
Region: aws.String(region),
|
||||||
@ -47,10 +47,10 @@ func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
|
|||||||
ui.Error(err.Error())
|
ui.Error(err.Error())
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
|
|
||||||
regionconn := ec2.New(session)
|
regionconn := ec2.New(session)
|
||||||
|
|
||||||
// Retrieve image list for given AMI
|
// Retrieve image list for given AMI
|
||||||
|
resourceIds := []*string{&ami}
|
||||||
imageResp, err := regionconn.DescribeImages(&ec2.DescribeImagesInput{
|
imageResp, err := regionconn.DescribeImages(&ec2.DescribeImagesInput{
|
||||||
ImageIds: resourceIds,
|
ImageIds: resourceIds,
|
||||||
})
|
})
|
||||||
@ -70,27 +70,41 @@ func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
image := imageResp.Images[0]
|
image := imageResp.Images[0]
|
||||||
|
snapshotIds := []*string{}
|
||||||
|
|
||||||
// Add only those with a Snapshot ID, i.e. not Ephemeral
|
// Add only those with a Snapshot ID, i.e. not Ephemeral
|
||||||
for _, device := range image.BlockDeviceMappings {
|
for _, device := range image.BlockDeviceMappings {
|
||||||
if device.Ebs != nil && device.Ebs.SnapshotId != nil {
|
if device.Ebs != nil && device.Ebs.SnapshotId != nil {
|
||||||
ui.Say(fmt.Sprintf("Tagging snapshot: %s", *device.Ebs.SnapshotId))
|
ui.Say(fmt.Sprintf("Tagging snapshot: %s", *device.Ebs.SnapshotId))
|
||||||
resourceIds = append(resourceIds, device.Ebs.SnapshotId)
|
resourceIds = append(resourceIds, device.Ebs.SnapshotId)
|
||||||
|
snapshotIds = append(snapshotIds, device.Ebs.SnapshotId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retry creating tags for about 2.5 minutes
|
// Retry creating tags for about 2.5 minutes
|
||||||
err = retry.Retry(0.2, 30, 11, func() (bool, error) {
|
err = retry.Retry(0.2, 30, 11, func() (bool, error) {
|
||||||
|
// Tag images and snapshots
|
||||||
_, err := regionconn.CreateTags(&ec2.CreateTagsInput{
|
_, err := regionconn.CreateTags(&ec2.CreateTagsInput{
|
||||||
Resources: resourceIds,
|
Resources: resourceIds,
|
||||||
Tags: ec2Tags,
|
Tags: amiTags,
|
||||||
|
})
|
||||||
|
if awsErr, ok := err.(awserr.Error); ok {
|
||||||
|
if awsErr.Code() == "InvalidAMIID.NotFound" ||
|
||||||
|
awsErr.Code() == "InvalidSnapshot.NotFound" {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override tags on snapshots
|
||||||
|
_, err = regionconn.CreateTags(&ec2.CreateTagsInput{
|
||||||
|
Resources: snapshotIds,
|
||||||
|
Tags: snapshotTags,
|
||||||
})
|
})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
if awsErr, ok := err.(awserr.Error); ok {
|
if awsErr, ok := err.(awserr.Error); ok {
|
||||||
if awsErr.Code() == "InvalidAMIID.NotFound" ||
|
if awsErr.Code() == "InvalidSnapshot.NotFound" {
|
||||||
awsErr.Code() == "InvalidSnapshot.NotFound" {
|
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,7 +118,6 @@ func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
|
|||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
@ -112,3 +125,15 @@ func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
|
|||||||
func (s *StepCreateTags) Cleanup(state multistep.StateBag) {
|
func (s *StepCreateTags) Cleanup(state multistep.StateBag) {
|
||||||
// No cleanup...
|
// No cleanup...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ConvertToEC2Tags(tags map[string]string, ui packer.Ui) []*ec2.Tag {
|
||||||
|
var amiTags []*ec2.Tag
|
||||||
|
for key, value := range tags {
|
||||||
|
ui.Message(fmt.Sprintf("Adding tag: \"%s\": \"%s\"", key, value))
|
||||||
|
amiTags = append(amiTags, &ec2.Tag{
|
||||||
|
Key: aws.String(key),
|
||||||
|
Value: aws.String(value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return amiTags
|
||||||
|
}
|
||||||
|
@ -183,6 +183,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||||||
},
|
},
|
||||||
&awscommon.StepCreateTags{
|
&awscommon.StepCreateTags{
|
||||||
Tags: b.config.AMITags,
|
Tags: b.config.AMITags,
|
||||||
|
SnapshotTags: b.config.SnapshotTags,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package ebs
|
package ebs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -11,6 +12,21 @@ import (
|
|||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type TFBuilder struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Region string `json:"region"`
|
||||||
|
SourceAmi string `json:"source_ami"`
|
||||||
|
InstanceType string `json:"instance_type"`
|
||||||
|
SshUsername string `json:"ssh_username"`
|
||||||
|
AmiName string `json:"ami_name"`
|
||||||
|
Tags map[string]string `json:"tags"`
|
||||||
|
SnapshotTags map[string]string `json:"snapshot_tags"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TFConfig struct {
|
||||||
|
Builders []TFBuilder `json:"builders"`
|
||||||
|
}
|
||||||
|
|
||||||
func TestBuilderTagsAcc_basic(t *testing.T) {
|
func TestBuilderTagsAcc_basic(t *testing.T) {
|
||||||
builderT.Test(t, builderT.TestCase{
|
builderT.Test(t, builderT.TestCase{
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
@ -26,9 +42,10 @@ func checkTags() builderT.TestCheckFunc {
|
|||||||
return fmt.Errorf("more than 1 artifact")
|
return fmt.Errorf("more than 1 artifact")
|
||||||
}
|
}
|
||||||
|
|
||||||
tags := make(map[string]string)
|
config := TFConfig{}
|
||||||
tags["OS_Version"] = "Ubuntu"
|
json.Unmarshal([]byte(testBuilderTagsAccBasic), &config)
|
||||||
tags["Release"] = "Latest"
|
tags := config.Builders[0].Tags
|
||||||
|
snapshotTags := config.Builders[0].SnapshotTags
|
||||||
|
|
||||||
// Get the actual *Artifact pointer so we can access the AMIs directly
|
// Get the actual *Artifact pointer so we can access the AMIs directly
|
||||||
artifactRaw := artifacts[0]
|
artifactRaw := artifacts[0]
|
||||||
@ -37,18 +54,18 @@ func checkTags() builderT.TestCheckFunc {
|
|||||||
return fmt.Errorf("unknown artifact: %#v", artifactRaw)
|
return fmt.Errorf("unknown artifact: %#v", artifactRaw)
|
||||||
}
|
}
|
||||||
|
|
||||||
// describe the image, get block devices with a snapshot
|
// Describe the image, get block devices with a snapshot
|
||||||
ec2conn, _ := testEC2Conn()
|
ec2conn, _ := testEC2Conn()
|
||||||
imageResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{
|
imageResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{
|
||||||
ImageIds: []*string{aws.String(artifact.Amis["us-east-1"])},
|
ImageIds: []*string{aws.String(artifact.Amis["us-east-1"])},
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error retrieving details for AMI Artifcat (%#v) in Tags Test: %s", artifact, err)
|
return fmt.Errorf("Error retrieving details for AMI Artifact (%#v) in Tags Test: %s", artifact, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(imageResp.Images) == 0 {
|
if len(imageResp.Images) == 0 {
|
||||||
return fmt.Errorf("No images found for AMI Artifcat (%#v) in Tags Test: %s", artifact, err)
|
return fmt.Errorf("No images found for AMI Artifact (%#v) in Tags Test: %s", artifact, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
image := imageResp.Images[0]
|
image := imageResp.Images[0]
|
||||||
@ -61,7 +78,7 @@ func checkTags() builderT.TestCheckFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// grab matching snapshot info
|
// Grab matching snapshot info
|
||||||
resp, err := ec2conn.DescribeSnapshots(&ec2.DescribeSnapshotsInput{
|
resp, err := ec2conn.DescribeSnapshots(&ec2.DescribeSnapshotsInput{
|
||||||
SnapshotIds: snapshots,
|
SnapshotIds: snapshots,
|
||||||
})
|
})
|
||||||
@ -74,12 +91,14 @@ func checkTags() builderT.TestCheckFunc {
|
|||||||
return fmt.Errorf("No Snapshots found for AMI Artifcat (%#v) in Tags Test", artifact)
|
return fmt.Errorf("No Snapshots found for AMI Artifcat (%#v) in Tags Test", artifact)
|
||||||
}
|
}
|
||||||
|
|
||||||
// grab the snapshots, check the tags
|
// Grab the snapshots, check the tags
|
||||||
for _, s := range resp.Snapshots {
|
for _, s := range resp.Snapshots {
|
||||||
expected := len(tags)
|
expected := len(tags)
|
||||||
for _, t := range s.Tags {
|
for _, t := range s.Tags {
|
||||||
for key, value := range tags {
|
for key, value := range tags {
|
||||||
if key == *t.Key && value == *t.Value {
|
if val, ok := snapshotTags[key]; ok && val == *t.Value {
|
||||||
|
expected--
|
||||||
|
} else if key == *t.Key && value == *t.Value {
|
||||||
expected--
|
expected--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -106,7 +125,11 @@ const testBuilderTagsAccBasic = `
|
|||||||
"ami_name": "packer-tags-testing-{{timestamp}}",
|
"ami_name": "packer-tags-testing-{{timestamp}}",
|
||||||
"tags": {
|
"tags": {
|
||||||
"OS_Version": "Ubuntu",
|
"OS_Version": "Ubuntu",
|
||||||
"Release": "Latest"
|
"Release": "Latest",
|
||||||
|
"Name": "Bleep"
|
||||||
|
},
|
||||||
|
"snapshot_tags": {
|
||||||
|
"Name": "Foobar"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -264,6 +264,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||||||
},
|
},
|
||||||
&awscommon.StepCreateTags{
|
&awscommon.StepCreateTags{
|
||||||
Tags: b.config.AMITags,
|
Tags: b.config.AMITags,
|
||||||
|
SnapshotTags: b.config.SnapshotTags,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,6 +200,9 @@ each category, the available configuration keys are alphabetized.
|
|||||||
- `most_recent` (bool) - Selects the newest created image when true.
|
- `most_recent` (bool) - Selects the newest created image when true.
|
||||||
This is most useful for selecting a daily distro build.
|
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.
|
- `tags` (object of key/value strings) - Tags applied to the AMI.
|
||||||
|
|
||||||
## Basic Example
|
## Basic Example
|
||||||
|
@ -204,6 +204,9 @@ builder.
|
|||||||
- `most_recent` (bool) - Selects the newest created image when true.
|
- `most_recent` (bool) - Selects the newest created image when true.
|
||||||
This is most useful for selecting a daily distro build.
|
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
|
- `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
|
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
|
when the current spot price is less than the maximum price you specify. Spot
|
||||||
|
@ -219,6 +219,9 @@ builder.
|
|||||||
- `most_recent` (bool) - Selects the newest created image when true.
|
- `most_recent` (bool) - Selects the newest created image when true.
|
||||||
This is most useful for selecting a daily distro build.
|
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
|
- `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
|
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
|
maximum price that you specify exceeds the current spot price. Spot price
|
||||||
|
Loading…
x
Reference in New Issue
Block a user