github.com/andresvia/terraform@v0.6.15-0.20160412045437-d51c75946785/builtin/providers/aws/resource_aws_route.go (about)

     1  package aws
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"log"
     7  
     8  	"github.com/aws/aws-sdk-go/aws"
     9  	"github.com/aws/aws-sdk-go/service/ec2"
    10  	"github.com/hashicorp/terraform/helper/hashcode"
    11  	"github.com/hashicorp/terraform/helper/schema"
    12  )
    13  
    14  // How long to sleep if a limit-exceeded event happens
    15  var routeTargetValidationError = errors.New("Error: more than 1 target specified. Only 1 of gateway_id" +
    16  	"nat_gateway_id, instance_id, network_interface_id, route_table_id or" +
    17  	"vpc_peering_connection_id is allowed.")
    18  
    19  // AWS Route resource Schema declaration
    20  func resourceAwsRoute() *schema.Resource {
    21  	return &schema.Resource{
    22  		Create: resourceAwsRouteCreate,
    23  		Read:   resourceAwsRouteRead,
    24  		Update: resourceAwsRouteUpdate,
    25  		Delete: resourceAwsRouteDelete,
    26  		Exists: resourceAwsRouteExists,
    27  
    28  		Schema: map[string]*schema.Schema{
    29  			"destination_cidr_block": &schema.Schema{
    30  				Type:     schema.TypeString,
    31  				Required: true,
    32  				ForceNew: true,
    33  			},
    34  
    35  			"destination_prefix_list_id": &schema.Schema{
    36  				Type:     schema.TypeString,
    37  				Computed: true,
    38  			},
    39  
    40  			"gateway_id": &schema.Schema{
    41  				Type:     schema.TypeString,
    42  				Optional: true,
    43  				Computed: true,
    44  			},
    45  
    46  			"nat_gateway_id": &schema.Schema{
    47  				Type:     schema.TypeString,
    48  				Optional: true,
    49  				Computed: true,
    50  			},
    51  
    52  			"instance_id": &schema.Schema{
    53  				Type:     schema.TypeString,
    54  				Optional: true,
    55  				Computed: true,
    56  			},
    57  
    58  			"instance_owner_id": &schema.Schema{
    59  				Type:     schema.TypeString,
    60  				Computed: true,
    61  			},
    62  
    63  			"network_interface_id": &schema.Schema{
    64  				Type:     schema.TypeString,
    65  				Optional: true,
    66  				Computed: true,
    67  			},
    68  
    69  			"origin": &schema.Schema{
    70  				Type:     schema.TypeString,
    71  				Computed: true,
    72  			},
    73  
    74  			"state": &schema.Schema{
    75  				Type:     schema.TypeString,
    76  				Computed: true,
    77  			},
    78  
    79  			"route_table_id": &schema.Schema{
    80  				Type:     schema.TypeString,
    81  				Required: true,
    82  			},
    83  
    84  			"vpc_peering_connection_id": &schema.Schema{
    85  				Type:     schema.TypeString,
    86  				Optional: true,
    87  			},
    88  		},
    89  	}
    90  }
    91  
    92  func resourceAwsRouteCreate(d *schema.ResourceData, meta interface{}) error {
    93  	conn := meta.(*AWSClient).ec2conn
    94  	var numTargets int
    95  	var setTarget string
    96  	allowedTargets := []string{
    97  		"gateway_id",
    98  		"nat_gateway_id",
    99  		"instance_id",
   100  		"network_interface_id",
   101  		"vpc_peering_connection_id",
   102  	}
   103  
   104  	// Check if more than 1 target is specified
   105  	for _, target := range allowedTargets {
   106  		if len(d.Get(target).(string)) > 0 {
   107  			numTargets++
   108  			setTarget = target
   109  		}
   110  	}
   111  
   112  	if numTargets > 1 {
   113  		return routeTargetValidationError
   114  	}
   115  
   116  	createOpts := &ec2.CreateRouteInput{}
   117  	// Formulate CreateRouteInput based on the target type
   118  	switch setTarget {
   119  	case "gateway_id":
   120  		createOpts = &ec2.CreateRouteInput{
   121  			RouteTableId:         aws.String(d.Get("route_table_id").(string)),
   122  			DestinationCidrBlock: aws.String(d.Get("destination_cidr_block").(string)),
   123  			GatewayId:            aws.String(d.Get("gateway_id").(string)),
   124  		}
   125  	case "nat_gateway_id":
   126  		createOpts = &ec2.CreateRouteInput{
   127  			RouteTableId:         aws.String(d.Get("route_table_id").(string)),
   128  			DestinationCidrBlock: aws.String(d.Get("destination_cidr_block").(string)),
   129  			NatGatewayId:         aws.String(d.Get("nat_gateway_id").(string)),
   130  		}
   131  	case "instance_id":
   132  		createOpts = &ec2.CreateRouteInput{
   133  			RouteTableId:         aws.String(d.Get("route_table_id").(string)),
   134  			DestinationCidrBlock: aws.String(d.Get("destination_cidr_block").(string)),
   135  			InstanceId:           aws.String(d.Get("instance_id").(string)),
   136  		}
   137  	case "network_interface_id":
   138  		createOpts = &ec2.CreateRouteInput{
   139  			RouteTableId:         aws.String(d.Get("route_table_id").(string)),
   140  			DestinationCidrBlock: aws.String(d.Get("destination_cidr_block").(string)),
   141  			NetworkInterfaceId:   aws.String(d.Get("network_interface_id").(string)),
   142  		}
   143  	case "vpc_peering_connection_id":
   144  		createOpts = &ec2.CreateRouteInput{
   145  			RouteTableId:           aws.String(d.Get("route_table_id").(string)),
   146  			DestinationCidrBlock:   aws.String(d.Get("destination_cidr_block").(string)),
   147  			VpcPeeringConnectionId: aws.String(d.Get("vpc_peering_connection_id").(string)),
   148  		}
   149  	default:
   150  		return fmt.Errorf("Error: invalid target type specified.")
   151  	}
   152  	log.Printf("[DEBUG] Route create config: %s", createOpts)
   153  
   154  	// Create the route
   155  	_, err := conn.CreateRoute(createOpts)
   156  	if err != nil {
   157  		return fmt.Errorf("Error creating route: %s", err)
   158  	}
   159  
   160  	route, err := findResourceRoute(conn, d.Get("route_table_id").(string), d.Get("destination_cidr_block").(string))
   161  	if err != nil {
   162  		return err
   163  	}
   164  
   165  	d.SetId(routeIDHash(d, route))
   166  
   167  	return resourceAwsRouteRead(d, meta)
   168  }
   169  
   170  func resourceAwsRouteRead(d *schema.ResourceData, meta interface{}) error {
   171  	conn := meta.(*AWSClient).ec2conn
   172  	route, err := findResourceRoute(conn, d.Get("route_table_id").(string), d.Get("destination_cidr_block").(string))
   173  	if err != nil {
   174  		return err
   175  	}
   176  
   177  	d.Set("destination_prefix_list_id", route.DestinationPrefixListId)
   178  	d.Set("gateway_id", route.GatewayId)
   179  	d.Set("nat_gateway_id", route.NatGatewayId)
   180  	d.Set("instance_id", route.InstanceId)
   181  	d.Set("instance_owner_id", route.InstanceOwnerId)
   182  	d.Set("network_interface_id", route.NetworkInterfaceId)
   183  	d.Set("origin", route.Origin)
   184  	d.Set("state", route.State)
   185  	d.Set("vpc_peering_connection_id", route.VpcPeeringConnectionId)
   186  
   187  	return nil
   188  }
   189  
   190  func resourceAwsRouteUpdate(d *schema.ResourceData, meta interface{}) error {
   191  	conn := meta.(*AWSClient).ec2conn
   192  	var numTargets int
   193  	var setTarget string
   194  	allowedTargets := []string{
   195  		"gateway_id",
   196  		"nat_gateway_id",
   197  		"instance_id",
   198  		"network_interface_id",
   199  		"vpc_peering_connection_id",
   200  	}
   201  	replaceOpts := &ec2.ReplaceRouteInput{}
   202  
   203  	// Check if more than 1 target is specified
   204  	for _, target := range allowedTargets {
   205  		if len(d.Get(target).(string)) > 0 {
   206  			numTargets++
   207  			setTarget = target
   208  		}
   209  	}
   210  
   211  	if numTargets > 1 {
   212  		return routeTargetValidationError
   213  	}
   214  
   215  	// Formulate ReplaceRouteInput based on the target type
   216  	switch setTarget {
   217  	case "gateway_id":
   218  		replaceOpts = &ec2.ReplaceRouteInput{
   219  			RouteTableId:         aws.String(d.Get("route_table_id").(string)),
   220  			DestinationCidrBlock: aws.String(d.Get("destination_cidr_block").(string)),
   221  			GatewayId:            aws.String(d.Get("gateway_id").(string)),
   222  		}
   223  	case "nat_gateway_id":
   224  		replaceOpts = &ec2.ReplaceRouteInput{
   225  			RouteTableId:         aws.String(d.Get("route_table_id").(string)),
   226  			DestinationCidrBlock: aws.String(d.Get("destination_cidr_block").(string)),
   227  			NatGatewayId:         aws.String(d.Get("nat_gateway_id").(string)),
   228  		}
   229  	case "instance_id":
   230  		replaceOpts = &ec2.ReplaceRouteInput{
   231  			RouteTableId:         aws.String(d.Get("route_table_id").(string)),
   232  			DestinationCidrBlock: aws.String(d.Get("destination_cidr_block").(string)),
   233  			InstanceId:           aws.String(d.Get("instance_id").(string)),
   234  			//NOOP: Ensure we don't blow away network interface id that is set after instance is launched
   235  			NetworkInterfaceId: aws.String(d.Get("network_interface_id").(string)),
   236  		}
   237  	case "network_interface_id":
   238  		replaceOpts = &ec2.ReplaceRouteInput{
   239  			RouteTableId:         aws.String(d.Get("route_table_id").(string)),
   240  			DestinationCidrBlock: aws.String(d.Get("destination_cidr_block").(string)),
   241  			NetworkInterfaceId:   aws.String(d.Get("network_interface_id").(string)),
   242  		}
   243  	case "vpc_peering_connection_id":
   244  		replaceOpts = &ec2.ReplaceRouteInput{
   245  			RouteTableId:           aws.String(d.Get("route_table_id").(string)),
   246  			DestinationCidrBlock:   aws.String(d.Get("destination_cidr_block").(string)),
   247  			VpcPeeringConnectionId: aws.String(d.Get("vpc_peering_connection_id").(string)),
   248  		}
   249  	default:
   250  		return fmt.Errorf("Error: invalid target type specified.")
   251  	}
   252  	log.Printf("[DEBUG] Route replace config: %s", replaceOpts)
   253  
   254  	// Replace the route
   255  	_, err := conn.ReplaceRoute(replaceOpts)
   256  	if err != nil {
   257  		return err
   258  	}
   259  
   260  	return nil
   261  }
   262  
   263  func resourceAwsRouteDelete(d *schema.ResourceData, meta interface{}) error {
   264  	conn := meta.(*AWSClient).ec2conn
   265  
   266  	deleteOpts := &ec2.DeleteRouteInput{
   267  		RouteTableId:         aws.String(d.Get("route_table_id").(string)),
   268  		DestinationCidrBlock: aws.String(d.Get("destination_cidr_block").(string)),
   269  	}
   270  	log.Printf("[DEBUG] Route delete opts: %s", deleteOpts)
   271  
   272  	resp, err := conn.DeleteRoute(deleteOpts)
   273  	log.Printf("[DEBUG] Route delete result: %s", resp)
   274  	if err != nil {
   275  		return err
   276  	}
   277  
   278  	d.SetId("")
   279  	return nil
   280  }
   281  
   282  func resourceAwsRouteExists(d *schema.ResourceData, meta interface{}) (bool, error) {
   283  	conn := meta.(*AWSClient).ec2conn
   284  	routeTableId := d.Get("route_table_id").(string)
   285  
   286  	findOpts := &ec2.DescribeRouteTablesInput{
   287  		RouteTableIds: []*string{&routeTableId},
   288  	}
   289  
   290  	res, err := conn.DescribeRouteTables(findOpts)
   291  	if err != nil {
   292  		return false, fmt.Errorf("Error while checking if route exists: %s", err)
   293  	}
   294  
   295  	if len(res.RouteTables) < 1 || res.RouteTables[0] == nil {
   296  		log.Printf("[WARN] Route table %s is gone, so route does not exist.",
   297  			routeTableId)
   298  		return false, nil
   299  	}
   300  
   301  	cidr := d.Get("destination_cidr_block").(string)
   302  	for _, route := range (*res.RouteTables[0]).Routes {
   303  		if route.DestinationCidrBlock != nil && *route.DestinationCidrBlock == cidr {
   304  			return true, nil
   305  		}
   306  	}
   307  
   308  	return false, nil
   309  }
   310  
   311  // Create an ID for a route
   312  func routeIDHash(d *schema.ResourceData, r *ec2.Route) string {
   313  	return fmt.Sprintf("r-%s%d", d.Get("route_table_id").(string), hashcode.String(*r.DestinationCidrBlock))
   314  }
   315  
   316  // Helper: retrieve a route
   317  func findResourceRoute(conn *ec2.EC2, rtbid string, cidr string) (*ec2.Route, error) {
   318  	routeTableID := rtbid
   319  
   320  	findOpts := &ec2.DescribeRouteTablesInput{
   321  		RouteTableIds: []*string{&routeTableID},
   322  	}
   323  
   324  	resp, err := conn.DescribeRouteTables(findOpts)
   325  	if err != nil {
   326  		return nil, err
   327  	}
   328  
   329  	if len(resp.RouteTables) < 1 || resp.RouteTables[0] == nil {
   330  		return nil, fmt.Errorf("Route table %s is gone, so route does not exist.",
   331  			routeTableID)
   332  	}
   333  
   334  	for _, route := range (*resp.RouteTables[0]).Routes {
   335  		if *route.DestinationCidrBlock == cidr {
   336  			return route, nil
   337  		}
   338  	}
   339  
   340  	return nil, fmt.Errorf(`
   341  error finding matching route for Route table (%s) and destination CIDR block (%s)`,
   342  		rtbid, cidr)
   343  }