Implements Snapshot tagging
While implementing my acceptance test, I stumbled upon a comment stating that snapshot deletion should also be implemented, so I snuck that in. I can't help but wonder if there is some generic logic that is implemented a few times throughout the packer code base that could maybe better serve us if it were abstracted to the common package.
This commit is contained in:
parent
2e65867cba
commit
0c7e73b1cf
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,12 +22,17 @@ 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 || len(s.SnapshotTags) > 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))
|
||||||
|
|
||||||
// Convert tags to ec2.Tag format
|
// Convert tags to ec2.Tag format
|
||||||
ec2Tags := ConvertToEC2Tags(s.Tags, ui)
|
amiTags := ConvertToEC2Tags(s.Tags, ui)
|
||||||
|
ui.Say(fmt.Sprintf("Snapshot tags:"))
|
||||||
snapshotTags := ConvertToEC2Tags(s.SnapshotTags, ui)
|
snapshotTags := ConvertToEC2Tags(s.SnapshotTags, ui)
|
||||||
|
|
||||||
// Declare list of resources to tag
|
// Declare list of resources to tag
|
||||||
|
@ -81,11 +86,8 @@ func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
// Tag images and snapshots
|
// Tag images and snapshots
|
||||||
_, err := regionconn.CreateTags(&ec2.CreateTagsInput{
|
_, err := regionconn.CreateTags(&ec2.CreateTagsInput{
|
||||||
Resources: resourceIds,
|
Resources: resourceIds,
|
||||||
Tags: ec2Tags,
|
Tags: amiTags,
|
||||||
})
|
})
|
||||||
if err == 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() == "InvalidAMIID.NotFound" ||
|
||||||
awsErr.Code() == "InvalidSnapshot.NotFound" {
|
awsErr.Code() == "InvalidSnapshot.NotFound" {
|
||||||
|
@ -116,7 +118,6 @@ func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
@ -126,13 +127,13 @@ func (s *StepCreateTags) Cleanup(state multistep.StateBag) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConvertToEC2Tags(tags map[string]string, ui packer.Ui) []*ec2.Tag {
|
func ConvertToEC2Tags(tags map[string]string, ui packer.Ui) []*ec2.Tag {
|
||||||
var ec2Tags []*ec2.Tag
|
var amiTags []*ec2.Tag
|
||||||
for key, value := range tags {
|
for key, value := range tags {
|
||||||
ui.Message(fmt.Sprintf("Adding tag: \"%s\": \"%s\"", key, value))
|
ui.Message(fmt.Sprintf("Adding tag: \"%s\": \"%s\"", key, value))
|
||||||
ec2Tags = append(ec2Tags, &ec2.Tag{
|
amiTags = append(amiTags, &ec2.Tag{
|
||||||
Key: aws.String(key),
|
Key: aws.String(key),
|
||||||
Value: aws.String(value),
|
Value: aws.String(value),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return ec2Tags
|
return amiTags
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue