github.com/recobe182/terraform@v0.8.5-0.20170117231232-49ab22a935b7/builtin/providers/aws/resource_aws_volume_attachment.go (about)

     1  package aws
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"time"
     8  
     9  	"github.com/aws/aws-sdk-go/aws"
    10  	"github.com/aws/aws-sdk-go/aws/awserr"
    11  	"github.com/aws/aws-sdk-go/service/ec2"
    12  	"github.com/hashicorp/terraform/helper/hashcode"
    13  	"github.com/hashicorp/terraform/helper/resource"
    14  	"github.com/hashicorp/terraform/helper/schema"
    15  )
    16  
    17  func resourceAwsVolumeAttachment() *schema.Resource {
    18  	return &schema.Resource{
    19  		Create: resourceAwsVolumeAttachmentCreate,
    20  		Read:   resourceAwsVolumeAttachmentRead,
    21  		Delete: resourceAwsVolumeAttachmentDelete,
    22  
    23  		Schema: map[string]*schema.Schema{
    24  			"device_name": {
    25  				Type:     schema.TypeString,
    26  				Required: true,
    27  				ForceNew: true,
    28  			},
    29  
    30  			"instance_id": {
    31  				Type:     schema.TypeString,
    32  				Required: true,
    33  				ForceNew: true,
    34  			},
    35  
    36  			"volume_id": {
    37  				Type:     schema.TypeString,
    38  				Required: true,
    39  				ForceNew: true,
    40  			},
    41  
    42  			"force_detach": {
    43  				Type:     schema.TypeBool,
    44  				Optional: true,
    45  				Computed: true,
    46  			},
    47  			"skip_destroy": {
    48  				Type:     schema.TypeBool,
    49  				Optional: true,
    50  				Computed: true,
    51  			},
    52  		},
    53  	}
    54  }
    55  
    56  func resourceAwsVolumeAttachmentCreate(d *schema.ResourceData, meta interface{}) error {
    57  	conn := meta.(*AWSClient).ec2conn
    58  	name := d.Get("device_name").(string)
    59  	iID := d.Get("instance_id").(string)
    60  	vID := d.Get("volume_id").(string)
    61  
    62  	opts := &ec2.AttachVolumeInput{
    63  		Device:     aws.String(name),
    64  		InstanceId: aws.String(iID),
    65  		VolumeId:   aws.String(vID),
    66  	}
    67  
    68  	log.Printf("[DEBUG] Attaching Volume (%s) to Instance (%s)", vID, iID)
    69  	_, err := conn.AttachVolume(opts)
    70  	if err != nil {
    71  		if awsErr, ok := err.(awserr.Error); ok {
    72  			return fmt.Errorf("[WARN] Error attaching volume (%s) to instance (%s), message: \"%s\", code: \"%s\"",
    73  				vID, iID, awsErr.Message(), awsErr.Code())
    74  		}
    75  		return err
    76  	}
    77  
    78  	stateConf := &resource.StateChangeConf{
    79  		Pending:    []string{"attaching"},
    80  		Target:     []string{"attached"},
    81  		Refresh:    volumeAttachmentStateRefreshFunc(conn, vID, iID),
    82  		Timeout:    5 * time.Minute,
    83  		Delay:      10 * time.Second,
    84  		MinTimeout: 3 * time.Second,
    85  	}
    86  
    87  	_, err = stateConf.WaitForState()
    88  	if err != nil {
    89  		return fmt.Errorf(
    90  			"Error waiting for Volume (%s) to attach to Instance: %s, error: %s",
    91  			vID, iID, err)
    92  	}
    93  
    94  	d.SetId(volumeAttachmentID(name, vID, iID))
    95  	return resourceAwsVolumeAttachmentRead(d, meta)
    96  }
    97  
    98  func volumeAttachmentStateRefreshFunc(conn *ec2.EC2, volumeID, instanceID string) resource.StateRefreshFunc {
    99  	return func() (interface{}, string, error) {
   100  
   101  		request := &ec2.DescribeVolumesInput{
   102  			VolumeIds: []*string{aws.String(volumeID)},
   103  			Filters: []*ec2.Filter{
   104  				&ec2.Filter{
   105  					Name:   aws.String("attachment.instance-id"),
   106  					Values: []*string{aws.String(instanceID)},
   107  				},
   108  			},
   109  		}
   110  
   111  		resp, err := conn.DescribeVolumes(request)
   112  		if err != nil {
   113  			if awsErr, ok := err.(awserr.Error); ok {
   114  				return nil, "failed", fmt.Errorf("code: %s, message: %s", awsErr.Code(), awsErr.Message())
   115  			}
   116  			return nil, "failed", err
   117  		}
   118  
   119  		if len(resp.Volumes) > 0 {
   120  			v := resp.Volumes[0]
   121  			for _, a := range v.Attachments {
   122  				if a.InstanceId != nil && *a.InstanceId == instanceID {
   123  					return a, *a.State, nil
   124  				}
   125  			}
   126  		}
   127  		// assume detached if volume count is 0
   128  		return 42, "detached", nil
   129  	}
   130  }
   131  func resourceAwsVolumeAttachmentRead(d *schema.ResourceData, meta interface{}) error {
   132  	conn := meta.(*AWSClient).ec2conn
   133  
   134  	request := &ec2.DescribeVolumesInput{
   135  		VolumeIds: []*string{aws.String(d.Get("volume_id").(string))},
   136  		Filters: []*ec2.Filter{
   137  			&ec2.Filter{
   138  				Name:   aws.String("attachment.instance-id"),
   139  				Values: []*string{aws.String(d.Get("instance_id").(string))},
   140  			},
   141  		},
   142  	}
   143  
   144  	vols, err := conn.DescribeVolumes(request)
   145  	if err != nil {
   146  		if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVolume.NotFound" {
   147  			d.SetId("")
   148  			return nil
   149  		}
   150  		return fmt.Errorf("Error reading EC2 volume %s for instance: %s: %#v", d.Get("volume_id").(string), d.Get("instance_id").(string), err)
   151  	}
   152  
   153  	if len(vols.Volumes) == 0 || *vols.Volumes[0].State == "available" {
   154  		log.Printf("[DEBUG] Volume Attachment (%s) not found, removing from state", d.Id())
   155  		d.SetId("")
   156  	}
   157  
   158  	return nil
   159  }
   160  
   161  func resourceAwsVolumeAttachmentDelete(d *schema.ResourceData, meta interface{}) error {
   162  	conn := meta.(*AWSClient).ec2conn
   163  
   164  	if _, ok := d.GetOk("skip_destroy"); ok {
   165  		log.Printf("[INFO] Found skip_destroy to be true, removing attachment %q from state", d.Id())
   166  		d.SetId("")
   167  		return nil
   168  	}
   169  
   170  	vID := d.Get("volume_id").(string)
   171  	iID := d.Get("instance_id").(string)
   172  
   173  	opts := &ec2.DetachVolumeInput{
   174  		Device:     aws.String(d.Get("device_name").(string)),
   175  		InstanceId: aws.String(iID),
   176  		VolumeId:   aws.String(vID),
   177  		Force:      aws.Bool(d.Get("force_detach").(bool)),
   178  	}
   179  
   180  	_, err := conn.DetachVolume(opts)
   181  	if err != nil {
   182  		return fmt.Errorf("Failed to detach Volume (%s) from Instance (%s): %s",
   183  			vID, iID, err)
   184  	}
   185  	stateConf := &resource.StateChangeConf{
   186  		Pending:    []string{"detaching"},
   187  		Target:     []string{"detached"},
   188  		Refresh:    volumeAttachmentStateRefreshFunc(conn, vID, iID),
   189  		Timeout:    5 * time.Minute,
   190  		Delay:      10 * time.Second,
   191  		MinTimeout: 3 * time.Second,
   192  	}
   193  
   194  	log.Printf("[DEBUG] Detaching Volume (%s) from Instance (%s)", vID, iID)
   195  	_, err = stateConf.WaitForState()
   196  	if err != nil {
   197  		return fmt.Errorf(
   198  			"Error waiting for Volume (%s) to detach from Instance: %s",
   199  			vID, iID)
   200  	}
   201  	d.SetId("")
   202  	return nil
   203  }
   204  
   205  func volumeAttachmentID(name, volumeID, instanceID string) string {
   206  	var buf bytes.Buffer
   207  	buf.WriteString(fmt.Sprintf("%s-", name))
   208  	buf.WriteString(fmt.Sprintf("%s-", instanceID))
   209  	buf.WriteString(fmt.Sprintf("%s-", volumeID))
   210  
   211  	return fmt.Sprintf("vai-%d", hashcode.String(buf.String()))
   212  }