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