Fixing aws identifiers for aws_route.

Fixing basic acceptance test.
Adding warning to website about mixed mode.
Adding exists to aws_route.
Adding acceptance test for changing destination_cidr_block.
This commit is contained in:
BSick7 2015-10-15 17:55:47 -04:00
parent ac0afad6e9
commit 6a593f9d17
5 changed files with 316 additions and 66 deletions

View File

@ -11,13 +11,14 @@ import (
"github.com/hashicorp/terraform/helper/schema"
)
// AWS Route resource Schema delcaration
// AWS Route resource Schema declaration
func resourceAwsRoute() *schema.Resource {
return &schema.Resource{
Create: resourceAwsRouteCreate,
Read: resourceAwsRouteRead,
Update: resourceAwsRouteUpdate,
Delete: resourceAwsRouteDelete,
Exists: resourceAwsRouteExists,
Schema: map[string]*schema.Schema{
"destination_cidr_block": &schema.Schema{
@ -83,7 +84,6 @@ func resourceAwsRouteCreate(d *schema.ResourceData, meta interface{}) error {
"network_interface_id",
"vpc_peering_connection_id",
}
createOpts := &ec2.CreateRouteInput{}
// Check if more than 1 target is specified
for _, target := range allowedTargets {
@ -99,31 +99,32 @@ func resourceAwsRouteCreate(d *schema.ResourceData, meta interface{}) error {
"vpc_peering_connection_id is allowed.")
}
createOpts := &ec2.CreateRouteInput{}
// Formulate CreateRouteInput based on the target type
switch setTarget {
case "gateway_id":
createOpts = &ec2.CreateRouteInput{
RouteTableID: aws.String(d.Get("route_table_id").(string)),
DestinationCIDRBlock: aws.String(d.Get("destination_cidr_block").(string)),
GatewayID: aws.String(d.Get("gateway_id").(string)),
RouteTableId: aws.String(d.Get("route_table_id").(string)),
DestinationCidrBlock: aws.String(d.Get("destination_cidr_block").(string)),
GatewayId: aws.String(d.Get("gateway_id").(string)),
}
case "instance_id":
createOpts = &ec2.CreateRouteInput{
RouteTableID: aws.String(d.Get("route_table_id").(string)),
DestinationCIDRBlock: aws.String(d.Get("destination_cidr_block").(string)),
InstanceID: aws.String(d.Get("instance_id").(string)),
RouteTableId: aws.String(d.Get("route_table_id").(string)),
DestinationCidrBlock: aws.String(d.Get("destination_cidr_block").(string)),
InstanceId: aws.String(d.Get("instance_id").(string)),
}
case "network_interface_id":
createOpts = &ec2.CreateRouteInput{
RouteTableID: aws.String(d.Get("route_table_id").(string)),
DestinationCIDRBlock: aws.String(d.Get("destination_cidr_block").(string)),
NetworkInterfaceID: aws.String(d.Get("network_interface_id").(string)),
RouteTableId: aws.String(d.Get("route_table_id").(string)),
DestinationCidrBlock: aws.String(d.Get("destination_cidr_block").(string)),
NetworkInterfaceId: aws.String(d.Get("network_interface_id").(string)),
}
case "vpc_peering_connection_id":
createOpts = &ec2.CreateRouteInput{
RouteTableID: aws.String(d.Get("route_table_id").(string)),
DestinationCIDRBlock: aws.String(d.Get("destination_cidr_block").(string)),
VPCPeeringConnectionID: aws.String(d.Get("vpc_peering_connection_id").(string)),
RouteTableId: aws.String(d.Get("route_table_id").(string)),
DestinationCidrBlock: aws.String(d.Get("destination_cidr_block").(string)),
VpcPeeringConnectionId: aws.String(d.Get("vpc_peering_connection_id").(string)),
}
default:
fmt.Errorf("Error: invalid target type specified.")
@ -153,19 +154,23 @@ func resourceAwsRouteRead(d *schema.ResourceData, meta interface{}) error {
return err
}
d.Set("destination_prefix_list_id", route.DestinationPrefixListID)
d.Set("gateway_id", route.DestinationPrefixListID)
d.Set("instance_id", route.InstanceID)
d.Set("instance_owner_id", route.InstanceOwnerID)
d.Set("network_interface_id", route.NetworkInterfaceID)
d.Set("destination_prefix_list_id", route.DestinationPrefixListId)
d.Set("gateway_id", route.GatewayId)
d.Set("instance_id", route.InstanceId)
d.Set("instance_owner_id", route.InstanceOwnerId)
d.Set("network_interface_id", route.NetworkInterfaceId)
d.Set("origin", route.Origin)
d.Set("state", route.State)
d.Set("vpc_peering_connection_id", route.VPCPeeringConnectionID)
d.Set("vpc_peering_connection_id", route.VpcPeeringConnectionId)
return nil
}
func resourceAwsRouteUpdate(d *schema.ResourceData, meta interface{}) error {
if d.HasChange("destination_cidr_block") {
return resourceAwsRouteRecreate(d, meta)
}
conn := meta.(*AWSClient).ec2conn
var numTargets int
var setTarget string
@ -195,27 +200,29 @@ func resourceAwsRouteUpdate(d *schema.ResourceData, meta interface{}) error {
switch setTarget {
case "gateway_id":
replaceOpts = &ec2.ReplaceRouteInput{
RouteTableID: aws.String(d.Get("route_table_id").(string)),
DestinationCIDRBlock: aws.String(d.Get("destination_cidr_block").(string)),
GatewayID: aws.String(d.Get("gateway_id").(string)),
RouteTableId: aws.String(d.Get("route_table_id").(string)),
DestinationCidrBlock: aws.String(d.Get("destination_cidr_block").(string)),
GatewayId: aws.String(d.Get("gateway_id").(string)),
}
case "instance_id":
replaceOpts = &ec2.ReplaceRouteInput{
RouteTableID: aws.String(d.Get("route_table_id").(string)),
DestinationCIDRBlock: aws.String(d.Get("destination_cidr_block").(string)),
InstanceID: aws.String(d.Get("instance_id").(string)),
RouteTableId: aws.String(d.Get("route_table_id").(string)),
DestinationCidrBlock: aws.String(d.Get("destination_cidr_block").(string)),
InstanceId: aws.String(d.Get("instance_id").(string)),
//NOOP: Ensure we don't blow away network interface id that is set after instance is launched
NetworkInterfaceId: aws.String(d.Get("network_interface_id").(string)),
}
case "network_interface_id":
replaceOpts = &ec2.ReplaceRouteInput{
RouteTableID: aws.String(d.Get("route_table_id").(string)),
DestinationCIDRBlock: aws.String(d.Get("destination_cidr_block").(string)),
NetworkInterfaceID: aws.String(d.Get("network_interface_id").(string)),
RouteTableId: aws.String(d.Get("route_table_id").(string)),
DestinationCidrBlock: aws.String(d.Get("destination_cidr_block").(string)),
NetworkInterfaceId: aws.String(d.Get("network_interface_id").(string)),
}
case "vpc_peering_connection_id":
replaceOpts = &ec2.ReplaceRouteInput{
RouteTableID: aws.String(d.Get("route_table_id").(string)),
DestinationCIDRBlock: aws.String(d.Get("destination_cidr_block").(string)),
VPCPeeringConnectionID: aws.String(d.Get("vpc_peering_connection_id").(string)),
RouteTableId: aws.String(d.Get("route_table_id").(string)),
DestinationCidrBlock: aws.String(d.Get("destination_cidr_block").(string)),
VpcPeeringConnectionId: aws.String(d.Get("vpc_peering_connection_id").(string)),
}
default:
fmt.Errorf("Error: invalid target type specified.")
@ -231,28 +238,65 @@ func resourceAwsRouteUpdate(d *schema.ResourceData, meta interface{}) error {
return nil
}
func resourceAwsRouteDelete(d *schema.ResourceData, meta interface{}) error {
func resourceAwsRouteRecreate(d *schema.ResourceData, meta interface{}) error {
//Destination Cidr is used for identification
// if changed, we should delete the old route, recreate the new route
conn := meta.(*AWSClient).ec2conn
deleteOpts := &ec2.DeleteRouteInput{
DestinationCIDRBlock: aws.String(d.Get("destination_cidr_block").(string)),
RouteTableID: aws.String(d.Get("route_table_id").(string)),
}
log.Printf("[DEBUG] Route delete opts: %s", awsutil.Prettify(deleteOpts))
resp, err := conn.DeleteRoute(deleteOpts)
log.Printf("[DEBUG] Route delete result: %s", awsutil.Prettify(resp))
oc, _ := d.GetChange("destination_cidr_block")
var oldRtId interface{}
if d.HasChange("route_table_id") {
oldRtId, _ = d.GetChange("route_table_id")
} else {
oldRtId = d.Get("route_table_id")
}
if err := deleteAwsRoute(conn, oldRtId.(string), oc.(string)); err != nil {
return err
}
d.SetId("")
return resourceAwsRouteCreate(d, meta)
}
func resourceAwsRouteDelete(d *schema.ResourceData, meta interface{}) error {
err := deleteAwsRoute(meta.(*AWSClient).ec2conn,
d.Get("route_table_id").(string), d.Get("destination_cidr_block").(string))
if err != nil {
return err
}
d.SetId("")
return nil
}
func resourceAwsRouteExists(d *schema.ResourceData, meta interface{}) (bool, error) {
conn := meta.(*AWSClient).ec2conn
routeTableId := d.Get("route_table_id").(string)
findOpts := &ec2.DescribeRouteTablesInput{
RouteTableIds: []*string{&routeTableId},
}
res, err := conn.DescribeRouteTables(findOpts)
if err != nil {
return false, err
}
cidr := d.Get("destination_cidr_block").(string)
for _, route := range (*res.RouteTables[0]).Routes {
if *route.DestinationCidrBlock == cidr {
return true, nil
}
}
return false, nil
}
// Create an ID for a route
func routeIDHash(d *schema.ResourceData, r *ec2.Route) string {
return fmt.Sprintf("r-%s%d", d.Get("route_table_id").(string), hashcode.String(*r.DestinationCIDRBlock))
return fmt.Sprintf("r-%s%d", d.Get("route_table_id").(string), hashcode.String(*r.DestinationCidrBlock))
}
// Helper: retrieve a route
@ -260,7 +304,7 @@ func findResourceRoute(conn *ec2.EC2, rtbid string, cidr string) (*ec2.Route, er
routeTableID := rtbid
findOpts := &ec2.DescribeRouteTablesInput{
RouteTableIDs: []*string{&routeTableID},
RouteTableIds: []*string{&routeTableID},
}
resp, err := conn.DescribeRouteTables(findOpts)
@ -269,10 +313,25 @@ func findResourceRoute(conn *ec2.EC2, rtbid string, cidr string) (*ec2.Route, er
}
for _, route := range (*resp.RouteTables[0]).Routes {
if *route.DestinationCIDRBlock == cidr {
if *route.DestinationCidrBlock == cidr {
return route, nil
}
}
return nil, nil
}
func deleteAwsRoute(conn *ec2.EC2, routeTableId string, cidr string) error {
deleteOpts := &ec2.DeleteRouteInput{
RouteTableId: aws.String(routeTableId),
DestinationCidrBlock: aws.String(cidr),
}
log.Printf("[DEBUG] Route delete opts: %s", awsutil.Prettify(deleteOpts))
resp, err := conn.DeleteRoute(deleteOpts)
log.Printf("[DEBUG] Route delete result: %s", awsutil.Prettify(resp))
if err != nil {
return err
}
return nil
}

View File

@ -41,6 +41,7 @@ func resourceAwsRouteTable() *schema.Resource {
"route": &schema.Schema{
Type: schema.TypeSet,
Computed: true,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{

View File

@ -12,22 +12,172 @@ import (
func TestAccAWSRoute_basic(t *testing.T) {
var route ec2.Route
//aws creates a default route
testCheck := func(s *terraform.State) error {
if *route.DestinationCidrBlock != "10.3.0.0/16" {
return fmt.Errorf("Destination Cidr (Expected=%s, Actual=%s)\n", "10.3.0.0/16", *route.DestinationCidrBlock)
}
name := "aws_internet_gateway.foo"
gwres, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Not found: %s\n", name)
}
if *route.GatewayId != gwres.Primary.ID {
return fmt.Errorf("Internet Gateway Id (Expected=%s, Actual=%s)\n", gwres.Primary.ID, *route.GatewayId)
}
return nil
}
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSRouteDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSRouteConfig,
Config: testAccAWSRouteBasicConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSRouteExists("aws_route.bar", &route),
testAccCheckAWSRouteAttributes(&route),
testCheck,
),
},
},
})
}
func TestAccAWSRoute_changeCidr(t *testing.T) {
var route ec2.Route
var routeTable ec2.RouteTable
//aws creates a default route
testCheck := func(s *terraform.State) error {
if *route.DestinationCidrBlock != "10.3.0.0/16" {
return fmt.Errorf("Destination Cidr (Expected=%s, Actual=%s)\n", "10.3.0.0/16", *route.DestinationCidrBlock)
}
name := "aws_internet_gateway.foo"
gwres, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Not found: %s\n", name)
}
if *route.GatewayId != gwres.Primary.ID {
return fmt.Errorf("Internet Gateway Id (Expected=%s, Actual=%s)\n", gwres.Primary.ID, *route.GatewayId)
}
return nil
}
testCheckChange := func(s *terraform.State) error {
if *route.DestinationCidrBlock != "10.2.0.0/16" {
return fmt.Errorf("Destination Cidr (Expected=%s, Actual=%s)\n", "10.2.0.0/16", *route.DestinationCidrBlock)
}
name := "aws_internet_gateway.foo"
gwres, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Not found: %s\n", name)
}
if *route.GatewayId != gwres.Primary.ID {
return fmt.Errorf("Internet Gateway Id (Expected=%s, Actual=%s)\n", gwres.Primary.ID, *route.GatewayId)
}
if rtlen := len(routeTable.Routes); rtlen != 2 {
return fmt.Errorf("Route Table has too many routes (Expected=%d, Actual=%d)\n", rtlen, 2)
}
return nil
}
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSRouteDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSRouteBasicConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSRouteExists("aws_route.bar", &route),
testCheck,
),
},
resource.TestStep{
Config: testAccAWSRouteBasicConfigChangeCidr,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSRouteExists("aws_route.bar", &route),
testAccCheckRouteTableExists("aws_route_table.foo", &routeTable),
testCheckChange,
),
},
},
})
}
// Acceptance test if mixed inline and external routes are implemented
/*
func TestAccAWSRoute_mix(t *testing.T) {
var rt ec2.RouteTable
var route ec2.Route
//aws creates a default route
testCheck := func(s *terraform.State) error {
if *route.DestinationCidrBlock != "0.0.0.0/0" {
return fmt.Errorf("Destination Cidr (Expected=%s, Actual=%s)\n", "0.0.0.0/0", *route.DestinationCidrBlock)
}
name := "aws_internet_gateway.foo"
gwres, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Not found: %s\n", name)
}
if *route.GatewayId != gwres.Primary.ID {
return fmt.Errorf("Internet Gateway Id (Expected=%s, Actual=%s)\n", gwres.Primary.ID, *route.GatewayId)
}
if len(rt.Routes) != 3 {
return fmt.Errorf("bad routes: %#v", rt.Routes)
}
routes := make(map[string]*ec2.Route)
for _, r := range rt.Routes {
routes[*r.DestinationCidrBlock] = r
}
if _, ok := routes["10.1.0.0/16"]; !ok {
return fmt.Errorf("Missing route %s: %#v", "10.1.0.0/16", rt.Routes)
}
if _, ok := routes["10.2.0.0/16"]; !ok {
return fmt.Errorf("Missing route %s: %#v", "10.2.0.0/16", rt.Routes)
}
if _, ok := routes["0.0.0.0/0"]; !ok {
return fmt.Errorf("Missing route %s: %#v", "0.0.0.0/0", rt.Routes)
}
return nil
}
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSRouteDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSRouteMixConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckRouteTableExists("aws_route_table.foo", &rt),
testAccCheckAWSRouteExists("aws_route.bar", &route),
testCheck,
),
},
},
})
}
*/
func testAccCheckAWSRouteExists(n string, res *ec2.Route) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
@ -42,7 +192,7 @@ func testAccCheckAWSRouteExists(n string, res *ec2.Route) resource.TestCheckFunc
conn := testAccProvider.Meta().(*AWSClient).ec2conn
r, err := findResourceRoute(
conn,
rs.Primary.ID,
rs.Primary.Attributes["route_table_id"],
rs.Primary.Attributes["destination_cidr_block"],
)
@ -54,21 +204,7 @@ func testAccCheckAWSRouteExists(n string, res *ec2.Route) resource.TestCheckFunc
return fmt.Errorf("Route not found")
}
return nil
}
}
func testAccCheckAWSRouteAttributes(route *ec2.Route) resource.TestCheckFunc {
return func(s *terraform.State) error {
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_route" {
continue
}
if *route.DestinationCIDRBlock != rs.Primary.Attributes["destination_cidr_block"] {
return fmt.Errorf("Bad Destination CIDR Block on Route\n\t expected: %s\n\tgot %s\n", rs.Primary.Attributes["destination_cidr_block"], *route.DestinationCIDRBlock)
}
}
*res = *r
return nil
}
@ -83,7 +219,7 @@ func testAccCheckAWSRouteDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).ec2conn
route, err := findResourceRoute(
conn,
rs.Primary.ID,
rs.Primary.Attributes["route_table_id"],
rs.Primary.Attributes["destination_cidr_block"],
)
@ -95,7 +231,7 @@ func testAccCheckAWSRouteDestroy(s *terraform.State) error {
return nil
}
var testAccAWSRouteConfig = fmt.Sprint(`
var testAccAWSRouteBasicConfig = fmt.Sprint(`
resource "aws_vpc" "foo" {
cidr_block = "10.1.0.0/16"
}
@ -106,15 +242,57 @@ resource "aws_internet_gateway" "foo" {
resource "aws_route_table" "foo" {
vpc_id = "${aws_vpc.foo.id}"
}
resource "aws_route" "bar" {
route_table_id = "${aws_route_table.foo.id}"
destination_cidr_block = "10.3.0.0/16"
gateway_id = "${aws_internet_gateway.foo.id}"
}
`)
var testAccAWSRouteBasicConfigChangeCidr = fmt.Sprint(`
resource "aws_vpc" "foo" {
cidr_block = "10.1.0.0/16"
}
resource "aws_internet_gateway" "foo" {
vpc_id = "${aws_vpc.foo.id}"
}
resource "aws_route_table" "foo" {
vpc_id = "${aws_vpc.foo.id}"
}
resource "aws_route" "bar" {
route_table_id = "${aws_route_table.foo.id}"
destination_cidr_block = "10.2.0.0/16"
gateway_id = "${aws_internet_gateway.foo.id}"
}
`)
// Acceptance test if mixed inline and external routes are implemented
var testAccAWSRouteMixConfig = fmt.Sprint(`
resource "aws_vpc" "foo" {
cidr_block = "10.1.0.0/16"
}
resource "aws_internet_gateway" "foo" {
vpc_id = "${aws_vpc.foo.id}"
}
resource "aws_route_table" "foo" {
vpc_id = "${aws_vpc.foo.id}"
route {
cidr_block = "10.2.0.0/16"
gateway_id = "${aws_internet_gateway.foo.id}"
}
}
resource "aws_route" "foo" {
resource "aws_route" "bar" {
route_table_id = "${aws_route_table.foo.id}"
destination_cidr_block = "${aws_vpc.foo.cidr_block}"
destination_cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.foo.id}"
}
`)

View File

@ -10,6 +10,12 @@ description: |-
Provides a resource to create a routing table entry (a route) in a VPC routing table.
~> **NOTE on Route Tables and Routes:** Terraform currently
provides both a standalone [Route resource](route.html) and a Route Table resource with routes
defined in-line. At this time you cannot use a Route Table with in-line routes
in conjunction with any Route resources. Doing so will cause
a conflict of rule settings and will overwrite rules.
## Example usage:
```

View File

@ -10,6 +10,12 @@ description: |-
Provides a resource to create a VPC routing table.
~> **NOTE on Route Tables and Routes:** Terraform currently
provides both a standalone [Route resource](route.html) and a Route Table resource with routes
defined in-line. At this time you cannot use a Route Table with in-line routes
in conjunction with any Route resources. Doing so will cause
a conflict of rule settings and will overwrite rules.
## Example usage with tags:
```