From b29e9d3b6feda7ad523935d682e0733dea0e474c Mon Sep 17 00:00:00 2001 From: James Stremick Date: Sat, 25 Apr 2015 22:30:37 -0400 Subject: [PATCH] Allow assocation of EIP to ENI --- builtin/providers/aws/resource_aws_eip.go | 39 +++++++++----- .../providers/aws/resource_aws_eip_test.go | 52 +++++++++++++++++++ 2 files changed, 79 insertions(+), 12 deletions(-) diff --git a/builtin/providers/aws/resource_aws_eip.go b/builtin/providers/aws/resource_aws_eip.go index f871e764d..b667b0779 100644 --- a/builtin/providers/aws/resource_aws_eip.go +++ b/builtin/providers/aws/resource_aws_eip.go @@ -31,6 +31,12 @@ func resourceAwsEip() *schema.Resource { Optional: true, }, + "network_interface": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"instance"}, + }, + "allocation_id": &schema.Schema{ Type: schema.TypeString, Computed: true, @@ -114,10 +120,14 @@ func resourceAwsEipRead(d *schema.ResourceData, meta interface{}) error { "[DEBUG] EIP describe configuration: %#v, %#v (domain: %s)", assocIds, publicIps, domain) - req := &ec2.DescribeAddressesInput{ - AllocationIDs: assocIds, - PublicIPs: publicIps, + req := &ec2.DescribeAddressesInput{} + + if domain == "vpc" { + req.AllocationIDs = []*string{aws.String(id)} + } else { + req.PublicIPs = []*string{aws.String(id)} } + describeAddresses, err := ec2conn.DescribeAddresses(req) if err != nil { if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "InvalidAllocationID.NotFound" { @@ -141,6 +151,7 @@ func resourceAwsEipRead(d *schema.ResourceData, meta interface{}) error { d.Set("association_id", address.AssociationID) d.Set("instance", address.InstanceID) + d.Set("network_interface", address.NetworkInterfaceID) d.Set("private_ip", address.PrivateIPAddress) d.Set("public_ip", address.PublicIP) @@ -152,9 +163,13 @@ func resourceAwsEipUpdate(d *schema.ResourceData, meta interface{}) error { domain := resourceAwsEipDomain(d) - // Only register with an instance if we have one - if v, ok := d.GetOk("instance"); ok { - instanceId := v.(string) + // Associate to instance or interface if specified + v_instance, ok_instance := d.GetOk("instance") + v_interface, ok_interface := d.GetOk("network_interface") + + if ok_instance || ok_interface { + instanceId := v_instance.(string) + networkInterfaceId := v_interface.(string) assocOpts := &ec2.AssociateAddressInput{ InstanceID: aws.String(instanceId), @@ -164,16 +179,16 @@ func resourceAwsEipUpdate(d *schema.ResourceData, meta interface{}) error { // more unique ID conditionals if domain == "vpc" { assocOpts = &ec2.AssociateAddressInput{ - InstanceID: aws.String(instanceId), - AllocationID: aws.String(d.Id()), - PublicIP: aws.String(""), + NetworkInterfaceID: aws.String(networkInterfaceId), + InstanceID: aws.String(instanceId), + AllocationID: aws.String(d.Id()), } } log.Printf("[DEBUG] EIP associate configuration: %#v (domain: %v)", assocOpts, domain) _, err := ec2conn.AssociateAddress(assocOpts) if err != nil { - return fmt.Errorf("Failure associating instances: %s", err) + return fmt.Errorf("Failure associating EIP: %s", err) } } @@ -191,8 +206,8 @@ func resourceAwsEipDelete(d *schema.ResourceData, meta interface{}) error { return nil } - // If we are attached to an instance, detach first. - if d.Get("instance").(string) != "" { + // If we are attached to an instance or interface, detach first. + if d.Get("instance").(string) != "" || d.Get("association_id").(string) != "" { log.Printf("[DEBUG] Disassociating EIP: %s", d.Id()) var err error switch resourceAwsEipDomain(d) { diff --git a/builtin/providers/aws/resource_aws_eip_test.go b/builtin/providers/aws/resource_aws_eip_test.go index 199dc3046..b87794131 100644 --- a/builtin/providers/aws/resource_aws_eip_test.go +++ b/builtin/providers/aws/resource_aws_eip_test.go @@ -57,6 +57,26 @@ func TestAccAWSEIP_instance(t *testing.T) { }) } +func TestAccAWSEIP_network_interface(t *testing.T) { + var conf ec2.Address + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEIPDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSEIPNetworkInterfaceConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEIPExists("aws_eip.bar", &conf), + testAccCheckAWSEIPAttributes(&conf), + testAccCheckAWSEIPAssociated(&conf), + ), + }, + }, + }) +} + func testAccCheckAWSEIPDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).ec2conn @@ -101,6 +121,16 @@ func testAccCheckAWSEIPAttributes(conf *ec2.Address) resource.TestCheckFunc { } } +func testAccCheckAWSEIPAssociated(conf *ec2.Address) resource.TestCheckFunc { + return func(s *terraform.State) error { + if *conf.AssociationID == "" { + return fmt.Errorf("empty association_id") + } + + return nil + } +} + func testAccCheckAWSEIPExists(n string, res *ec2.Address) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -177,3 +207,25 @@ resource "aws_eip" "bar" { instance = "${aws_instance.bar.id}" } ` +const testAccAWSEIPNetworkInterfaceConfig = ` +resource "aws_vpc" "bar" { + cidr_block = "10.0.0.0/24" +} +resource "aws_internet_gateway" "bar" { + vpc_id = "${aws_vpc.bar.id}" +} +resource "aws_subnet" "bar" { + vpc_id = "${aws_vpc.bar.id}" + availability_zone = "us-west-2a" + cidr_block = "10.0.0.0/24" +} +resource "aws_network_interface" "bar" { + subnet_id = "${aws_subnet.bar.id}" + private_ips = ["10.0.0.10"] + security_groups = [ "${aws_vpc.bar.default_security_group_id}" ] +} +resource "aws_eip" "bar" { + vpc = "true" + network_interface = "${aws_network_interface.bar.id}" +} +`