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