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  }