github.com/sneal/packer@v0.5.2/builder/amazon/common/step_ami_region_copy.go (about)

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