github.com/rothwerx/packer@v0.9.0/builder/amazon/common/step_ami_region_copy.go (about) 1 package common 2 3 import ( 4 "fmt" 5 6 "sync" 7 8 "github.com/aws/aws-sdk-go/aws" 9 "github.com/aws/aws-sdk-go/aws/session" 10 "github.com/aws/aws-sdk-go/service/ec2" 11 12 "github.com/mitchellh/multistep" 13 "github.com/mitchellh/packer/packer" 14 ) 15 16 type StepAMIRegionCopy struct { 17 AccessConfig *AccessConfig 18 Regions []string 19 Name string 20 } 21 22 func (s *StepAMIRegionCopy) Run(state multistep.StateBag) multistep.StepAction { 23 ec2conn := state.Get("ec2").(*ec2.EC2) 24 ui := state.Get("ui").(packer.Ui) 25 amis := state.Get("amis").(map[string]string) 26 ami := amis[*ec2conn.Config.Region] 27 28 if len(s.Regions) == 0 { 29 return multistep.ActionContinue 30 } 31 32 ui.Say(fmt.Sprintf("Copying AMI (%s) to other regions...", ami)) 33 34 var lock sync.Mutex 35 var wg sync.WaitGroup 36 errs := new(packer.MultiError) 37 for _, region := range s.Regions { 38 if region == *ec2conn.Config.Region { 39 ui.Message(fmt.Sprintf( 40 "Avoiding copying AMI to duplicate region %s", region)) 41 continue 42 } 43 44 wg.Add(1) 45 ui.Message(fmt.Sprintf("Copying to: %s", region)) 46 47 go func(region string) { 48 defer wg.Done() 49 id, err := amiRegionCopy(state, s.AccessConfig, s.Name, ami, region, *ec2conn.Config.Region) 50 51 lock.Lock() 52 defer lock.Unlock() 53 amis[region] = id 54 if err != nil { 55 errs = packer.MultiErrorAppend(errs, err) 56 } 57 }(region) 58 } 59 60 // TODO(mitchellh): Wait but also allow for cancels to go through... 61 ui.Message("Waiting for all copies to complete...") 62 wg.Wait() 63 64 // If there were errors, show them 65 if len(errs.Errors) > 0 { 66 state.Put("error", errs) 67 ui.Error(errs.Error()) 68 return multistep.ActionHalt 69 } 70 71 state.Put("amis", amis) 72 return multistep.ActionContinue 73 } 74 75 func (s *StepAMIRegionCopy) Cleanup(state multistep.StateBag) { 76 // No cleanup... 77 } 78 79 // amiRegionCopy does a copy for the given AMI to the target region and 80 // returns the resulting ID or error. 81 func amiRegionCopy(state multistep.StateBag, config *AccessConfig, name string, imageId string, 82 target string, source string) (string, error) { 83 84 // Connect to the region where the AMI will be copied to 85 awsConfig, err := config.Config() 86 if err != nil { 87 return "", err 88 } 89 awsConfig.Region = aws.String(target) 90 91 sess := session.New(awsConfig) 92 regionconn := ec2.New(sess) 93 94 resp, err := regionconn.CopyImage(&ec2.CopyImageInput{ 95 SourceRegion: &source, 96 SourceImageId: &imageId, 97 Name: &name, 98 }) 99 100 if err != nil { 101 return "", fmt.Errorf("Error Copying AMI (%s) to region (%s): %s", 102 imageId, target, err) 103 } 104 105 stateChange := StateChangeConf{ 106 Pending: []string{"pending"}, 107 Target: "available", 108 Refresh: AMIStateRefreshFunc(regionconn, *resp.ImageId), 109 StepState: state, 110 } 111 112 if _, err := WaitForState(&stateChange); err != nil { 113 return "", fmt.Errorf("Error waiting for AMI (%s) in region (%s): %s", 114 *resp.ImageId, target, err) 115 } 116 117 return *resp.ImageId, nil 118 }