Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

provider/aws: Add aws_default_route_table resource #8323

Merged
merged 8 commits into from
Aug 25, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions builtin/providers/aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ func Provider() terraform.ResourceProvider {
"aws_nat_gateway": resourceAwsNatGateway(),
"aws_network_acl": resourceAwsNetworkAcl(),
"aws_default_network_acl": resourceAwsDefaultNetworkAcl(),
"aws_default_route_table": resourceAwsDefaultRouteTable(),
"aws_network_acl_rule": resourceAwsNetworkAclRule(),
"aws_network_interface": resourceAwsNetworkInterface(),
"aws_opsworks_application": resourceAwsOpsworksApplication(),
Expand Down
204 changes: 204 additions & 0 deletions builtin/providers/aws/resource_aws_default_route_table.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
package aws

import (
"fmt"
"log"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsDefaultRouteTable() *schema.Resource {
return &schema.Resource{
Create: resourceAwsDefaultRouteTableCreate,
Read: resourceAwsDefaultRouteTableRead,
Update: resourceAwsRouteTableUpdate,
Delete: resourceAwsDefaultRouteTableDelete,

Schema: map[string]*schema.Schema{
"default_route_table_id": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"vpc_id": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},

"propagating_vgws": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},

"route": &schema.Schema{
Type: schema.TypeSet,
Computed: true,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"cidr_block": &schema.Schema{
Type: schema.TypeString,
Required: true,
},

"gateway_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},

"instance_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},

"nat_gateway_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},

"vpc_peering_connection_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},

"network_interface_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
},
},
Set: resourceAwsRouteTableHash,
},

"tags": tagsSchema(),
},
}
}

func resourceAwsDefaultRouteTableCreate(d *schema.ResourceData, meta interface{}) error {
d.SetId(d.Get("default_route_table_id").(string))

conn := meta.(*AWSClient).ec2conn
rtRaw, _, err := resourceAwsRouteTableStateRefreshFunc(conn, d.Id())()
if err != nil {
return err
}
if rtRaw == nil {
log.Printf("[WARN] Default Route Table not found")
d.SetId("")
return nil
}

rt := rtRaw.(*ec2.RouteTable)

d.Set("vpc_id", rt.VpcId)

// revoke all default and pre-existing routes on the default route table.
// In the UPDATE method, we'll apply only the rules in the configuration.
log.Printf("[DEBUG] Revoking default routes for Default Route Table for %s", d.Id())
if err := revokeAllRouteTableRules(d.Id(), meta); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a potentially for this to cause issues if the revoke happens and the update fails?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to revoke the rules regardless if there is any update to be made. The procedure is that during "creation" we wipe the slate clean. This is documented to warn people .

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

return err
}

return resourceAwsRouteTableUpdate(d, meta)
}

func resourceAwsDefaultRouteTableRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn
// look up default route table for VPC
filter1 := &ec2.Filter{
Name: aws.String("association.main"),
Values: []*string{aws.String("true")},
}
filter2 := &ec2.Filter{
Name: aws.String("vpc-id"),
Values: []*string{aws.String(d.Get("vpc_id").(string))},
}

findOpts := &ec2.DescribeRouteTablesInput{
Filters: []*ec2.Filter{filter1, filter2},
}

resp, err := conn.DescribeRouteTables(findOpts)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it not be best to use d.Id here as part of the lookup?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on read we need to find the default route table for this VPC. Unlike Default Network ACLs or Default Security Groups, the Default Route Table can be changed (you can make a new one and then make that "default"). So we need to find if we are the default, and then refresh and show a diff (if there is one) accordingly.

if err != nil {
return err
}

if len(resp.RouteTables) < 1 || resp.RouteTables[0] == nil {
return fmt.Errorf("Default Route table not found")
}

rt := resp.RouteTables[0]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was being stupid here... comment revoked ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eh


d.Set("default_route_table_id", rt.RouteTableId)
d.SetId(*rt.RouteTableId)

// re-use regular AWS Route Table READ. This is an extra API call but saves us
// from trying to manually keep parity
return resourceAwsRouteTableRead(d, meta)
}

func resourceAwsDefaultRouteTableDelete(d *schema.ResourceData, meta interface{}) error {
log.Printf("[WARN] Cannot destroy Default Route Table. Terraform will remove this resource from the state file, however resources may remain.")
d.SetId("")
return nil
}

// revokeAllRouteTableRules revoke all routes on the Default Route Table
// This should only be ran once at creation time of this resource
func revokeAllRouteTableRules(defaultRouteTableId string, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn
log.Printf("\n***\nrevokeAllRouteTableRules\n***\n")

resp, err := conn.DescribeRouteTables(&ec2.DescribeRouteTablesInput{
RouteTableIds: []*string{aws.String(defaultRouteTableId)},
})
if err != nil {
return err
}

if len(resp.RouteTables) < 1 || resp.RouteTables[0] == nil {
return fmt.Errorf("Default Route table not found")
}

rt := resp.RouteTables[0]

// Remove all Gateway association
for _, r := range rt.PropagatingVgws {
log.Printf(
"[INFO] Deleting VGW propagation from %s: %s",
defaultRouteTableId, *r.GatewayId)
_, err := conn.DisableVgwRoutePropagation(&ec2.DisableVgwRoutePropagationInput{
RouteTableId: aws.String(defaultRouteTableId),
GatewayId: r.GatewayId,
})
if err != nil {
return err
}
}

// Delete all routes
for _, r := range rt.Routes {
// you cannot delete the local route
if r.GatewayId != nil && *r.GatewayId == "local" {
continue
}
log.Printf(
"[INFO] Deleting route from %s: %s",
defaultRouteTableId, *r.DestinationCidrBlock)
_, err := conn.DeleteRoute(&ec2.DeleteRouteInput{
RouteTableId: aws.String(defaultRouteTableId),
DestinationCidrBlock: r.DestinationCidrBlock,
})
if err != nil {
return err
}
}

return nil
}
Loading