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  }