github.com/armen/terraform@v0.5.2-0.20150529052519-caa8117a08f1/builtin/providers/aws/resource_aws_network_interface.go (about)

     1  package aws
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"strconv"
     8  	"time"
     9  
    10  	"github.com/awslabs/aws-sdk-go/aws"
    11  	"github.com/awslabs/aws-sdk-go/aws/awserr"
    12  	"github.com/awslabs/aws-sdk-go/service/ec2"
    13  	"github.com/hashicorp/terraform/helper/hashcode"
    14  	"github.com/hashicorp/terraform/helper/resource"
    15  	"github.com/hashicorp/terraform/helper/schema"
    16  )
    17  
    18  func resourceAwsNetworkInterface() *schema.Resource {
    19  	return &schema.Resource{
    20  		Create: resourceAwsNetworkInterfaceCreate,
    21  		Read:   resourceAwsNetworkInterfaceRead,
    22  		Update: resourceAwsNetworkInterfaceUpdate,
    23  		Delete: resourceAwsNetworkInterfaceDelete,
    24  
    25  		Schema: map[string]*schema.Schema{
    26  
    27  			"subnet_id": &schema.Schema{
    28  				Type:     schema.TypeString,
    29  				Required: true,
    30  				ForceNew: true,
    31  			},
    32  
    33  			"private_ips": &schema.Schema{
    34  				Type:     schema.TypeSet,
    35  				Optional: true,
    36  				ForceNew: true,
    37  				Elem:     &schema.Schema{Type: schema.TypeString},
    38  				Set:      schema.HashString,
    39  			},
    40  
    41  			"security_groups": &schema.Schema{
    42  				Type:     schema.TypeSet,
    43  				Optional: true,
    44  				Computed: true,
    45  				Elem:     &schema.Schema{Type: schema.TypeString},
    46  				Set:      schema.HashString,
    47  			},
    48  
    49  			"attachment": &schema.Schema{
    50  				Type:     schema.TypeSet,
    51  				Optional: true,
    52  				Elem: &schema.Resource{
    53  					Schema: map[string]*schema.Schema{
    54  						"instance": &schema.Schema{
    55  							Type:     schema.TypeString,
    56  							Required: true,
    57  						},
    58  						"device_index": &schema.Schema{
    59  							Type:     schema.TypeInt,
    60  							Required: true,
    61  						},
    62  						"attachment_id": &schema.Schema{
    63  							Type:     schema.TypeString,
    64  							Computed: true,
    65  						},
    66  					},
    67  				},
    68  				Set: resourceAwsEniAttachmentHash,
    69  			},
    70  
    71  			"tags": tagsSchema(),
    72  		},
    73  	}
    74  }
    75  
    76  func resourceAwsNetworkInterfaceCreate(d *schema.ResourceData, meta interface{}) error {
    77  
    78  	conn := meta.(*AWSClient).ec2conn
    79  
    80  	request := &ec2.CreateNetworkInterfaceInput{
    81  		Groups:             expandStringList(d.Get("security_groups").(*schema.Set).List()),
    82  		SubnetID:           aws.String(d.Get("subnet_id").(string)),
    83  		PrivateIPAddresses: expandPrivateIPAddesses(d.Get("private_ips").(*schema.Set).List()),
    84  	}
    85  
    86  	log.Printf("[DEBUG] Creating network interface")
    87  	resp, err := conn.CreateNetworkInterface(request)
    88  	if err != nil {
    89  		return fmt.Errorf("Error creating ENI: %s", err)
    90  	}
    91  
    92  	d.SetId(*resp.NetworkInterface.NetworkInterfaceID)
    93  	log.Printf("[INFO] ENI ID: %s", d.Id())
    94  	return resourceAwsNetworkInterfaceUpdate(d, meta)
    95  }
    96  
    97  func resourceAwsNetworkInterfaceRead(d *schema.ResourceData, meta interface{}) error {
    98  
    99  	conn := meta.(*AWSClient).ec2conn
   100  	describe_network_interfaces_request := &ec2.DescribeNetworkInterfacesInput{
   101  		NetworkInterfaceIDs: []*string{aws.String(d.Id())},
   102  	}
   103  	describeResp, err := conn.DescribeNetworkInterfaces(describe_network_interfaces_request)
   104  
   105  	if err != nil {
   106  		if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidNetworkInterfaceID.NotFound" {
   107  			// The ENI is gone now, so just remove it from the state
   108  			d.SetId("")
   109  			return nil
   110  		}
   111  
   112  		return fmt.Errorf("Error retrieving ENI: %s", err)
   113  	}
   114  	if len(describeResp.NetworkInterfaces) != 1 {
   115  		return fmt.Errorf("Unable to find ENI: %#v", describeResp.NetworkInterfaces)
   116  	}
   117  
   118  	eni := describeResp.NetworkInterfaces[0]
   119  	d.Set("subnet_id", eni.SubnetID)
   120  	d.Set("private_ips", flattenNetworkInterfacesPrivateIPAddesses(eni.PrivateIPAddresses))
   121  	d.Set("security_groups", flattenGroupIdentifiers(eni.Groups))
   122  
   123  	// Tags
   124  	d.Set("tags", tagsToMap(eni.TagSet))
   125  
   126  	if eni.Attachment != nil {
   127  		attachment := []map[string]interface{}{flattenAttachment(eni.Attachment)}
   128  		d.Set("attachment", attachment)
   129  	} else {
   130  		d.Set("attachment", nil)
   131  	}
   132  
   133  	return nil
   134  }
   135  
   136  func networkInterfaceAttachmentRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
   137  	return func() (interface{}, string, error) {
   138  
   139  		describe_network_interfaces_request := &ec2.DescribeNetworkInterfacesInput{
   140  			NetworkInterfaceIDs: []*string{aws.String(id)},
   141  		}
   142  		describeResp, err := conn.DescribeNetworkInterfaces(describe_network_interfaces_request)
   143  
   144  		if err != nil {
   145  			log.Printf("[ERROR] Could not find network interface %s. %s", id, err)
   146  			return nil, "", err
   147  		}
   148  
   149  		eni := describeResp.NetworkInterfaces[0]
   150  		hasAttachment := strconv.FormatBool(eni.Attachment != nil)
   151  		log.Printf("[DEBUG] ENI %s has attachment state %s", id, hasAttachment)
   152  		return eni, hasAttachment, nil
   153  	}
   154  }
   155  
   156  func resourceAwsNetworkInterfaceDetach(oa *schema.Set, meta interface{}, eniId string) error {
   157  	// if there was an old attachment, remove it
   158  	if oa != nil && len(oa.List()) > 0 {
   159  		old_attachment := oa.List()[0].(map[string]interface{})
   160  		detach_request := &ec2.DetachNetworkInterfaceInput{
   161  			AttachmentID: aws.String(old_attachment["attachment_id"].(string)),
   162  			Force:        aws.Boolean(true),
   163  		}
   164  		conn := meta.(*AWSClient).ec2conn
   165  		_, detach_err := conn.DetachNetworkInterface(detach_request)
   166  		if detach_err != nil {
   167  			return fmt.Errorf("Error detaching ENI: %s", detach_err)
   168  		}
   169  
   170  		log.Printf("[DEBUG] Waiting for ENI (%s) to become dettached", eniId)
   171  		stateConf := &resource.StateChangeConf{
   172  			Pending: []string{"true"},
   173  			Target:  "false",
   174  			Refresh: networkInterfaceAttachmentRefreshFunc(conn, eniId),
   175  			Timeout: 10 * time.Minute,
   176  		}
   177  		if _, err := stateConf.WaitForState(); err != nil {
   178  			return fmt.Errorf(
   179  				"Error waiting for ENI (%s) to become dettached: %s", eniId, err)
   180  		}
   181  	}
   182  
   183  	return nil
   184  }
   185  
   186  func resourceAwsNetworkInterfaceUpdate(d *schema.ResourceData, meta interface{}) error {
   187  	conn := meta.(*AWSClient).ec2conn
   188  	d.Partial(true)
   189  
   190  	if d.HasChange("attachment") {
   191  		oa, na := d.GetChange("attachment")
   192  
   193  		detach_err := resourceAwsNetworkInterfaceDetach(oa.(*schema.Set), meta, d.Id())
   194  		if detach_err != nil {
   195  			return detach_err
   196  		}
   197  
   198  		// if there is a new attachment, attach it
   199  		if na != nil && len(na.(*schema.Set).List()) > 0 {
   200  			new_attachment := na.(*schema.Set).List()[0].(map[string]interface{})
   201  			di := new_attachment["device_index"].(int)
   202  			attach_request := &ec2.AttachNetworkInterfaceInput{
   203  				DeviceIndex:        aws.Long(int64(di)),
   204  				InstanceID:         aws.String(new_attachment["instance"].(string)),
   205  				NetworkInterfaceID: aws.String(d.Id()),
   206  			}
   207  			_, attach_err := conn.AttachNetworkInterface(attach_request)
   208  			if attach_err != nil {
   209  				return fmt.Errorf("Error attaching ENI: %s", attach_err)
   210  			}
   211  		}
   212  
   213  		d.SetPartial("attachment")
   214  	}
   215  
   216  	if d.HasChange("security_groups") {
   217  		request := &ec2.ModifyNetworkInterfaceAttributeInput{
   218  			NetworkInterfaceID: aws.String(d.Id()),
   219  			Groups:             expandStringList(d.Get("security_groups").(*schema.Set).List()),
   220  		}
   221  
   222  		_, err := conn.ModifyNetworkInterfaceAttribute(request)
   223  		if err != nil {
   224  			return fmt.Errorf("Failure updating ENI: %s", err)
   225  		}
   226  
   227  		d.SetPartial("security_groups")
   228  	}
   229  
   230  	if err := setTags(conn, d); err != nil {
   231  		return err
   232  	} else {
   233  		d.SetPartial("tags")
   234  	}
   235  
   236  	d.Partial(false)
   237  
   238  	return resourceAwsNetworkInterfaceRead(d, meta)
   239  }
   240  
   241  func resourceAwsNetworkInterfaceDelete(d *schema.ResourceData, meta interface{}) error {
   242  	conn := meta.(*AWSClient).ec2conn
   243  
   244  	log.Printf("[INFO] Deleting ENI: %s", d.Id())
   245  
   246  	detach_err := resourceAwsNetworkInterfaceDetach(d.Get("attachment").(*schema.Set), meta, d.Id())
   247  	if detach_err != nil {
   248  		return detach_err
   249  	}
   250  
   251  	deleteEniOpts := ec2.DeleteNetworkInterfaceInput{
   252  		NetworkInterfaceID: aws.String(d.Id()),
   253  	}
   254  	if _, err := conn.DeleteNetworkInterface(&deleteEniOpts); err != nil {
   255  		return fmt.Errorf("Error deleting ENI: %s", err)
   256  	}
   257  
   258  	return nil
   259  }
   260  
   261  func resourceAwsEniAttachmentHash(v interface{}) int {
   262  	var buf bytes.Buffer
   263  	m := v.(map[string]interface{})
   264  	buf.WriteString(fmt.Sprintf("%s-", m["instance"].(string)))
   265  	buf.WriteString(fmt.Sprintf("%d-", m["device_index"].(int)))
   266  	return hashcode.String(buf.String())
   267  }