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 }