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