From 228d0d593ac8b12f6f1ef257be314837eb8c35ac Mon Sep 17 00:00:00 2001 From: James Massara Date: Wed, 21 Aug 2013 13:53:07 -0700 Subject: [PATCH 1/2] amazon/common: Added AMI CopyImage support --- builder/amazon/common/ami_config.go | 2 + builder/amazon/common/step_ami_region_copy.go | 79 +++++++++++++++++++ builder/amazon/ebs/builder.go | 4 + builder/amazon/instance/builder.go | 4 + 4 files changed, 89 insertions(+) create mode 100644 builder/amazon/common/step_ami_region_copy.go diff --git a/builder/amazon/common/ami_config.go b/builder/amazon/common/ami_config.go index c261cca77..524f01eb2 100644 --- a/builder/amazon/common/ami_config.go +++ b/builder/amazon/common/ami_config.go @@ -12,6 +12,7 @@ type AMIConfig struct { AMIUsers []string `mapstructure:"ami_users"` AMIGroups []string `mapstructure:"ami_groups"` AMIProductCodes []string `mapstructure:"ami_product_codes"` + AMIRegions []string `mapstructure:"ami_regions"` } func (c *AMIConfig) Prepare(t *packer.ConfigTemplate) []error { @@ -42,6 +43,7 @@ func (c *AMIConfig) Prepare(t *packer.ConfigTemplate) []error { "ami_users": c.AMIUsers, "ami_groups": c.AMIGroups, "ami_product_codes": c.AMIProductCodes, + "ami_regions": c.AMIRegions, } for n, slice := range sliceTemplates { diff --git a/builder/amazon/common/step_ami_region_copy.go b/builder/amazon/common/step_ami_region_copy.go new file mode 100644 index 000000000..652982135 --- /dev/null +++ b/builder/amazon/common/step_ami_region_copy.go @@ -0,0 +1,79 @@ +package common + +import ( + "fmt" + "github.com/mitchellh/goamz/aws" + "github.com/mitchellh/goamz/ec2" + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" +) + +type StepAMIRegionCopyAttributes struct { + Regions []string + Tags map[string]string +} + +func (s *StepAMIRegionCopyAttributes) Run(state map[string]interface{}) multistep.StepAction { + ec2conn := state["ec2"].(*ec2.EC2) + ui := state["ui"].(packer.Ui) + amis := state["amis"].(map[string]string) + ami := amis[ec2conn.Region.Name] + + if len(s.Regions) == 0 { + return multistep.ActionContinue + } + + for _, region := range s.Regions { + ui.Say(fmt.Sprintf("Copying AMI (%s) to region (%s)...", ami, region)) + + // Connect to the region where the AMI will be copied to + regionconn := ec2.New(ec2conn.Auth, aws.Regions[region]) + resp, err := regionconn.CopyImage(&ec2.CopyImage{ + SourceRegion: ec2conn.Region.Name, + SourceImageId: ami, + }) + + if err != nil { + err := fmt.Errorf("Error Copying AMI (%s) to region (%s): %s", ami, region, err) + state["error"] = err + ui.Error(err.Error()) + return multistep.ActionHalt + } + + ui.Say(fmt.Sprintf("Waiting for AMI (%s) in region (%s) to become ready...", resp.ImageId, region)) + if err := WaitForAMI(regionconn, resp.ImageId); err != nil { + err := fmt.Errorf("Error waiting for AMI (%s) in region (%s): %s", resp.ImageId, region, err) + state["error"] = err + ui.Error(err.Error()) + return multistep.ActionHalt + } + + // Need to re-apply Tags since they are not copied with the AMI + if len(s.Tags) > 0 { + ui.Say(fmt.Sprintf("Adding tags to AMI (%s)...", resp.ImageId)) + + 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, value}) + } + + _, err := regionconn.CreateTags([]string{resp.ImageId}, ec2Tags) + if err != nil { + err := fmt.Errorf("Error adding tags to AMI (%s): %s", resp.ImageId, err) + state["error"] = err + ui.Error(err.Error()) + return multistep.ActionHalt + } + } + + amis[region] = resp.ImageId + } + + state["amis"] = amis + return multistep.ActionContinue +} + +func (s *StepAMIRegionCopyAttributes) Cleanup(state map[string]interface{}) { + // No cleanup... +} diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index d358005b2..37ac4c13f 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -136,6 +136,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Users: b.config.AMIUsers, Groups: b.config.AMIGroups, }, + &awscommon.StepAMIRegionCopyAttributes{ + Regions: b.config.AMIRegions, + Tags: b.config.Tags, + }, } // Run! diff --git a/builder/amazon/instance/builder.go b/builder/amazon/instance/builder.go index ad88e6d97..8d36d2eeb 100644 --- a/builder/amazon/instance/builder.go +++ b/builder/amazon/instance/builder.go @@ -218,6 +218,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Groups: b.config.AMIGroups, ProductCodes: b.config.AMIProductCodes, }, + &awscommon.StepAMIRegionCopyAttributes{ + Regions: b.config.AMIRegions, + Tags: b.config.Tags, + }, } // Run! From c9de4c96442e57a234b64982c595de01975570ee Mon Sep 17 00:00:00 2001 From: James Massara Date: Wed, 21 Aug 2013 18:44:14 -0700 Subject: [PATCH 2/2] Make sure ami_regions are valid --- builder/amazon/common/ami_config.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/builder/amazon/common/ami_config.go b/builder/amazon/common/ami_config.go index 524f01eb2..074bd5a9c 100644 --- a/builder/amazon/common/ami_config.go +++ b/builder/amazon/common/ami_config.go @@ -2,6 +2,7 @@ package common import ( "fmt" + "github.com/mitchellh/goamz/aws" "github.com/mitchellh/packer/packer" ) @@ -61,6 +62,14 @@ func (c *AMIConfig) Prepare(t *packer.ConfigTemplate) []error { errs = append(errs, fmt.Errorf("ami_name must be specified")) } + if len(c.AMIRegions) > 0 { + for _, region := range c.AMIRegions { + if _, ok := aws.Regions[region]; !ok { + errs = append(errs, fmt.Errorf("Unknown region: %s", region)) + } + } + } + if len(errs) > 0 { return errs }