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