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