github.com/kaixiang/packer@v0.5.2-0.20140114230416-1f5786b0d7f1/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 Pending: []string{"attaching"}, 50 StepState: state, 51 Target: "attached", 52 Refresh: func() (interface{}, string, error) { 53 resp, err := ec2conn.Volumes([]string{volumeId}, ec2.NewFilter()) 54 if err != nil { 55 return nil, "", err 56 } 57 58 if len(resp.Volumes[0].Attachments) == 0 { 59 return nil, "", errors.New("No attachments on volume.") 60 } 61 62 a := resp.Volumes[0].Attachments[0] 63 return a, a.Status, nil 64 }, 65 } 66 67 _, err = awscommon.WaitForState(&stateChange) 68 if err != nil { 69 err := fmt.Errorf("Error waiting for volume: %s", err) 70 state.Put("error", err) 71 ui.Error(err.Error()) 72 return multistep.ActionHalt 73 } 74 75 state.Put("attach_cleanup", s) 76 return multistep.ActionContinue 77 } 78 79 func (s *StepAttachVolume) Cleanup(state multistep.StateBag) { 80 ui := state.Get("ui").(packer.Ui) 81 if err := s.CleanupFunc(state); err != nil { 82 ui.Error(err.Error()) 83 } 84 } 85 86 func (s *StepAttachVolume) CleanupFunc(state multistep.StateBag) error { 87 if !s.attached { 88 return nil 89 } 90 91 ec2conn := state.Get("ec2").(*ec2.EC2) 92 ui := state.Get("ui").(packer.Ui) 93 94 ui.Say("Detaching EBS volume...") 95 _, err := ec2conn.DetachVolume(s.volumeId) 96 if err != nil { 97 return fmt.Errorf("Error detaching EBS volume: %s", err) 98 } 99 100 s.attached = false 101 102 // Wait for the volume to detach 103 stateChange := awscommon.StateChangeConf{ 104 Pending: []string{"attaching", "attached", "detaching"}, 105 StepState: state, 106 Target: "detached", 107 Refresh: func() (interface{}, string, error) { 108 resp, err := ec2conn.Volumes([]string{s.volumeId}, ec2.NewFilter()) 109 if err != nil { 110 return nil, "", err 111 } 112 113 v := resp.Volumes[0] 114 if len(v.Attachments) > 0 { 115 return v, v.Attachments[0].Status, nil 116 } else { 117 return v, "detached", nil 118 } 119 }, 120 } 121 122 _, err = awscommon.WaitForState(&stateChange) 123 if err != nil { 124 return fmt.Errorf("Error waiting for volume: %s", err) 125 } 126 127 return nil 128 }