github.com/rothwerx/packer@v0.9.0/builder/amazon/chroot/step_attach_volume.go (about) 1 package chroot 2 3 import ( 4 "errors" 5 "fmt" 6 "strings" 7 "time" 8 9 "github.com/aws/aws-sdk-go/service/ec2" 10 "github.com/mitchellh/multistep" 11 awscommon "github.com/mitchellh/packer/builder/amazon/common" 12 "github.com/mitchellh/packer/packer" 13 ) 14 15 // StepAttachVolume attaches the previously created volume to an 16 // available device location. 17 // 18 // Produces: 19 // device string - The location where the volume was attached. 20 // attach_cleanup CleanupFunc 21 type StepAttachVolume struct { 22 attached bool 23 volumeId string 24 } 25 26 func (s *StepAttachVolume) Run(state multistep.StateBag) multistep.StepAction { 27 ec2conn := state.Get("ec2").(*ec2.EC2) 28 device := state.Get("device").(string) 29 instance := state.Get("instance").(*ec2.Instance) 30 ui := state.Get("ui").(packer.Ui) 31 volumeId := state.Get("volume_id").(string) 32 33 // For the API call, it expects "sd" prefixed devices. 34 attachVolume := strings.Replace(device, "/xvd", "/sd", 1) 35 36 ui.Say(fmt.Sprintf("Attaching the root volume to %s", attachVolume)) 37 _, err := ec2conn.AttachVolume(&ec2.AttachVolumeInput{ 38 InstanceId: instance.InstanceId, 39 VolumeId: &volumeId, 40 Device: &attachVolume, 41 }) 42 if err != nil { 43 err := fmt.Errorf("Error attaching volume: %s", err) 44 state.Put("error", err) 45 ui.Error(err.Error()) 46 return multistep.ActionHalt 47 } 48 49 // Mark that we attached it so we can detach it later 50 s.attached = true 51 s.volumeId = volumeId 52 53 // Wait for the volume to become attached 54 stateChange := awscommon.StateChangeConf{ 55 Pending: []string{"attaching"}, 56 StepState: state, 57 Target: "attached", 58 Refresh: func() (interface{}, string, error) { 59 attempts := 0 60 for attempts < 30 { 61 resp, err := ec2conn.DescribeVolumes(&ec2.DescribeVolumesInput{VolumeIds: []*string{&volumeId}}) 62 if err != nil { 63 return nil, "", err 64 } 65 if len(resp.Volumes[0].Attachments) > 0 { 66 a := resp.Volumes[0].Attachments[0] 67 return a, *a.State, nil 68 } 69 // When Attachment on volume is not present sleep for 2s and retry 70 attempts += 1 71 ui.Say(fmt.Sprintf( 72 "Volume %s show no attachments. Attempt %d/30. Sleeping for 2s and will retry.", 73 volumeId, attempts)) 74 time.Sleep(2 * time.Second) 75 } 76 77 // Attachment on volume is not present after all attempts 78 return nil, "", errors.New("No attachments on volume.") 79 }, 80 } 81 82 _, err = awscommon.WaitForState(&stateChange) 83 if err != nil { 84 err := fmt.Errorf("Error waiting for volume: %s", err) 85 state.Put("error", err) 86 ui.Error(err.Error()) 87 return multistep.ActionHalt 88 } 89 90 state.Put("attach_cleanup", s) 91 return multistep.ActionContinue 92 } 93 94 func (s *StepAttachVolume) Cleanup(state multistep.StateBag) { 95 ui := state.Get("ui").(packer.Ui) 96 if err := s.CleanupFunc(state); err != nil { 97 ui.Error(err.Error()) 98 } 99 } 100 101 func (s *StepAttachVolume) CleanupFunc(state multistep.StateBag) error { 102 if !s.attached { 103 return nil 104 } 105 106 ec2conn := state.Get("ec2").(*ec2.EC2) 107 ui := state.Get("ui").(packer.Ui) 108 109 ui.Say("Detaching EBS volume...") 110 _, err := ec2conn.DetachVolume(&ec2.DetachVolumeInput{VolumeId: &s.volumeId}) 111 if err != nil { 112 return fmt.Errorf("Error detaching EBS volume: %s", err) 113 } 114 115 s.attached = false 116 117 // Wait for the volume to detach 118 stateChange := awscommon.StateChangeConf{ 119 Pending: []string{"attaching", "attached", "detaching"}, 120 StepState: state, 121 Target: "detached", 122 Refresh: func() (interface{}, string, error) { 123 resp, err := ec2conn.DescribeVolumes(&ec2.DescribeVolumesInput{VolumeIds: []*string{&s.volumeId}}) 124 if err != nil { 125 return nil, "", err 126 } 127 128 v := resp.Volumes[0] 129 if len(v.Attachments) > 0 { 130 return v, *v.Attachments[0].State, nil 131 } else { 132 return v, "detached", nil 133 } 134 }, 135 } 136 137 _, err = awscommon.WaitForState(&stateChange) 138 if err != nil { 139 return fmt.Errorf("Error waiting for volume: %s", err) 140 } 141 142 return nil 143 }