github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/builtin/providers/google/resource_compute_vpn_tunnel.go (about)

     1  package google
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"net"
     7  	"strings"
     8  
     9  	"github.com/hashicorp/terraform/helper/schema"
    10  
    11  	"google.golang.org/api/compute/v1"
    12  )
    13  
    14  func resourceComputeVpnTunnel() *schema.Resource {
    15  	return &schema.Resource{
    16  		// Unfortunately, the VPNTunnelService does not support update
    17  		// operations. This is why everything is marked forcenew
    18  		Create: resourceComputeVpnTunnelCreate,
    19  		Read:   resourceComputeVpnTunnelRead,
    20  		Delete: resourceComputeVpnTunnelDelete,
    21  
    22  		Schema: map[string]*schema.Schema{
    23  			"name": &schema.Schema{
    24  				Type:     schema.TypeString,
    25  				Required: true,
    26  				ForceNew: true,
    27  			},
    28  
    29  			"peer_ip": &schema.Schema{
    30  				Type:         schema.TypeString,
    31  				Required:     true,
    32  				ForceNew:     true,
    33  				ValidateFunc: validatePeerAddr,
    34  			},
    35  
    36  			"shared_secret": &schema.Schema{
    37  				Type:     schema.TypeString,
    38  				Required: true,
    39  				ForceNew: true,
    40  			},
    41  
    42  			"target_vpn_gateway": &schema.Schema{
    43  				Type:     schema.TypeString,
    44  				Required: true,
    45  				ForceNew: true,
    46  			},
    47  
    48  			"description": &schema.Schema{
    49  				Type:     schema.TypeString,
    50  				Optional: true,
    51  				ForceNew: true,
    52  			},
    53  
    54  			"detailed_status": &schema.Schema{
    55  				Type:     schema.TypeString,
    56  				Computed: true,
    57  			},
    58  
    59  			"ike_version": &schema.Schema{
    60  				Type:     schema.TypeInt,
    61  				Optional: true,
    62  				Default:  2,
    63  				ForceNew: true,
    64  			},
    65  
    66  			"local_traffic_selector": &schema.Schema{
    67  				Type:     schema.TypeSet,
    68  				Optional: true,
    69  				ForceNew: true,
    70  				Computed: true,
    71  				Elem:     &schema.Schema{Type: schema.TypeString},
    72  				Set:      schema.HashString,
    73  			},
    74  
    75  			"remote_traffic_selector": &schema.Schema{
    76  				Type:     schema.TypeSet,
    77  				Optional: true,
    78  				ForceNew: true,
    79  				Computed: true,
    80  				Elem:     &schema.Schema{Type: schema.TypeString},
    81  				Set:      schema.HashString,
    82  			},
    83  
    84  			"project": &schema.Schema{
    85  				Type:     schema.TypeString,
    86  				Optional: true,
    87  				ForceNew: true,
    88  			},
    89  
    90  			"region": &schema.Schema{
    91  				Type:     schema.TypeString,
    92  				Optional: true,
    93  				ForceNew: true,
    94  			},
    95  
    96  			"router": &schema.Schema{
    97  				Type:     schema.TypeString,
    98  				Optional: true,
    99  				ForceNew: true,
   100  			},
   101  
   102  			"self_link": &schema.Schema{
   103  				Type:     schema.TypeString,
   104  				Computed: true,
   105  			},
   106  		},
   107  	}
   108  }
   109  
   110  func resourceComputeVpnTunnelCreate(d *schema.ResourceData, meta interface{}) error {
   111  	config := meta.(*Config)
   112  
   113  	region, err := getRegion(d, config)
   114  	if err != nil {
   115  		return err
   116  	}
   117  
   118  	project, err := getProject(d, config)
   119  	if err != nil {
   120  		return err
   121  	}
   122  
   123  	name := d.Get("name").(string)
   124  	peerIp := d.Get("peer_ip").(string)
   125  	sharedSecret := d.Get("shared_secret").(string)
   126  	targetVpnGateway := d.Get("target_vpn_gateway").(string)
   127  	ikeVersion := d.Get("ike_version").(int)
   128  
   129  	if ikeVersion < 1 || ikeVersion > 2 {
   130  		return fmt.Errorf("Only IKE version 1 or 2 supported, not %d", ikeVersion)
   131  	}
   132  
   133  	// Build up the list of sources
   134  	var localTrafficSelectors []string
   135  	if v := d.Get("local_traffic_selector").(*schema.Set); v.Len() > 0 {
   136  		localTrafficSelectors = make([]string, v.Len())
   137  		for i, v := range v.List() {
   138  			localTrafficSelectors[i] = v.(string)
   139  		}
   140  	}
   141  
   142  	var remoteTrafficSelectors []string
   143  	if v := d.Get("remote_traffic_selector").(*schema.Set); v.Len() > 0 {
   144  		remoteTrafficSelectors = make([]string, v.Len())
   145  		for i, v := range v.List() {
   146  			remoteTrafficSelectors[i] = v.(string)
   147  		}
   148  	}
   149  
   150  	vpnTunnelsService := compute.NewVpnTunnelsService(config.clientCompute)
   151  
   152  	vpnTunnel := &compute.VpnTunnel{
   153  		Name:                  name,
   154  		PeerIp:                peerIp,
   155  		SharedSecret:          sharedSecret,
   156  		TargetVpnGateway:      targetVpnGateway,
   157  		IkeVersion:            int64(ikeVersion),
   158  		LocalTrafficSelector:  localTrafficSelectors,
   159  		RemoteTrafficSelector: remoteTrafficSelectors,
   160  	}
   161  
   162  	if v, ok := d.GetOk("description"); ok {
   163  		vpnTunnel.Description = v.(string)
   164  	}
   165  
   166  	if v, ok := d.GetOk("router"); ok {
   167  		routerLink, err := getRouterLink(config, project, region, v.(string))
   168  		if err != nil {
   169  			return err
   170  		}
   171  		vpnTunnel.Router = routerLink
   172  	}
   173  
   174  	op, err := vpnTunnelsService.Insert(project, region, vpnTunnel).Do()
   175  	if err != nil {
   176  		return fmt.Errorf("Error Inserting VPN Tunnel %s : %s", name, err)
   177  	}
   178  
   179  	err = computeOperationWaitRegion(config, op, project, region, "Inserting VPN Tunnel")
   180  	if err != nil {
   181  		return fmt.Errorf("Error Waiting to Insert VPN Tunnel %s: %s", name, err)
   182  	}
   183  
   184  	return resourceComputeVpnTunnelRead(d, meta)
   185  }
   186  
   187  func resourceComputeVpnTunnelRead(d *schema.ResourceData, meta interface{}) error {
   188  	config := meta.(*Config)
   189  
   190  	region, err := getRegion(d, config)
   191  	if err != nil {
   192  		return err
   193  	}
   194  
   195  	project, err := getProject(d, config)
   196  	if err != nil {
   197  		return err
   198  	}
   199  
   200  	name := d.Get("name").(string)
   201  
   202  	vpnTunnelsService := compute.NewVpnTunnelsService(config.clientCompute)
   203  
   204  	vpnTunnel, err := vpnTunnelsService.Get(project, region, name).Do()
   205  	if err != nil {
   206  		return handleNotFoundError(err, d, fmt.Sprintf("VPN Tunnel %q", d.Get("name").(string)))
   207  	}
   208  
   209  	localTrafficSelectors := []string{}
   210  	for _, lts := range vpnTunnel.LocalTrafficSelector {
   211  		localTrafficSelectors = append(localTrafficSelectors, lts)
   212  	}
   213  	d.Set("local_traffic_selector", localTrafficSelectors)
   214  
   215  	remoteTrafficSelectors := []string{}
   216  	for _, rts := range vpnTunnel.RemoteTrafficSelector {
   217  		remoteTrafficSelectors = append(remoteTrafficSelectors, rts)
   218  	}
   219  	d.Set("remote_traffic_selector", remoteTrafficSelectors)
   220  
   221  	d.Set("detailed_status", vpnTunnel.DetailedStatus)
   222  	d.Set("self_link", vpnTunnel.SelfLink)
   223  
   224  	d.SetId(name)
   225  
   226  	return nil
   227  }
   228  
   229  func resourceComputeVpnTunnelDelete(d *schema.ResourceData, meta interface{}) error {
   230  	config := meta.(*Config)
   231  
   232  	region, err := getRegion(d, config)
   233  	if err != nil {
   234  		return err
   235  	}
   236  
   237  	project, err := getProject(d, config)
   238  	if err != nil {
   239  		return err
   240  	}
   241  
   242  	name := d.Get("name").(string)
   243  
   244  	vpnTunnelsService := compute.NewVpnTunnelsService(config.clientCompute)
   245  
   246  	op, err := vpnTunnelsService.Delete(project, region, name).Do()
   247  	if err != nil {
   248  		return fmt.Errorf("Error Reading VPN Tunnel %s: %s", name, err)
   249  	}
   250  
   251  	err = computeOperationWaitRegion(config, op, project, region, "Deleting VPN Tunnel")
   252  	if err != nil {
   253  		return fmt.Errorf("Error Waiting to Delete VPN Tunnel %s: %s", name, err)
   254  	}
   255  
   256  	return nil
   257  }
   258  
   259  // validatePeerAddr returns false if a tunnel's peer_ip property
   260  // is invalid. Currently, only addresses that collide with RFC
   261  // 5735 (https://tools.ietf.org/html/rfc5735) fail validation.
   262  func validatePeerAddr(i interface{}, val string) ([]string, []error) {
   263  	ip := net.ParseIP(i.(string))
   264  	if ip == nil {
   265  		return nil, []error{fmt.Errorf("could not parse %q to IP address", val)}
   266  	}
   267  	for _, test := range invalidPeerAddrs {
   268  		if bytes.Compare(ip, test.from) >= 0 && bytes.Compare(ip, test.to) <= 0 {
   269  			return nil, []error{fmt.Errorf("address is invalid (is between %q and %q, conflicting with RFC5735)", test.from, test.to)}
   270  		}
   271  	}
   272  	return nil, nil
   273  }
   274  
   275  // invalidPeerAddrs is a collection of IP addres ranges that represent
   276  // a conflict with RFC 5735 (https://tools.ietf.org/html/rfc5735#page-3).
   277  // CIDR range notations in the RFC were converted to a (from, to) pair
   278  // for easy checking with bytes.Compare.
   279  var invalidPeerAddrs = []struct {
   280  	from net.IP
   281  	to   net.IP
   282  }{
   283  	{
   284  		from: net.ParseIP("0.0.0.0"),
   285  		to:   net.ParseIP("0.255.255.255"),
   286  	},
   287  	{
   288  		from: net.ParseIP("10.0.0.0"),
   289  		to:   net.ParseIP("10.255.255.255"),
   290  	},
   291  	{
   292  		from: net.ParseIP("127.0.0.0"),
   293  		to:   net.ParseIP("127.255.255.255"),
   294  	},
   295  	{
   296  		from: net.ParseIP("169.254.0.0"),
   297  		to:   net.ParseIP("169.254.255.255"),
   298  	},
   299  	{
   300  		from: net.ParseIP("172.16.0.0"),
   301  		to:   net.ParseIP("172.31.255.255"),
   302  	},
   303  	{
   304  		from: net.ParseIP("192.0.0.0"),
   305  		to:   net.ParseIP("192.0.0.255"),
   306  	},
   307  	{
   308  		from: net.ParseIP("192.0.2.0"),
   309  		to:   net.ParseIP("192.0.2.255"),
   310  	},
   311  	{
   312  		from: net.ParseIP("192.88.99.0"),
   313  		to:   net.ParseIP("192.88.99.255"),
   314  	},
   315  	{
   316  		from: net.ParseIP("192.168.0.0"),
   317  		to:   net.ParseIP("192.168.255.255"),
   318  	},
   319  	{
   320  		from: net.ParseIP("198.18.0.0"),
   321  		to:   net.ParseIP("198.19.255.255"),
   322  	},
   323  	{
   324  		from: net.ParseIP("198.51.100.0"),
   325  		to:   net.ParseIP("198.51.100.255"),
   326  	},
   327  	{
   328  		from: net.ParseIP("203.0.113.0"),
   329  		to:   net.ParseIP("203.0.113.255"),
   330  	},
   331  	{
   332  		from: net.ParseIP("224.0.0.0"),
   333  		to:   net.ParseIP("239.255.255.255"),
   334  	},
   335  	{
   336  		from: net.ParseIP("240.0.0.0"),
   337  		to:   net.ParseIP("255.255.255.255"),
   338  	},
   339  	{
   340  		from: net.ParseIP("255.255.255.255"),
   341  		to:   net.ParseIP("255.255.255.255"),
   342  	},
   343  }
   344  
   345  func getVpnTunnelLink(config *Config, project string, region string, tunnel string) (string, error) {
   346  
   347  	if !strings.HasPrefix(tunnel, "https://www.googleapis.com/compute/") {
   348  		// Tunnel value provided is just the name, lookup the tunnel SelfLink
   349  		tunnelData, err := config.clientCompute.VpnTunnels.Get(
   350  			project, region, tunnel).Do()
   351  		if err != nil {
   352  			return "", fmt.Errorf("Error reading tunnel: %s", err)
   353  		}
   354  		tunnel = tunnelData.SelfLink
   355  	}
   356  
   357  	return tunnel, nil
   358  
   359  }
   360  
   361  func getVpnTunnelName(vpntunnel string) (string, error) {
   362  
   363  	if strings.HasPrefix(vpntunnel, "https://www.googleapis.com/compute/") {
   364  		// extract the VPN tunnel name from SelfLink URL
   365  		vpntunnelName := vpntunnel[strings.LastIndex(vpntunnel, "/")+1:]
   366  		if vpntunnelName == "" {
   367  			return "", fmt.Errorf("VPN tunnel url not valid")
   368  		}
   369  		return vpntunnelName, nil
   370  	}
   371  
   372  	return vpntunnel, nil
   373  }