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 }