diff --git a/builtin/providers/aws/resource_aws_route_table_association.go b/builtin/providers/aws/resource_aws_route_table_association.go new file mode 100644 index 0000000000..f872ddf766 --- /dev/null +++ b/builtin/providers/aws/resource_aws_route_table_association.go @@ -0,0 +1,136 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/helper/diff" + "github.com/hashicorp/terraform/terraform" + "github.com/mitchellh/goamz/ec2" +) + +func resource_aws_route_table_association_create( + s *terraform.ResourceState, + d *terraform.ResourceDiff, + meta interface{}) (*terraform.ResourceState, error) { + p := meta.(*ResourceProvider) + ec2conn := p.ec2conn + rs := s.MergeDiff(d) + + log.Printf( + "[INFO] Creating route table association: %s => %s", + rs.Attributes["subnet_id"], + rs.Attributes["route_table_id"]) + resp, err := ec2conn.AssociateRouteTable( + rs.Attributes["route_table_id"], + rs.Attributes["subnet_id"]) + if err != nil { + return nil, err + } + + // Set the ID and return + rs.ID = resp.AssociationId + log.Printf("[INFO] Association ID: %s", rs.ID) + + rs.Dependencies = []terraform.ResourceDependency{ + terraform.ResourceDependency{ID: rs.Attributes["route_table_id"]}, + } + + return rs, nil +} + +func resource_aws_route_table_association_update( + s *terraform.ResourceState, + d *terraform.ResourceDiff, + meta interface{}) (*terraform.ResourceState, error) { + p := meta.(*ResourceProvider) + ec2conn := p.ec2conn + + rs := s.MergeDiff(d) + log.Printf( + "[INFO] Replacing route table association: %s => %s", + rs.Attributes["subnet_id"], + rs.Attributes["route_table_id"]) + resp, err := ec2conn.ReassociateRouteTable( + rs.ID, + rs.Attributes["route_table_id"]) + if err != nil { + return s, err + } + + // Update the ID + rs.ID = resp.AssociationId + log.Printf("[INFO] Association ID: %s", rs.ID) + + rs.Dependencies = []terraform.ResourceDependency{ + terraform.ResourceDependency{ID: rs.Attributes["route_table_id"]}, + } + + return rs, nil +} + +func resource_aws_route_table_association_destroy( + s *terraform.ResourceState, + meta interface{}) error { + p := meta.(*ResourceProvider) + ec2conn := p.ec2conn + + log.Printf("[INFO] Deleting route table association: %s", s.ID) + if _, err := ec2conn.DisassociateRouteTable(s.ID); err != nil { + ec2err, ok := err.(*ec2.Error) + if ok && ec2err.Code == "InvalidAssociationID.NotFound" { + return nil + } + + return fmt.Errorf("Error deleting route table association: %s", err) + } + + return nil +} + +func resource_aws_route_table_association_refresh( + s *terraform.ResourceState, + meta interface{}) (*terraform.ResourceState, error) { + p := meta.(*ResourceProvider) + ec2conn := p.ec2conn + + // Get the routing table that this association belongs to + rtRaw, _, err := RouteTableStateRefreshFunc( + ec2conn, s.Attributes["route_table_id"])() + if err != nil { + return s, err + } + if rtRaw == nil { + return nil, nil + } + rt := rtRaw.(*ec2.RouteTable) + + // Inspect that the association exists + found := false + for _, a := range rt.Associations { + if a.AssociationId == s.ID { + found = true + s.Attributes["subnet_id"] = a.SubnetId + break + } + } + if !found { + return nil, nil + } + + return s, nil +} + +func resource_aws_route_table_association_diff( + s *terraform.ResourceState, + c *terraform.ResourceConfig, + meta interface{}) (*terraform.ResourceDiff, error) { + b := &diff.ResourceBuilder{ + Attrs: map[string]diff.AttrType{ + "subnet_id": diff.AttrTypeCreate, + "route_table_id": diff.AttrTypeUpdate, + }, + } + + return b.Diff(s, c) +} diff --git a/builtin/providers/aws/resource_aws_route_table_association_test.go b/builtin/providers/aws/resource_aws_route_table_association_test.go new file mode 100644 index 0000000000..944c83ba4a --- /dev/null +++ b/builtin/providers/aws/resource_aws_route_table_association_test.go @@ -0,0 +1,114 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/mitchellh/goamz/ec2" +) + +func TestAccAWSRouteTableAssociation(t *testing.T) { + var v ec2.RouteTable + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckRouteTableAssociationDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRouteTableAssociationConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckRouteTableAssociationExists( + "aws_route_table_association.foo", &v), + ), + }, + }, + }) +} + +func testAccCheckRouteTableAssociationDestroy(s *terraform.State) error { + conn := testAccProvider.ec2conn + + for _, rs := range s.Resources { + if rs.Type != "aws_route_table_association" { + continue + } + + // Try to find the resource + resp, err := conn.DescribeRouteTables( + []string{rs.Attributes["route_table_Id"]}, ec2.NewFilter()) + if err != nil { + // Verify the error is what we want + ec2err, ok := err.(*ec2.Error) + if !ok { + return err + } + if ec2err.Code != "InvalidRouteTableID.NotFound" { + return err + } + return nil + } + + rt := resp.RouteTables[0] + if len(rt.Associations) > 0 { + return fmt.Errorf( + "route table %s has associations", rt.RouteTableId) + + } + } + + return nil +} + +func testAccCheckRouteTableAssociationExists(n string, v *ec2.RouteTable) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.ID == "" { + return fmt.Errorf("No ID is set") + } + + conn := testAccProvider.ec2conn + resp, err := conn.DescribeRouteTables( + []string{rs.Attributes["route_table_id"]}, ec2.NewFilter()) + if err != nil { + return err + } + if len(resp.RouteTables) == 0 { + return fmt.Errorf("RouteTable not found") + } + + *v = resp.RouteTables[0] + + if len(v.Associations) == 0 { + return fmt.Errorf("no associations") + } + + return nil + } +} + +const testAccRouteTableAssociationConfig = ` +resource "aws_vpc" "foo" { + cidr_block = "10.1.0.0/16" +} + +resource "aws_subnet" "foo" { + vpc_id = "${aws_vpc.foo.id}" + cidr_block = "10.1.1.0/24" +} + +resource "aws_route_table" "foo" { + vpc_id = "${aws_vpc.foo.id}" +} + +resource "aws_route_table_association" "foo" { + route_table_id = "${aws_route_table.foo.id}" + subnet_id = "${aws_subnet.foo.id}" +} +` diff --git a/builtin/providers/aws/resources.go b/builtin/providers/aws/resources.go index 1f0327e327..ab58c61ca9 100644 --- a/builtin/providers/aws/resources.go +++ b/builtin/providers/aws/resources.go @@ -73,6 +73,20 @@ func init() { Update: resource_aws_route_table_update, }, + "aws_route_table_association": resource.Resource{ + ConfigValidator: &config.Validator{ + Required: []string{ + "route_table_id", + "subnet_id", + }, + }, + Create: resource_aws_route_table_association_create, + Destroy: resource_aws_route_table_association_destroy, + Diff: resource_aws_route_table_association_diff, + Refresh: resource_aws_route_table_association_refresh, + Update: resource_aws_route_table_association_update, + }, + "aws_security_group": resource.Resource{ Create: resource_aws_security_group_create, Destroy: resource_aws_security_group_destroy,