github.com/mmcquillan/packer@v1.1.1-0.20171009221028-c85cf0483a5d/builder/amazon/common/step_stop_ebs_instance.go (about)

     1  package common
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/aws/aws-sdk-go/aws/awserr"
     7  	"github.com/aws/aws-sdk-go/service/ec2"
     8  	"github.com/hashicorp/packer/common"
     9  	"github.com/hashicorp/packer/packer"
    10  	"github.com/mitchellh/multistep"
    11  )
    12  
    13  type StepStopEBSBackedInstance struct {
    14  	SpotPrice           string
    15  	DisableStopInstance bool
    16  }
    17  
    18  func (s *StepStopEBSBackedInstance) Run(state multistep.StateBag) multistep.StepAction {
    19  	ec2conn := state.Get("ec2").(*ec2.EC2)
    20  	instance := state.Get("instance").(*ec2.Instance)
    21  	ui := state.Get("ui").(packer.Ui)
    22  
    23  	// Skip when it is a spot instance
    24  	if s.SpotPrice != "" && s.SpotPrice != "0" {
    25  		return multistep.ActionContinue
    26  	}
    27  
    28  	var err error
    29  
    30  	if !s.DisableStopInstance {
    31  		// Stop the instance so we can create an AMI from it
    32  		ui.Say("Stopping the source instance...")
    33  
    34  		// Amazon EC2 API follows an eventual consistency model.
    35  
    36  		// This means that if you run a command to modify or describe a resource
    37  		// that you just created, its ID might not have propagated throughout
    38  		// the system, and you will get an error responding that the resource
    39  		// does not exist.
    40  
    41  		// Work around this by retrying a few times, up to about 5 minutes.
    42  		err := common.Retry(10, 60, 6, func(i uint) (bool, error) {
    43  			ui.Message(fmt.Sprintf("Stopping instance, attempt %d", i+1))
    44  
    45  			_, err = ec2conn.StopInstances(&ec2.StopInstancesInput{
    46  				InstanceIds: []*string{instance.InstanceId},
    47  			})
    48  
    49  			if err == nil {
    50  				// success
    51  				return true, nil
    52  			}
    53  
    54  			if awsErr, ok := err.(awserr.Error); ok {
    55  				if awsErr.Code() == "InvalidInstanceID.NotFound" {
    56  					ui.Message(fmt.Sprintf(
    57  						"Error stopping instance; will retry ..."+
    58  							"Error: %s", err))
    59  					// retry
    60  					return false, nil
    61  				}
    62  			}
    63  			// errored, but not in expected way. Don't want to retry
    64  			return true, err
    65  		})
    66  
    67  		if err != nil {
    68  			err := fmt.Errorf("Error stopping instance: %s", err)
    69  			state.Put("error", err)
    70  			ui.Error(err.Error())
    71  			return multistep.ActionHalt
    72  		}
    73  
    74  	} else {
    75  		ui.Say("Automatic instance stop disabled. Please stop instance manually.")
    76  	}
    77  
    78  	// Wait for the instance to actual stop
    79  	ui.Say("Waiting for the instance to stop...")
    80  	stateChange := StateChangeConf{
    81  		Pending:   []string{"running", "pending", "stopping"},
    82  		Target:    "stopped",
    83  		Refresh:   InstanceStateRefreshFunc(ec2conn, *instance.InstanceId),
    84  		StepState: state,
    85  	}
    86  	_, err = WaitForState(&stateChange)
    87  	if err != nil {
    88  		err := fmt.Errorf("Error waiting for instance to stop: %s", err)
    89  		state.Put("error", err)
    90  		ui.Error(err.Error())
    91  		return multistep.ActionHalt
    92  	}
    93  
    94  	return multistep.ActionContinue
    95  }
    96  
    97  func (s *StepStopEBSBackedInstance) Cleanup(multistep.StateBag) {
    98  	// No cleanup...
    99  }