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