github.com/tarrant/terraform@v0.3.8-0.20150402012457-f68c9eee638e/builtin/providers/aws/resource_aws_route_table.go (about) 1 package aws 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "time" 8 9 "github.com/hashicorp/aws-sdk-go/aws" 10 "github.com/hashicorp/aws-sdk-go/gen/ec2" 11 "github.com/hashicorp/terraform/helper/hashcode" 12 "github.com/hashicorp/terraform/helper/resource" 13 "github.com/hashicorp/terraform/helper/schema" 14 ) 15 16 func resourceAwsRouteTable() *schema.Resource { 17 return &schema.Resource{ 18 Create: resourceAwsRouteTableCreate, 19 Read: resourceAwsRouteTableRead, 20 Update: resourceAwsRouteTableUpdate, 21 Delete: resourceAwsRouteTableDelete, 22 23 Schema: map[string]*schema.Schema{ 24 "vpc_id": &schema.Schema{ 25 Type: schema.TypeString, 26 Required: true, 27 ForceNew: true, 28 }, 29 30 "tags": tagsSchema(), 31 32 "route": &schema.Schema{ 33 Type: schema.TypeSet, 34 Optional: true, 35 Elem: &schema.Resource{ 36 Schema: map[string]*schema.Schema{ 37 "cidr_block": &schema.Schema{ 38 Type: schema.TypeString, 39 Required: true, 40 }, 41 42 "gateway_id": &schema.Schema{ 43 Type: schema.TypeString, 44 Optional: true, 45 }, 46 47 "instance_id": &schema.Schema{ 48 Type: schema.TypeString, 49 Optional: true, 50 }, 51 52 "vpc_peering_connection_id": &schema.Schema{ 53 Type: schema.TypeString, 54 Optional: true, 55 }, 56 }, 57 }, 58 Set: resourceAwsRouteTableHash, 59 }, 60 }, 61 } 62 } 63 64 func resourceAwsRouteTableCreate(d *schema.ResourceData, meta interface{}) error { 65 ec2conn := meta.(*AWSClient).ec2conn 66 67 // Create the routing table 68 createOpts := &ec2.CreateRouteTableRequest{ 69 VPCID: aws.String(d.Get("vpc_id").(string)), 70 } 71 log.Printf("[DEBUG] RouteTable create config: %#v", createOpts) 72 73 resp, err := ec2conn.CreateRouteTable(createOpts) 74 if err != nil { 75 return fmt.Errorf("Error creating route table: %s", err) 76 } 77 78 // Get the ID and store it 79 rt := resp.RouteTable 80 d.SetId(*rt.RouteTableID) 81 log.Printf("[INFO] Route Table ID: %s", d.Id()) 82 83 // Wait for the route table to become available 84 log.Printf( 85 "[DEBUG] Waiting for route table (%s) to become available", 86 d.Id()) 87 stateConf := &resource.StateChangeConf{ 88 Pending: []string{"pending"}, 89 Target: "ready", 90 Refresh: resourceAwsRouteTableStateRefreshFunc(ec2conn, d.Id()), 91 Timeout: 1 * time.Minute, 92 } 93 if _, err := stateConf.WaitForState(); err != nil { 94 return fmt.Errorf( 95 "Error waiting for route table (%s) to become available: %s", 96 d.Id(), err) 97 } 98 99 return resourceAwsRouteTableUpdate(d, meta) 100 } 101 102 func resourceAwsRouteTableRead(d *schema.ResourceData, meta interface{}) error { 103 ec2conn := meta.(*AWSClient).ec2conn 104 105 rtRaw, _, err := resourceAwsRouteTableStateRefreshFunc(ec2conn, d.Id())() 106 if err != nil { 107 return err 108 } 109 if rtRaw == nil { 110 d.SetId("") 111 return nil 112 } 113 114 rt := rtRaw.(*ec2.RouteTable) 115 d.Set("vpc_id", rt.VPCID) 116 117 // Create an empty schema.Set to hold all routes 118 route := &schema.Set{F: resourceAwsRouteTableHash} 119 120 // Loop through the routes and add them to the set 121 for _, r := range rt.Routes { 122 if r.GatewayID != nil && *r.GatewayID == "local" { 123 continue 124 } 125 126 if r.Origin != nil && *r.Origin == "EnableVgwRoutePropagation" { 127 continue 128 } 129 130 m := make(map[string]interface{}) 131 132 if r.DestinationCIDRBlock != nil { 133 m["cidr_block"] = *r.DestinationCIDRBlock 134 } 135 if r.GatewayID != nil { 136 m["gateway_id"] = *r.GatewayID 137 } 138 if r.InstanceID != nil { 139 m["instance_id"] = *r.InstanceID 140 } 141 if r.VPCPeeringConnectionID != nil { 142 m["vpc_peering_connection_id"] = *r.VPCPeeringConnectionID 143 } 144 145 route.Add(m) 146 } 147 d.Set("route", route) 148 149 // Tags 150 d.Set("tags", tagsToMap(rt.Tags)) 151 152 return nil 153 } 154 155 func resourceAwsRouteTableUpdate(d *schema.ResourceData, meta interface{}) error { 156 ec2conn := meta.(*AWSClient).ec2conn 157 158 // Check if the route set as a whole has changed 159 if d.HasChange("route") { 160 o, n := d.GetChange("route") 161 ors := o.(*schema.Set).Difference(n.(*schema.Set)) 162 nrs := n.(*schema.Set).Difference(o.(*schema.Set)) 163 164 // Now first loop through all the old routes and delete any obsolete ones 165 for _, route := range ors.List() { 166 m := route.(map[string]interface{}) 167 168 // Delete the route as it no longer exists in the config 169 log.Printf( 170 "[INFO] Deleting route from %s: %s", 171 d.Id(), m["cidr_block"].(string)) 172 err := ec2conn.DeleteRoute(&ec2.DeleteRouteRequest{ 173 RouteTableID: aws.String(d.Id()), 174 DestinationCIDRBlock: aws.String(m["cidr_block"].(string)), 175 }) 176 if err != nil { 177 return err 178 } 179 } 180 181 // Make sure we save the state of the currently configured rules 182 routes := o.(*schema.Set).Intersection(n.(*schema.Set)) 183 d.Set("route", routes) 184 185 // Then loop through al the newly configured routes and create them 186 for _, route := range nrs.List() { 187 m := route.(map[string]interface{}) 188 189 opts := ec2.CreateRouteRequest{ 190 RouteTableID: aws.String(d.Id()), 191 DestinationCIDRBlock: aws.String(m["cidr_block"].(string)), 192 GatewayID: aws.String(m["gateway_id"].(string)), 193 InstanceID: aws.String(m["instance_id"].(string)), 194 VPCPeeringConnectionID: aws.String(m["vpc_peering_connection_id"].(string)), 195 } 196 197 log.Printf("[INFO] Creating route for %s: %#v", d.Id(), opts) 198 if err := ec2conn.CreateRoute(&opts); err != nil { 199 return err 200 } 201 202 routes.Add(route) 203 d.Set("route", routes) 204 } 205 } 206 207 if err := setTags(ec2conn, d); err != nil { 208 return err 209 } else { 210 d.SetPartial("tags") 211 } 212 213 return resourceAwsRouteTableRead(d, meta) 214 } 215 216 func resourceAwsRouteTableDelete(d *schema.ResourceData, meta interface{}) error { 217 ec2conn := meta.(*AWSClient).ec2conn 218 219 // First request the routing table since we'll have to disassociate 220 // all the subnets first. 221 rtRaw, _, err := resourceAwsRouteTableStateRefreshFunc(ec2conn, d.Id())() 222 if err != nil { 223 return err 224 } 225 if rtRaw == nil { 226 return nil 227 } 228 rt := rtRaw.(*ec2.RouteTable) 229 230 // Do all the disassociations 231 for _, a := range rt.Associations { 232 log.Printf("[INFO] Disassociating association: %s", *a.RouteTableAssociationID) 233 err := ec2conn.DisassociateRouteTable(&ec2.DisassociateRouteTableRequest{ 234 AssociationID: a.RouteTableAssociationID, 235 }) 236 if err != nil { 237 return err 238 } 239 } 240 241 // Delete the route table 242 log.Printf("[INFO] Deleting Route Table: %s", d.Id()) 243 err = ec2conn.DeleteRouteTable(&ec2.DeleteRouteTableRequest{ 244 RouteTableID: aws.String(d.Id()), 245 }) 246 if err != nil { 247 ec2err, ok := err.(aws.APIError) 248 if ok && ec2err.Code == "InvalidRouteTableID.NotFound" { 249 return nil 250 } 251 252 return fmt.Errorf("Error deleting route table: %s", err) 253 } 254 255 // Wait for the route table to really destroy 256 log.Printf( 257 "[DEBUG] Waiting for route table (%s) to become destroyed", 258 d.Id()) 259 260 stateConf := &resource.StateChangeConf{ 261 Pending: []string{"ready"}, 262 Target: "", 263 Refresh: resourceAwsRouteTableStateRefreshFunc(ec2conn, d.Id()), 264 Timeout: 1 * time.Minute, 265 } 266 if _, err := stateConf.WaitForState(); err != nil { 267 return fmt.Errorf( 268 "Error waiting for route table (%s) to become destroyed: %s", 269 d.Id(), err) 270 } 271 272 return nil 273 } 274 275 func resourceAwsRouteTableHash(v interface{}) int { 276 var buf bytes.Buffer 277 m := v.(map[string]interface{}) 278 buf.WriteString(fmt.Sprintf("%s-", m["cidr_block"].(string))) 279 280 if v, ok := m["gateway_id"]; ok { 281 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 282 } 283 284 if v, ok := m["instance_id"]; ok { 285 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 286 } 287 288 if v, ok := m["vpc_peering_connection_id"]; ok { 289 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 290 } 291 292 return hashcode.String(buf.String()) 293 } 294 295 // resourceAwsRouteTableStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch 296 // a RouteTable. 297 func resourceAwsRouteTableStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { 298 return func() (interface{}, string, error) { 299 resp, err := conn.DescribeRouteTables(&ec2.DescribeRouteTablesRequest{ 300 RouteTableIDs: []string{id}, 301 }) 302 if err != nil { 303 if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "InvalidRouteTableID.NotFound" { 304 resp = nil 305 } else { 306 log.Printf("Error on RouteTableStateRefresh: %s", err) 307 return nil, "", err 308 } 309 } 310 311 if resp == nil { 312 // Sometimes AWS just has consistency issues and doesn't see 313 // our instance yet. Return an empty state. 314 return nil, "", nil 315 } 316 317 rt := &resp.RouteTables[0] 318 return rt, "ready", nil 319 } 320 }