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  }