Merge pull request #1149 from peterbeams/master
Support AWS Elastic Network Interfaces (ENI)
This commit is contained in:
commit
df514c9003
|
@ -58,6 +58,7 @@ func Provider() terraform.ResourceProvider {
|
||||||
"aws_launch_configuration": resourceAwsLaunchConfiguration(),
|
"aws_launch_configuration": resourceAwsLaunchConfiguration(),
|
||||||
"aws_main_route_table_association": resourceAwsMainRouteTableAssociation(),
|
"aws_main_route_table_association": resourceAwsMainRouteTableAssociation(),
|
||||||
"aws_network_acl": resourceAwsNetworkAcl(),
|
"aws_network_acl": resourceAwsNetworkAcl(),
|
||||||
|
"aws_network_interface": resourceAwsNetworkInterface(),
|
||||||
"aws_route53_record": resourceAwsRoute53Record(),
|
"aws_route53_record": resourceAwsRoute53Record(),
|
||||||
"aws_route53_zone": resourceAwsRoute53Zone(),
|
"aws_route53_zone": resourceAwsRoute53Zone(),
|
||||||
"aws_route_table": resourceAwsRouteTable(),
|
"aws_route_table": resourceAwsRouteTable(),
|
||||||
|
|
|
@ -0,0 +1,263 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/aws-sdk-go/aws"
|
||||||
|
"github.com/hashicorp/aws-sdk-go/gen/ec2"
|
||||||
|
"github.com/hashicorp/terraform/helper/hashcode"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsNetworkInterface() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsNetworkInterfaceCreate,
|
||||||
|
Read: resourceAwsNetworkInterfaceRead,
|
||||||
|
Update: resourceAwsNetworkInterfaceUpdate,
|
||||||
|
Delete: resourceAwsNetworkInterfaceDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
|
||||||
|
"subnet_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"private_ips": &schema.Schema{
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
Set: func(v interface{}) int {
|
||||||
|
return hashcode.String(v.(string))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"security_groups": &schema.Schema{
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
Set: func(v interface{}) int {
|
||||||
|
return hashcode.String(v.(string))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"attachment": &schema.Schema{
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Optional: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"instance": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"device_index": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"attachment_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Set: resourceAwsEniAttachmentHash,
|
||||||
|
},
|
||||||
|
|
||||||
|
"tags": tagsSchema(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsNetworkInterfaceCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
|
||||||
|
ec2conn := meta.(*AWSClient).ec2conn
|
||||||
|
|
||||||
|
request := &ec2.CreateNetworkInterfaceRequest{
|
||||||
|
Groups: expandStringList(d.Get("security_groups").(*schema.Set).List()),
|
||||||
|
SubnetID: aws.String(d.Get("subnet_id").(string)),
|
||||||
|
PrivateIPAddresses: expandPrivateIPAddesses(d.Get("private_ips").(*schema.Set).List()),
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Creating network interface")
|
||||||
|
resp, err := ec2conn.CreateNetworkInterface(request)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating ENI: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId(*resp.NetworkInterface.NetworkInterfaceID)
|
||||||
|
log.Printf("[INFO] ENI ID: %s", d.Id())
|
||||||
|
|
||||||
|
return resourceAwsNetworkInterfaceUpdate(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsNetworkInterfaceRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
|
||||||
|
ec2conn := meta.(*AWSClient).ec2conn
|
||||||
|
describe_network_interfaces_request := &ec2.DescribeNetworkInterfacesRequest{
|
||||||
|
NetworkInterfaceIDs: []string{d.Id()},
|
||||||
|
}
|
||||||
|
describeResp, err := ec2conn.DescribeNetworkInterfaces(describe_network_interfaces_request)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "InvalidNetworkInterfaceID.NotFound" {
|
||||||
|
// The ENI is gone now, so just remove it from the state
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Error retrieving ENI: %s", err)
|
||||||
|
}
|
||||||
|
if len(describeResp.NetworkInterfaces) != 1 {
|
||||||
|
return fmt.Errorf("Unable to find ENI: %#v", describeResp.NetworkInterfaces)
|
||||||
|
}
|
||||||
|
|
||||||
|
eni := describeResp.NetworkInterfaces[0]
|
||||||
|
d.Set("subnet_id", eni.SubnetID)
|
||||||
|
d.Set("private_ips", flattenNetworkInterfacesPrivateIPAddesses(eni.PrivateIPAddresses))
|
||||||
|
d.Set("security_groups", flattenGroupIdentifiers(eni.Groups))
|
||||||
|
|
||||||
|
if eni.Attachment != nil {
|
||||||
|
attachment := []map[string]interface{} { flattenAttachment(eni.Attachment) }
|
||||||
|
d.Set("attachment", attachment)
|
||||||
|
} else {
|
||||||
|
d.Set("attachment", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func networkInterfaceAttachmentRefreshFunc(ec2conn *ec2.EC2, id string) resource.StateRefreshFunc {
|
||||||
|
return func() (interface{}, string, error) {
|
||||||
|
|
||||||
|
describe_network_interfaces_request := &ec2.DescribeNetworkInterfacesRequest{
|
||||||
|
NetworkInterfaceIDs: []string{id},
|
||||||
|
}
|
||||||
|
describeResp, err := ec2conn.DescribeNetworkInterfaces(describe_network_interfaces_request)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] Could not find network interface %s. %s", id, err)
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
eni := describeResp.NetworkInterfaces[0]
|
||||||
|
hasAttachment := strconv.FormatBool(eni.Attachment != nil)
|
||||||
|
log.Printf("[DEBUG] ENI %s has attachment state %s", id, hasAttachment)
|
||||||
|
return eni, hasAttachment, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsNetworkInterfaceDetach(oa *schema.Set, meta interface{}, eniId string) error {
|
||||||
|
// if there was an old attachment, remove it
|
||||||
|
if oa != nil && len(oa.List()) > 0 {
|
||||||
|
old_attachment := oa.List()[0].(map[string]interface{})
|
||||||
|
detach_request := &ec2.DetachNetworkInterfaceRequest{
|
||||||
|
AttachmentID: aws.String(old_attachment["attachment_id"].(string)),
|
||||||
|
Force: aws.Boolean(true),
|
||||||
|
}
|
||||||
|
ec2conn := meta.(*AWSClient).ec2conn
|
||||||
|
detach_err := ec2conn.DetachNetworkInterface(detach_request)
|
||||||
|
if detach_err != nil {
|
||||||
|
return fmt.Errorf("Error detaching ENI: %s", detach_err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Waiting for ENI (%s) to become dettached", eniId)
|
||||||
|
stateConf := &resource.StateChangeConf{
|
||||||
|
Pending: []string{"true"},
|
||||||
|
Target: "false",
|
||||||
|
Refresh: networkInterfaceAttachmentRefreshFunc(ec2conn, eniId),
|
||||||
|
Timeout: 10 * time.Minute,
|
||||||
|
}
|
||||||
|
if _, err := stateConf.WaitForState(); err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"Error waiting for ENI (%s) to become dettached: %s", eniId, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsNetworkInterfaceUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
|
||||||
|
d.Partial(true)
|
||||||
|
|
||||||
|
if d.HasChange("attachment") {
|
||||||
|
ec2conn := meta.(*AWSClient).ec2conn
|
||||||
|
oa, na := d.GetChange("attachment")
|
||||||
|
|
||||||
|
detach_err := resourceAwsNetworkInterfaceDetach(oa.(*schema.Set), meta, d.Id())
|
||||||
|
if detach_err != nil {
|
||||||
|
return detach_err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there is a new attachment, attach it
|
||||||
|
if na != nil && len(na.(*schema.Set).List()) > 0 {
|
||||||
|
new_attachment := na.(*schema.Set).List()[0].(map[string]interface{})
|
||||||
|
attach_request := &ec2.AttachNetworkInterfaceRequest{
|
||||||
|
DeviceIndex: aws.Integer(new_attachment["device_index"].(int)),
|
||||||
|
InstanceID: aws.String(new_attachment["instance"].(string)),
|
||||||
|
NetworkInterfaceID: aws.String(d.Id()),
|
||||||
|
}
|
||||||
|
_, attach_err := ec2conn.AttachNetworkInterface(attach_request)
|
||||||
|
if attach_err != nil {
|
||||||
|
return fmt.Errorf("Error attaching ENI: %s", attach_err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetPartial("attachment")
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.HasChange("security_groups") {
|
||||||
|
request := &ec2.ModifyNetworkInterfaceAttributeRequest{
|
||||||
|
NetworkInterfaceID: aws.String(d.Id()),
|
||||||
|
Groups: expandStringList(d.Get("security_groups").(*schema.Set).List()),
|
||||||
|
}
|
||||||
|
|
||||||
|
ec2conn := meta.(*AWSClient).ec2conn
|
||||||
|
err := ec2conn.ModifyNetworkInterfaceAttribute(request)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failure updating ENI: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetPartial("security_groups")
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Partial(false)
|
||||||
|
|
||||||
|
return resourceAwsNetworkInterfaceRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsNetworkInterfaceDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
ec2conn := meta.(*AWSClient).ec2conn
|
||||||
|
|
||||||
|
log.Printf("[INFO] Deleting ENI: %s", d.Id())
|
||||||
|
|
||||||
|
detach_err := resourceAwsNetworkInterfaceDetach(d.Get("attachment").(*schema.Set), meta, d.Id())
|
||||||
|
if detach_err != nil {
|
||||||
|
return detach_err
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteEniOpts := ec2.DeleteNetworkInterfaceRequest{
|
||||||
|
NetworkInterfaceID: aws.String(d.Id()),
|
||||||
|
}
|
||||||
|
if err := ec2conn.DeleteNetworkInterface(&deleteEniOpts); err != nil {
|
||||||
|
return fmt.Errorf("Error deleting ENI: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsEniAttachmentHash(v interface{}) int {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
m := v.(map[string]interface{})
|
||||||
|
buf.WriteString(fmt.Sprintf("%s-", m["instance"].(string)))
|
||||||
|
buf.WriteString(fmt.Sprintf("%d-", m["device_index"].(int)))
|
||||||
|
return hashcode.String(buf.String())
|
||||||
|
}
|
|
@ -0,0 +1,235 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/aws-sdk-go/aws"
|
||||||
|
"github.com/hashicorp/aws-sdk-go/gen/ec2"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccAWSENI_basic(t *testing.T) {
|
||||||
|
var conf ec2.NetworkInterface
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAWSENIDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSENIConfig,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckAWSENIExists("aws_network_interface.bar", &conf),
|
||||||
|
testAccCheckAWSENIAttributes(&conf),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_network_interface.bar", "private_ips.#", "1"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_network_interface.bar", "tags.Name", "bar_interface"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccAWSENI_attached(t *testing.T) {
|
||||||
|
var conf ec2.NetworkInterface
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAWSENIDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSENIConfigWithAttachment,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckAWSENIExists("aws_network_interface.bar", &conf),
|
||||||
|
testAccCheckAWSENIAttributesWithAttachment(&conf),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_network_interface.bar", "private_ips.#", "1"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_network_interface.bar", "tags.Name", "bar_interface"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSENIExists(n string, res *ec2.NetworkInterface) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
rs, ok := s.RootModule().Resources[n]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not found: %s", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rs.Primary.ID == "" {
|
||||||
|
return fmt.Errorf("No ENI ID is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
ec2conn := testAccProvider.Meta().(*AWSClient).ec2conn
|
||||||
|
describe_network_interfaces_request := &ec2.DescribeNetworkInterfacesRequest{
|
||||||
|
NetworkInterfaceIDs: []string{rs.Primary.ID},
|
||||||
|
}
|
||||||
|
describeResp, err := ec2conn.DescribeNetworkInterfaces(describe_network_interfaces_request)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(describeResp.NetworkInterfaces) != 1 ||
|
||||||
|
*describeResp.NetworkInterfaces[0].NetworkInterfaceID != rs.Primary.ID {
|
||||||
|
return fmt.Errorf("ENI not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
*res = describeResp.NetworkInterfaces[0]
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSENIAttributes(conf *ec2.NetworkInterface) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
|
||||||
|
if conf.Attachment != nil {
|
||||||
|
return fmt.Errorf("expected attachment to be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if *conf.AvailabilityZone != "us-west-2a" {
|
||||||
|
return fmt.Errorf("expected availability_zone to be us-west-2a, but was %s", *conf.AvailabilityZone)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(conf.Groups) != 1 && *conf.Groups[0].GroupName != "foo" {
|
||||||
|
return fmt.Errorf("expected security group to be foo, but was %#v", conf.Groups)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *conf.PrivateIPAddress != "172.16.10.100" {
|
||||||
|
return fmt.Errorf("expected private ip to be 172.16.10.100, but was %s", *conf.PrivateIPAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSENIAttributesWithAttachment(conf *ec2.NetworkInterface) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
|
||||||
|
if conf.Attachment == nil {
|
||||||
|
return fmt.Errorf("expected attachment to be set, but was nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if *conf.Attachment.DeviceIndex != 1 {
|
||||||
|
return fmt.Errorf("expected attachment device index to be 1, but was %d", *conf.Attachment.DeviceIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *conf.AvailabilityZone != "us-west-2a" {
|
||||||
|
return fmt.Errorf("expected availability_zone to be us-west-2a, but was %s", *conf.AvailabilityZone)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(conf.Groups) != 1 && *conf.Groups[0].GroupName != "foo" {
|
||||||
|
return fmt.Errorf("expected security group to be foo, but was %#v", conf.Groups)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *conf.PrivateIPAddress != "172.16.10.100" {
|
||||||
|
return fmt.Errorf("expected private ip to be 172.16.10.100, but was %s", *conf.PrivateIPAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSENIDestroy(s *terraform.State) error {
|
||||||
|
for _, rs := range s.RootModule().Resources {
|
||||||
|
if rs.Type != "aws_network_interface" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ec2conn := testAccProvider.Meta().(*AWSClient).ec2conn
|
||||||
|
describe_network_interfaces_request := &ec2.DescribeNetworkInterfacesRequest{
|
||||||
|
NetworkInterfaceIDs: []string{rs.Primary.ID},
|
||||||
|
}
|
||||||
|
_, err := ec2conn.DescribeNetworkInterfaces(describe_network_interfaces_request)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "InvalidNetworkInterfaceID.NotFound" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const testAccAWSENIConfig = `
|
||||||
|
resource "aws_vpc" "foo" {
|
||||||
|
cidr_block = "172.16.0.0/16"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_subnet" "foo" {
|
||||||
|
vpc_id = "${aws_vpc.foo.id}"
|
||||||
|
cidr_block = "172.16.10.0/24"
|
||||||
|
availability_zone = "us-west-2a"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_security_group" "foo" {
|
||||||
|
vpc_id = "${aws_vpc.foo.id}"
|
||||||
|
description = "foo"
|
||||||
|
name = "foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_network_interface" "bar" {
|
||||||
|
subnet_id = "${aws_subnet.foo.id}"
|
||||||
|
private_ips = ["172.16.10.100"]
|
||||||
|
security_groups = ["${aws_security_group.foo.id}"]
|
||||||
|
tags {
|
||||||
|
Name = "bar_interface"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const testAccAWSENIConfigWithAttachment = `
|
||||||
|
resource "aws_vpc" "foo" {
|
||||||
|
cidr_block = "172.16.0.0/16"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_subnet" "foo" {
|
||||||
|
vpc_id = "${aws_vpc.foo.id}"
|
||||||
|
cidr_block = "172.16.10.0/24"
|
||||||
|
availability_zone = "us-west-2a"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_subnet" "bar" {
|
||||||
|
vpc_id = "${aws_vpc.foo.id}"
|
||||||
|
cidr_block = "172.16.11.0/24"
|
||||||
|
availability_zone = "us-west-2a"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_security_group" "foo" {
|
||||||
|
vpc_id = "${aws_vpc.foo.id}"
|
||||||
|
description = "foo"
|
||||||
|
name = "foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "foo" {
|
||||||
|
ami = "ami-c5eabbf5"
|
||||||
|
instance_type = "t2.micro"
|
||||||
|
subnet_id = "${aws_subnet.bar.id}"
|
||||||
|
associate_public_ip_address = false
|
||||||
|
private_ip = "172.16.11.50"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_network_interface" "bar" {
|
||||||
|
subnet_id = "${aws_subnet.foo.id}"
|
||||||
|
private_ips = ["172.16.10.100"]
|
||||||
|
security_groups = ["${aws_security_group.foo.id}"]
|
||||||
|
attachment {
|
||||||
|
instance = "${aws_instance.foo.id}"
|
||||||
|
device_index = 1
|
||||||
|
}
|
||||||
|
tags {
|
||||||
|
Name = "bar_interface"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
|
@ -207,3 +207,47 @@ func expandStringList(configured []interface{}) []string {
|
||||||
}
|
}
|
||||||
return vs
|
return vs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Flattens an array of private ip addresses into a []string, where the elements returned are the IP strings e.g. "192.168.0.0"
|
||||||
|
func flattenNetworkInterfacesPrivateIPAddesses(dtos []ec2.NetworkInterfacePrivateIPAddress) []string {
|
||||||
|
ips := make([]string, 0, len(dtos))
|
||||||
|
for _, v := range dtos {
|
||||||
|
ip := *v.PrivateIPAddress
|
||||||
|
ips = append(ips, ip)
|
||||||
|
}
|
||||||
|
return ips
|
||||||
|
}
|
||||||
|
|
||||||
|
//Flattens security group identifiers into a []string, where the elements returned are the GroupIDs
|
||||||
|
func flattenGroupIdentifiers(dtos []ec2.GroupIdentifier) []string {
|
||||||
|
ids := make([]string, 0, len(dtos))
|
||||||
|
for _, v := range dtos {
|
||||||
|
group_id := *v.GroupID
|
||||||
|
ids = append(ids, group_id)
|
||||||
|
}
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
||||||
|
//Expands an array of IPs into a ec2 Private IP Address Spec
|
||||||
|
func expandPrivateIPAddesses(ips []interface{}) []ec2.PrivateIPAddressSpecification {
|
||||||
|
dtos := make([]ec2.PrivateIPAddressSpecification, 0, len(ips))
|
||||||
|
for i, v := range ips {
|
||||||
|
new_private_ip := ec2.PrivateIPAddressSpecification{
|
||||||
|
PrivateIPAddress: aws.String(v.(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
new_private_ip.Primary = aws.Boolean(i == 0)
|
||||||
|
|
||||||
|
dtos = append(dtos, new_private_ip)
|
||||||
|
}
|
||||||
|
return dtos
|
||||||
|
}
|
||||||
|
|
||||||
|
//Flattens network interface attachment into a map[string]interface
|
||||||
|
func flattenAttachment(a *ec2.NetworkInterfaceAttachment) map[string]interface{} {
|
||||||
|
att := make(map[string]interface{})
|
||||||
|
att["instance"] = *a.InstanceID
|
||||||
|
att["device_index"] = *a.DeviceIndex
|
||||||
|
att["attachment_id"] = *a.AttachmentID
|
||||||
|
return att
|
||||||
|
}
|
||||||
|
|
|
@ -346,3 +346,99 @@ func TestExpandInstanceString(t *testing.T) {
|
||||||
t.Fatalf("Expand Instance String output did not match.\nGot:\n%#v\n\nexpected:\n%#v", expanded, expected)
|
t.Fatalf("Expand Instance String output did not match.\nGot:\n%#v\n\nexpected:\n%#v", expanded, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFlattenNetworkInterfacesPrivateIPAddesses(t *testing.T) {
|
||||||
|
expanded := []ec2.NetworkInterfacePrivateIPAddress{
|
||||||
|
ec2.NetworkInterfacePrivateIPAddress{PrivateIPAddress: aws.String("192.168.0.1")},
|
||||||
|
ec2.NetworkInterfacePrivateIPAddress{PrivateIPAddress: aws.String("192.168.0.2")},
|
||||||
|
}
|
||||||
|
|
||||||
|
result := flattenNetworkInterfacesPrivateIPAddesses(expanded)
|
||||||
|
|
||||||
|
if result == nil {
|
||||||
|
t.Fatal("result was nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(result) != 2 {
|
||||||
|
t.Fatalf("expected result had %d elements, but got %d", 2, len(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
if result[0] != "192.168.0.1" {
|
||||||
|
t.Fatalf("expected ip to be 192.168.0.1, but was %s", result[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if result[1] != "192.168.0.2" {
|
||||||
|
t.Fatalf("expected ip to be 192.168.0.2, but was %s", result[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlattenGroupIdentifiers(t *testing.T) {
|
||||||
|
expanded := []ec2.GroupIdentifier{
|
||||||
|
ec2.GroupIdentifier{GroupID: aws.String("sg-001")},
|
||||||
|
ec2.GroupIdentifier{GroupID: aws.String("sg-002")},
|
||||||
|
}
|
||||||
|
|
||||||
|
result := flattenGroupIdentifiers(expanded)
|
||||||
|
|
||||||
|
if len(result) != 2 {
|
||||||
|
t.Fatalf("expected result had %d elements, but got %d", 2, len(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
if result[0] != "sg-001" {
|
||||||
|
t.Fatalf("expected id to be sg-001, but was %s", result[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if result[1] != "sg-002" {
|
||||||
|
t.Fatalf("expected id to be sg-002, but was %s", result[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExpandPrivateIPAddesses(t *testing.T) {
|
||||||
|
|
||||||
|
ip1 := "192.168.0.1"
|
||||||
|
ip2 := "192.168.0.2"
|
||||||
|
flattened := []interface{}{
|
||||||
|
ip1,
|
||||||
|
ip2,
|
||||||
|
}
|
||||||
|
|
||||||
|
result := expandPrivateIPAddesses(flattened)
|
||||||
|
|
||||||
|
if len(result) != 2 {
|
||||||
|
t.Fatalf("expected result had %d elements, but got %d", 2, len(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
if *result[0].PrivateIPAddress != "192.168.0.1" || !*result[0].Primary {
|
||||||
|
t.Fatalf("expected ip to be 192.168.0.1 and Primary, but got %v, %b", *result[0].PrivateIPAddress, *result[0].Primary)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *result[1].PrivateIPAddress != "192.168.0.2" || *result[1].Primary {
|
||||||
|
t.Fatalf("expected ip to be 192.168.0.2 and not Primary, but got %v, %b", *result[1].PrivateIPAddress, *result[1].Primary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlattenAttachment(t *testing.T) {
|
||||||
|
expanded := &ec2.NetworkInterfaceAttachment{
|
||||||
|
InstanceID: aws.String("i-00001"),
|
||||||
|
DeviceIndex: aws.Integer(1),
|
||||||
|
AttachmentID: aws.String("at-002"),
|
||||||
|
}
|
||||||
|
|
||||||
|
result := flattenAttachment(expanded)
|
||||||
|
|
||||||
|
if result == nil {
|
||||||
|
t.Fatal("expected result to have value, but got nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if result["instance"] != "i-00001" {
|
||||||
|
t.Fatalf("expected instance to be i-00001, but got %s", result["instance"])
|
||||||
|
}
|
||||||
|
|
||||||
|
if result["device_index"] != 1 {
|
||||||
|
t.Fatalf("expected device_index to be 1, but got %d", result["device_index"])
|
||||||
|
}
|
||||||
|
|
||||||
|
if result["attachment_id"] != "at-002" {
|
||||||
|
t.Fatalf("expected attachment_id to be at-002, but got %s", result["attachment_id"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue