github.com/chalford/terraform@v0.3.7-0.20150113080010-a78c69a8c81f/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 if r.Origin == "EnableVgwRoutePropagation" { 120 continue 121 } 122 123 m := make(map[string]interface{}) 124 m["cidr_block"] = r.DestinationCidrBlock 125 126 m["gateway_id"] = r.GatewayId 127 m["instance_id"] = r.InstanceId 128 129 route.Add(m) 130 } 131 d.Set("route", route) 132 133 // Tags 134 d.Set("tags", tagsToMap(rt.Tags)) 135 136 return nil 137 } 138 139 func resourceAwsRouteTableUpdate(d *schema.ResourceData, meta interface{}) error { 140 ec2conn := meta.(*AWSClient).ec2conn 141 142 // Check if the route set as a whole has changed 143 if d.HasChange("route") { 144 o, n := d.GetChange("route") 145 ors := o.(*schema.Set).Difference(n.(*schema.Set)) 146 nrs := n.(*schema.Set).Difference(o.(*schema.Set)) 147 148 // Now first loop through all the old routes and delete any obsolete ones 149 for _, route := range ors.List() { 150 m := route.(map[string]interface{}) 151 152 // Delete the route as it no longer exists in the config 153 _, err := ec2conn.DeleteRoute( 154 d.Id(), m["cidr_block"].(string)) 155 if err != nil { 156 return err 157 } 158 } 159 160 // Make sure we save the state of the currently configured rules 161 routes := o.(*schema.Set).Intersection(n.(*schema.Set)) 162 d.Set("route", routes) 163 164 // Then loop through al the newly configured routes and create them 165 for _, route := range nrs.List() { 166 m := route.(map[string]interface{}) 167 168 opts := ec2.CreateRoute{ 169 RouteTableId: d.Id(), 170 DestinationCidrBlock: m["cidr_block"].(string), 171 GatewayId: m["gateway_id"].(string), 172 InstanceId: m["instance_id"].(string), 173 } 174 175 _, err := ec2conn.CreateRoute(&opts) 176 if err != nil { 177 return err 178 } 179 180 routes.Add(route) 181 d.Set("route", routes) 182 } 183 } 184 185 if err := setTags(ec2conn, d); err != nil { 186 return err 187 } else { 188 d.SetPartial("tags") 189 } 190 191 return resourceAwsRouteTableRead(d, meta) 192 } 193 194 func resourceAwsRouteTableDelete(d *schema.ResourceData, meta interface{}) error { 195 ec2conn := meta.(*AWSClient).ec2conn 196 197 // First request the routing table since we'll have to disassociate 198 // all the subnets first. 199 rtRaw, _, err := resourceAwsRouteTableStateRefreshFunc(ec2conn, d.Id())() 200 if err != nil { 201 return err 202 } 203 if rtRaw == nil { 204 return nil 205 } 206 rt := rtRaw.(*ec2.RouteTable) 207 208 // Do all the disassociations 209 for _, a := range rt.Associations { 210 log.Printf("[INFO] Disassociating association: %s", a.AssociationId) 211 if _, err := ec2conn.DisassociateRouteTable(a.AssociationId); err != nil { 212 return err 213 } 214 } 215 216 // Delete the route table 217 log.Printf("[INFO] Deleting Route Table: %s", d.Id()) 218 if _, err := ec2conn.DeleteRouteTable(d.Id()); err != nil { 219 ec2err, ok := err.(*ec2.Error) 220 if ok && ec2err.Code == "InvalidRouteTableID.NotFound" { 221 return nil 222 } 223 224 return fmt.Errorf("Error deleting route table: %s", err) 225 } 226 227 // Wait for the route table to really destroy 228 log.Printf( 229 "[DEBUG] Waiting for route table (%s) to become destroyed", 230 d.Id()) 231 232 stateConf := &resource.StateChangeConf{ 233 Pending: []string{"ready"}, 234 Target: "", 235 Refresh: resourceAwsRouteTableStateRefreshFunc(ec2conn, d.Id()), 236 Timeout: 1 * time.Minute, 237 } 238 if _, err := stateConf.WaitForState(); err != nil { 239 return fmt.Errorf( 240 "Error waiting for route table (%s) to become destroyed: %s", 241 d.Id(), err) 242 } 243 244 return nil 245 } 246 247 func resourceAwsRouteTableHash(v interface{}) int { 248 var buf bytes.Buffer 249 m := v.(map[string]interface{}) 250 buf.WriteString(fmt.Sprintf("%s-", m["cidr_block"].(string))) 251 252 if v, ok := m["gateway_id"]; ok { 253 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 254 } 255 256 if v, ok := m["instance_id"]; ok { 257 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 258 } 259 260 return hashcode.String(buf.String()) 261 } 262 263 // resourceAwsRouteTableStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch 264 // a RouteTable. 265 func resourceAwsRouteTableStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { 266 return func() (interface{}, string, error) { 267 resp, err := conn.DescribeRouteTables([]string{id}, ec2.NewFilter()) 268 if err != nil { 269 if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidRouteTableID.NotFound" { 270 resp = nil 271 } else { 272 log.Printf("Error on RouteTableStateRefresh: %s", err) 273 return nil, "", err 274 } 275 } 276 277 if resp == nil { 278 // Sometimes AWS just has consistency issues and doesn't see 279 // our instance yet. Return an empty state. 280 return nil, "", nil 281 } 282 283 rt := &resp.RouteTables[0] 284 return rt, "ready", nil 285 } 286 }