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