Merge pull request #12411 from drebes/cloud_router

provider/google: Cloud router resource
This commit is contained in:
Paddy 2017-05-24 21:36:17 -07:00 committed by GitHub
commit 63ce0dae98
18 changed files with 2152 additions and 1 deletions

View File

@ -0,0 +1,28 @@
package google
import (
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccComputeRouterInterface_import(t *testing.T) {
resourceName := "google_compute_router_interface.foobar"
testId := acctest.RandString(10)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeRouterInterfaceBasic(testId),
},
resource.TestStep{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -0,0 +1,28 @@
package google
import (
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccComputeRouterPeer_import(t *testing.T) {
resourceName := "google_compute_router_peer.foobar"
testId := acctest.RandString(10)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeRouterPeerBasic(testId),
},
resource.TestStep{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -0,0 +1,28 @@
package google
import (
"testing"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccComputeRouter_import(t *testing.T) {
resourceName := "google_compute_router.foobar"
resourceRegion := "europe-west1"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeRouterDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeRouterBasic(resourceRegion),
},
resource.TestStep{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -6,12 +6,16 @@ import (
"log" "log"
"strings" "strings"
"github.com/hashicorp/terraform/helper/mutexkv"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
"google.golang.org/api/compute/v1" "google.golang.org/api/compute/v1"
"google.golang.org/api/googleapi" "google.golang.org/api/googleapi"
) )
// Global MutexKV
var mutexKV = mutexkv.NewMutexKV()
// Provider returns a terraform.ResourceProvider. // Provider returns a terraform.ResourceProvider.
func Provider() terraform.ResourceProvider { func Provider() terraform.ResourceProvider {
return &schema.Provider{ return &schema.Provider{
@ -82,6 +86,9 @@ func Provider() terraform.ResourceProvider {
"google_compute_project_metadata": resourceComputeProjectMetadata(), "google_compute_project_metadata": resourceComputeProjectMetadata(),
"google_compute_region_backend_service": resourceComputeRegionBackendService(), "google_compute_region_backend_service": resourceComputeRegionBackendService(),
"google_compute_route": resourceComputeRoute(), "google_compute_route": resourceComputeRoute(),
"google_compute_router": resourceComputeRouter(),
"google_compute_router_interface": resourceComputeRouterInterface(),
"google_compute_router_peer": resourceComputeRouterPeer(),
"google_compute_ssl_certificate": resourceComputeSslCertificate(), "google_compute_ssl_certificate": resourceComputeSslCertificate(),
"google_compute_subnetwork": resourceComputeSubnetwork(), "google_compute_subnetwork": resourceComputeSubnetwork(),
"google_compute_target_http_proxy": resourceComputeTargetHttpProxy(), "google_compute_target_http_proxy": resourceComputeTargetHttpProxy(),
@ -256,6 +263,10 @@ func getNetworkNameFromSelfLink(network string) (string, error) {
return network, nil return network, nil
} }
func getRouterLockName(region string, router string) string {
return fmt.Sprintf("router/%s/%s", region, router)
}
func handleNotFoundError(err error, d *schema.ResourceData, resource string) error { func handleNotFoundError(err error, d *schema.ResourceData, resource string) error {
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
log.Printf("[WARN] Removing %s because it's gone", resource) log.Printf("[WARN] Removing %s because it's gone", resource)
@ -267,3 +278,11 @@ func handleNotFoundError(err error, d *schema.ResourceData, resource string) err
return fmt.Errorf("Error reading %s: %s", resource, err) return fmt.Errorf("Error reading %s: %s", resource, err)
} }
func linkDiffSuppress(k, old, new string, d *schema.ResourceData) bool {
parts := strings.Split(old, "/")
if parts[len(parts)-1] == new {
return true
}
return false
}

View File

@ -1,6 +1,7 @@
package google package google
import ( import (
"fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"strings" "strings"
@ -87,3 +88,25 @@ func TestProvider_getRegionFromZone(t *testing.T) {
t.Fatalf("Region (%s) did not match expected value: %s", actual, expected) t.Fatalf("Region (%s) did not match expected value: %s", actual, expected)
} }
} }
// getTestRegion has the same logic as the provider's getRegion, to be used in tests.
func getTestRegion(is *terraform.InstanceState, config *Config) (string, error) {
if res, ok := is.Attributes["region"]; ok {
return res, nil
}
if config.Region != "" {
return config.Region, nil
}
return "", fmt.Errorf("%q: required field is not set", "region")
}
// getTestProject has the same logic as the provider's getProject, to be used in tests.
func getTestProject(is *terraform.InstanceState, config *Config) (string, error) {
if res, ok := is.Attributes["project"]; ok {
return res, nil
}
if config.Project != "" {
return config.Project, nil
}
return "", fmt.Errorf("%q: required field is not set", "project")
}

View File

@ -0,0 +1,254 @@
package google
import (
"fmt"
"log"
"strings"
"github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/compute/v1"
"google.golang.org/api/googleapi"
)
func resourceComputeRouter() *schema.Resource {
return &schema.Resource{
Create: resourceComputeRouterCreate,
Read: resourceComputeRouterRead,
Delete: resourceComputeRouterDelete,
Importer: &schema.ResourceImporter{
State: resourceComputeRouterImportState,
},
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"network": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DiffSuppressFunc: linkDiffSuppress,
},
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"project": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"region": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"bgp": &schema.Schema{
Type: schema.TypeList,
MaxItems: 1,
Required: true,
ForceNew: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"asn": &schema.Schema{
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},
},
},
},
"self_link": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
},
}
}
func resourceComputeRouterCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
region, err := getRegion(d, config)
if err != nil {
return err
}
project, err := getProject(d, config)
if err != nil {
return err
}
name := d.Get("name").(string)
routerLock := getRouterLockName(region, name)
mutexKV.Lock(routerLock)
defer mutexKV.Unlock(routerLock)
network, err := getNetworkLink(d, config, "network")
if err != nil {
return err
}
routersService := config.clientCompute.Routers
router := &compute.Router{
Name: name,
Network: network,
}
if v, ok := d.GetOk("description"); ok {
router.Description = v.(string)
}
if _, ok := d.GetOk("bgp"); ok {
prefix := "bgp.0"
if v, ok := d.GetOk(prefix + ".asn"); ok {
asn := v.(int)
bgp := &compute.RouterBgp{
Asn: int64(asn),
}
router.Bgp = bgp
}
}
op, err := routersService.Insert(project, region, router).Do()
if err != nil {
return fmt.Errorf("Error Inserting Router %s into network %s: %s", name, network, err)
}
d.SetId(fmt.Sprintf("%s/%s", region, name))
err = computeOperationWaitRegion(config, op, project, region, "Inserting Router")
if err != nil {
d.SetId("")
return fmt.Errorf("Error Waiting to Insert Router %s into network %s: %s", name, network, err)
}
return resourceComputeRouterRead(d, meta)
}
func resourceComputeRouterRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
region, err := getRegion(d, config)
if err != nil {
return err
}
project, err := getProject(d, config)
if err != nil {
return err
}
name := d.Get("name").(string)
routersService := config.clientCompute.Routers
router, err := routersService.Get(project, region, name).Do()
if err != nil {
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
log.Printf("[WARN] Removing router %s/%s because it is gone", region, name)
d.SetId("")
return nil
}
return fmt.Errorf("Error Reading Router %s: %s", name, err)
}
d.Set("self_link", router.SelfLink)
d.Set("network", router.Network)
d.Set("name", router.Name)
d.Set("description", router.Description)
d.Set("region", region)
d.Set("project", project)
d.Set("bgp", flattenAsn(router.Bgp.Asn))
d.SetId(fmt.Sprintf("%s/%s", region, name))
return nil
}
func resourceComputeRouterDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
region, err := getRegion(d, config)
if err != nil {
return err
}
project, err := getProject(d, config)
if err != nil {
return err
}
name := d.Get("name").(string)
routerLock := getRouterLockName(region, name)
mutexKV.Lock(routerLock)
defer mutexKV.Unlock(routerLock)
routersService := config.clientCompute.Routers
op, err := routersService.Delete(project, region, name).Do()
if err != nil {
return fmt.Errorf("Error Reading Router %s: %s", name, err)
}
err = computeOperationWaitRegion(config, op, project, region, "Deleting Router")
if err != nil {
return fmt.Errorf("Error Waiting to Delete Router %s: %s", name, err)
}
d.SetId("")
return nil
}
func resourceComputeRouterImportState(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
parts := strings.Split(d.Id(), "/")
if len(parts) != 2 {
return nil, fmt.Errorf("Invalid router specifier. Expecting {region}/{name}")
}
d.Set("region", parts[0])
d.Set("name", parts[1])
return []*schema.ResourceData{d}, nil
}
func getRouterLink(config *Config, project string, region string, router string) (string, error) {
if !strings.HasPrefix(router, "https://www.googleapis.com/compute/") {
// Router value provided is just the name, lookup the router SelfLink
routerData, err := config.clientCompute.Routers.Get(
project, region, router).Do()
if err != nil {
return "", fmt.Errorf("Error reading router: %s", err)
}
router = routerData.SelfLink
}
return router, nil
}
func flattenAsn(asn int64) []map[string]interface{} {
result := make([]map[string]interface{}, 0, 1)
r := make(map[string]interface{})
r["asn"] = asn
result = append(result, r)
return result
}

View File

@ -0,0 +1,269 @@
package google
import (
"fmt"
"log"
"strings"
"github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/compute/v1"
"google.golang.org/api/googleapi"
)
func resourceComputeRouterInterface() *schema.Resource {
return &schema.Resource{
Create: resourceComputeRouterInterfaceCreate,
Read: resourceComputeRouterInterfaceRead,
Delete: resourceComputeRouterInterfaceDelete,
Importer: &schema.ResourceImporter{
State: resourceComputeRouterInterfaceImportState,
},
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"router": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"vpn_tunnel": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DiffSuppressFunc: linkDiffSuppress,
},
"ip_range": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"project": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"region": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
},
}
}
func resourceComputeRouterInterfaceCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
region, err := getRegion(d, config)
if err != nil {
return err
}
project, err := getProject(d, config)
if err != nil {
return err
}
routerName := d.Get("router").(string)
ifaceName := d.Get("name").(string)
routerLock := getRouterLockName(region, routerName)
mutexKV.Lock(routerLock)
defer mutexKV.Unlock(routerLock)
routersService := config.clientCompute.Routers
router, err := routersService.Get(project, region, routerName).Do()
if err != nil {
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
log.Printf("[WARN] Removing router interface %s because its router %s/%s is gone", ifaceName, region, routerName)
d.SetId("")
return nil
}
return fmt.Errorf("Error Reading router %s/%s: %s", region, routerName, err)
}
ifaces := router.Interfaces
for _, iface := range ifaces {
if iface.Name == ifaceName {
d.SetId("")
return fmt.Errorf("Router %s has interface %s already", routerName, ifaceName)
}
}
vpnTunnel, err := getVpnTunnelLink(config, project, region, d.Get("vpn_tunnel").(string))
if err != nil {
return err
}
iface := &compute.RouterInterface{Name: ifaceName,
LinkedVpnTunnel: vpnTunnel}
if v, ok := d.GetOk("ip_range"); ok {
iface.IpRange = v.(string)
}
log.Printf("[INFO] Adding interface %s", ifaceName)
ifaces = append(ifaces, iface)
patchRouter := &compute.Router{
Interfaces: ifaces,
}
log.Printf("[DEBUG] Updating router %s/%s with interfaces: %+v", region, routerName, ifaces)
op, err := routersService.Patch(project, region, router.Name, patchRouter).Do()
if err != nil {
return fmt.Errorf("Error patching router %s/%s: %s", region, routerName, err)
}
d.SetId(fmt.Sprintf("%s/%s/%s", region, routerName, ifaceName))
err = computeOperationWaitRegion(config, op, project, region, "Patching router")
if err != nil {
d.SetId("")
return fmt.Errorf("Error waiting to patch router %s/%s: %s", region, routerName, err)
}
return resourceComputeRouterInterfaceRead(d, meta)
}
func resourceComputeRouterInterfaceRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
region, err := getRegion(d, config)
if err != nil {
return err
}
project, err := getProject(d, config)
if err != nil {
return err
}
routerName := d.Get("router").(string)
ifaceName := d.Get("name").(string)
routersService := config.clientCompute.Routers
router, err := routersService.Get(project, region, routerName).Do()
if err != nil {
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
log.Printf("[WARN] Removing router interface %s because its router %s/%s is gone", ifaceName, region, routerName)
d.SetId("")
return nil
}
return fmt.Errorf("Error Reading router %s/%s: %s", region, routerName, err)
}
for _, iface := range router.Interfaces {
if iface.Name == ifaceName {
d.SetId(fmt.Sprintf("%s/%s/%s", region, routerName, ifaceName))
d.Set("vpn_tunnel", iface.LinkedVpnTunnel)
d.Set("ip_range", iface.IpRange)
d.Set("region", region)
d.Set("project", project)
return nil
}
}
log.Printf("[WARN] Removing router interface %s/%s/%s because it is gone", region, routerName, ifaceName)
d.SetId("")
return nil
}
func resourceComputeRouterInterfaceDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
region, err := getRegion(d, config)
if err != nil {
return err
}
project, err := getProject(d, config)
if err != nil {
return err
}
routerName := d.Get("router").(string)
ifaceName := d.Get("name").(string)
routerLock := getRouterLockName(region, routerName)
mutexKV.Lock(routerLock)
defer mutexKV.Unlock(routerLock)
routersService := config.clientCompute.Routers
router, err := routersService.Get(project, region, routerName).Do()
if err != nil {
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
log.Printf("[WARN] Removing router interface %s because its router %s/%s is gone", ifaceName, region, routerName)
return nil
}
return fmt.Errorf("Error Reading Router %s: %s", routerName, err)
}
var ifaceFound bool
newIfaces := make([]*compute.RouterInterface, 0, len(router.Interfaces))
for _, iface := range router.Interfaces {
if iface.Name == ifaceName {
ifaceFound = true
continue
} else {
newIfaces = append(newIfaces, iface)
}
}
if !ifaceFound {
log.Printf("[DEBUG] Router %s/%s had no interface %s already", region, routerName, ifaceName)
d.SetId("")
return nil
}
log.Printf(
"[INFO] Removing interface %s from router %s/%s", ifaceName, region, routerName)
patchRouter := &compute.Router{
Interfaces: newIfaces,
}
log.Printf("[DEBUG] Updating router %s/%s with interfaces: %+v", region, routerName, newIfaces)
op, err := routersService.Patch(project, region, router.Name, patchRouter).Do()
if err != nil {
return fmt.Errorf("Error patching router %s/%s: %s", region, routerName, err)
}
err = computeOperationWaitRegion(config, op, project, region, "Patching router")
if err != nil {
return fmt.Errorf("Error waiting to patch router %s/%s: %s", region, routerName, err)
}
d.SetId("")
return nil
}
func resourceComputeRouterInterfaceImportState(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
parts := strings.Split(d.Id(), "/")
if len(parts) != 3 {
return nil, fmt.Errorf("Invalid router interface specifier. Expecting {region}/{router}/{interface}")
}
d.Set("region", parts[0])
d.Set("router", parts[1])
d.Set("name", parts[2])
return []*schema.ResourceData{d}, nil
}

View File

@ -0,0 +1,282 @@
package google
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccComputeRouterInterface_basic(t *testing.T) {
testId := acctest.RandString(10)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeRouterInterfaceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeRouterInterfaceBasic(testId),
Check: testAccCheckComputeRouterInterfaceExists(
"google_compute_router_interface.foobar"),
},
resource.TestStep{
Config: testAccComputeRouterInterfaceKeepRouter(testId),
Check: testAccCheckComputeRouterInterfaceDelete(
"google_compute_router_interface.foobar"),
},
},
})
}
func testAccCheckComputeRouterInterfaceDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
routersService := config.clientCompute.Routers
for _, rs := range s.RootModule().Resources {
if rs.Type != "google_compute_router" {
continue
}
project, err := getTestProject(rs.Primary, config)
if err != nil {
return err
}
region, err := getTestRegion(rs.Primary, config)
if err != nil {
return err
}
routerName := rs.Primary.Attributes["router"]
_, err = routersService.Get(project, region, routerName).Do()
if err == nil {
return fmt.Errorf("Error, Router %s in region %s still exists",
routerName, region)
}
}
return nil
}
func testAccCheckComputeRouterInterfaceDelete(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
routersService := config.clientCompute.Routers
for _, rs := range s.RootModule().Resources {
if rs.Type != "google_compute_router_interface" {
continue
}
project, err := getTestProject(rs.Primary, config)
if err != nil {
return err
}
region, err := getTestRegion(rs.Primary, config)
if err != nil {
return err
}
name := rs.Primary.Attributes["name"]
routerName := rs.Primary.Attributes["router"]
router, err := routersService.Get(project, region, routerName).Do()
if err != nil {
return fmt.Errorf("Error Reading Router %s: %s", routerName, err)
}
ifaces := router.Interfaces
for _, iface := range ifaces {
if iface.Name == name {
return fmt.Errorf("Interface %s still exists on router %s/%s", name, region, router.Name)
}
}
}
return nil
}
}
func testAccCheckComputeRouterInterfaceExists(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set")
}
config := testAccProvider.Meta().(*Config)
project, err := getTestProject(rs.Primary, config)
if err != nil {
return err
}
region, err := getTestRegion(rs.Primary, config)
if err != nil {
return err
}
name := rs.Primary.Attributes["name"]
routerName := rs.Primary.Attributes["router"]
routersService := config.clientCompute.Routers
router, err := routersService.Get(project, region, routerName).Do()
if err != nil {
return fmt.Errorf("Error Reading Router %s: %s", routerName, err)
}
for _, iface := range router.Interfaces {
if iface.Name == name {
return nil
}
}
return fmt.Errorf("Interface %s not found for router %s", name, router.Name)
}
}
func testAccComputeRouterInterfaceBasic(testId string) string {
return fmt.Sprintf(`
resource "google_compute_network" "foobar" {
name = "router-interface-test-%s"
}
resource "google_compute_subnetwork" "foobar" {
name = "router-interface-test-%s"
network = "${google_compute_network.foobar.self_link}"
ip_cidr_range = "10.0.0.0/16"
region = "us-central1"
}
resource "google_compute_address" "foobar" {
name = "router-interface-test-%s"
region = "${google_compute_subnetwork.foobar.region}"
}
resource "google_compute_vpn_gateway" "foobar" {
name = "router-interface-test-%s"
network = "${google_compute_network.foobar.self_link}"
region = "${google_compute_subnetwork.foobar.region}"
}
resource "google_compute_forwarding_rule" "foobar_esp" {
name = "router-interface-test-%s-1"
region = "${google_compute_vpn_gateway.foobar.region}"
ip_protocol = "ESP"
ip_address = "${google_compute_address.foobar.address}"
target = "${google_compute_vpn_gateway.foobar.self_link}"
}
resource "google_compute_forwarding_rule" "foobar_udp500" {
name = "router-interface-test-%s-2"
region = "${google_compute_forwarding_rule.foobar_esp.region}"
ip_protocol = "UDP"
port_range = "500-500"
ip_address = "${google_compute_address.foobar.address}"
target = "${google_compute_vpn_gateway.foobar.self_link}"
}
resource "google_compute_forwarding_rule" "foobar_udp4500" {
name = "router-interface-test-%s-3"
region = "${google_compute_forwarding_rule.foobar_udp500.region}"
ip_protocol = "UDP"
port_range = "4500-4500"
ip_address = "${google_compute_address.foobar.address}"
target = "${google_compute_vpn_gateway.foobar.self_link}"
}
resource "google_compute_router" "foobar"{
name = "router-interface-test-%s"
region = "${google_compute_forwarding_rule.foobar_udp500.region}"
network = "${google_compute_network.foobar.self_link}"
bgp {
asn = 64514
}
}
resource "google_compute_vpn_tunnel" "foobar" {
name = "router-interface-test-%s"
region = "${google_compute_forwarding_rule.foobar_udp4500.region}"
target_vpn_gateway = "${google_compute_vpn_gateway.foobar.self_link}"
shared_secret = "unguessable"
peer_ip = "8.8.8.8"
router = "${google_compute_router.foobar.name}"
}
resource "google_compute_router_interface" "foobar" {
name = "router-interface-test-%s"
router = "${google_compute_router.foobar.name}"
region = "${google_compute_router.foobar.region}"
ip_range = "169.254.3.1/30"
vpn_tunnel = "${google_compute_vpn_tunnel.foobar.name}"
}
`, testId, testId, testId, testId, testId, testId, testId, testId, testId, testId)
}
func testAccComputeRouterInterfaceKeepRouter(testId string) string {
return fmt.Sprintf(`
resource "google_compute_network" "foobar" {
name = "router-interface-test-%s"
}
resource "google_compute_subnetwork" "foobar" {
name = "router-interface-test-%s"
network = "${google_compute_network.foobar.self_link}"
ip_cidr_range = "10.0.0.0/16"
region = "us-central1"
}
resource "google_compute_address" "foobar" {
name = "router-interface-test-%s"
region = "${google_compute_subnetwork.foobar.region}"
}
resource "google_compute_vpn_gateway" "foobar" {
name = "router-interface-test-%s"
network = "${google_compute_network.foobar.self_link}"
region = "${google_compute_subnetwork.foobar.region}"
}
resource "google_compute_forwarding_rule" "foobar_esp" {
name = "router-interface-test-%s-1"
region = "${google_compute_vpn_gateway.foobar.region}"
ip_protocol = "ESP"
ip_address = "${google_compute_address.foobar.address}"
target = "${google_compute_vpn_gateway.foobar.self_link}"
}
resource "google_compute_forwarding_rule" "foobar_udp500" {
name = "router-interface-test-%s-2"
region = "${google_compute_forwarding_rule.foobar_esp.region}"
ip_protocol = "UDP"
port_range = "500-500"
ip_address = "${google_compute_address.foobar.address}"
target = "${google_compute_vpn_gateway.foobar.self_link}"
}
resource "google_compute_forwarding_rule" "foobar_udp4500" {
name = "router-interface-test-%s-3"
region = "${google_compute_forwarding_rule.foobar_udp500.region}"
ip_protocol = "UDP"
port_range = "4500-4500"
ip_address = "${google_compute_address.foobar.address}"
target = "${google_compute_vpn_gateway.foobar.self_link}"
}
resource "google_compute_router" "foobar"{
name = "router-interface-test-%s"
region = "${google_compute_forwarding_rule.foobar_udp500.region}"
network = "${google_compute_network.foobar.self_link}"
bgp {
asn = 64514
}
}
resource "google_compute_vpn_tunnel" "foobar" {
name = "router-interface-test-%s"
region = "${google_compute_forwarding_rule.foobar_udp4500.region}"
target_vpn_gateway = "${google_compute_vpn_gateway.foobar.self_link}"
shared_secret = "unguessable"
peer_ip = "8.8.8.8"
router = "${google_compute_router.foobar.name}"
}
`, testId, testId, testId, testId, testId, testId, testId, testId, testId)
}

View File

@ -0,0 +1,290 @@
package google
import (
"fmt"
"log"
"strings"
"github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/compute/v1"
"google.golang.org/api/googleapi"
)
func resourceComputeRouterPeer() *schema.Resource {
return &schema.Resource{
Create: resourceComputeRouterPeerCreate,
Read: resourceComputeRouterPeerRead,
Delete: resourceComputeRouterPeerDelete,
Importer: &schema.ResourceImporter{
State: resourceComputeRouterPeerImportState,
},
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"router": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"interface": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"peer_ip_address": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"peer_asn": &schema.Schema{
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},
"advertised_route_priority": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},
"ip_address": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"project": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"region": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
},
}
}
func resourceComputeRouterPeerCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
region, err := getRegion(d, config)
if err != nil {
return err
}
project, err := getProject(d, config)
if err != nil {
return err
}
routerName := d.Get("router").(string)
peerName := d.Get("name").(string)
routerLock := getRouterLockName(region, routerName)
mutexKV.Lock(routerLock)
defer mutexKV.Unlock(routerLock)
routersService := config.clientCompute.Routers
router, err := routersService.Get(project, region, routerName).Do()
if err != nil {
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
log.Printf("[WARN] Removing router peer %s because its router %s/%s is gone", peerName, region, routerName)
d.SetId("")
return nil
}
return fmt.Errorf("Error Reading router %s/%s: %s", region, routerName, err)
}
peers := router.BgpPeers
for _, peer := range peers {
if peer.Name == peerName {
d.SetId("")
return fmt.Errorf("Router %s has peer %s already", routerName, peerName)
}
}
ifaceName := d.Get("interface").(string)
peer := &compute.RouterBgpPeer{Name: peerName,
InterfaceName: ifaceName}
if v, ok := d.GetOk("peer_ip_address"); ok {
peer.PeerIpAddress = v.(string)
}
if v, ok := d.GetOk("peer_asn"); ok {
peer.PeerAsn = int64(v.(int))
}
if v, ok := d.GetOk("advertised_route_priority"); ok {
peer.AdvertisedRoutePriority = int64(v.(int))
}
log.Printf("[INFO] Adding peer %s", peerName)
peers = append(peers, peer)
patchRouter := &compute.Router{
BgpPeers: peers,
}
log.Printf("[DEBUG] Updating router %s/%s with peers: %+v", region, routerName, peers)
op, err := routersService.Patch(project, region, router.Name, patchRouter).Do()
if err != nil {
return fmt.Errorf("Error patching router %s/%s: %s", region, routerName, err)
}
d.SetId(fmt.Sprintf("%s/%s/%s", region, routerName, peerName))
err = computeOperationWaitRegion(config, op, project, region, "Patching router")
if err != nil {
d.SetId("")
return fmt.Errorf("Error waiting to patch router %s/%s: %s", region, routerName, err)
}
return resourceComputeRouterPeerRead(d, meta)
}
func resourceComputeRouterPeerRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
region, err := getRegion(d, config)
if err != nil {
return err
}
project, err := getProject(d, config)
if err != nil {
return err
}
routerName := d.Get("router").(string)
peerName := d.Get("name").(string)
routersService := config.clientCompute.Routers
router, err := routersService.Get(project, region, routerName).Do()
if err != nil {
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
log.Printf("[WARN] Removing router peer %s because its router %s/%s is gone", peerName, region, routerName)
d.SetId("")
return nil
}
return fmt.Errorf("Error Reading router %s/%s: %s", region, routerName, err)
}
for _, peer := range router.BgpPeers {
if peer.Name == peerName {
d.SetId(fmt.Sprintf("%s/%s/%s", region, routerName, peerName))
d.Set("interface", peer.InterfaceName)
d.Set("peer_ip_address", peer.PeerIpAddress)
d.Set("peer_asn", peer.PeerAsn)
d.Set("advertised_route_priority", peer.AdvertisedRoutePriority)
d.Set("ip_address", peer.IpAddress)
d.Set("region", region)
d.Set("project", project)
return nil
}
}
log.Printf("[WARN] Removing router peer %s/%s/%s because it is gone", region, routerName, peerName)
d.SetId("")
return nil
}
func resourceComputeRouterPeerDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
region, err := getRegion(d, config)
if err != nil {
return err
}
project, err := getProject(d, config)
if err != nil {
return err
}
routerName := d.Get("router").(string)
peerName := d.Get("name").(string)
routerLock := getRouterLockName(region, routerName)
mutexKV.Lock(routerLock)
defer mutexKV.Unlock(routerLock)
routersService := config.clientCompute.Routers
router, err := routersService.Get(project, region, routerName).Do()
if err != nil {
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
log.Printf("[WARN] Removing router peer %s because its router %s/%s is gone", peerName, region, routerName)
return nil
}
return fmt.Errorf("Error Reading Router %s: %s", routerName, err)
}
var newPeers []*compute.RouterBgpPeer = make([]*compute.RouterBgpPeer, 0, len(router.BgpPeers))
for _, peer := range router.BgpPeers {
if peer.Name == peerName {
continue
} else {
newPeers = append(newPeers, peer)
}
}
if len(newPeers) == len(router.BgpPeers) {
log.Printf("[DEBUG] Router %s/%s had no peer %s already", region, routerName, peerName)
d.SetId("")
return nil
}
log.Printf(
"[INFO] Removing peer %s from router %s/%s", peerName, region, routerName)
patchRouter := &compute.Router{
BgpPeers: newPeers,
}
log.Printf("[DEBUG] Updating router %s/%s with peers: %+v", region, routerName, newPeers)
op, err := routersService.Patch(project, region, router.Name, patchRouter).Do()
if err != nil {
return fmt.Errorf("Error patching router %s/%s: %s", region, routerName, err)
}
err = computeOperationWaitRegion(config, op, project, region, "Patching router")
if err != nil {
return fmt.Errorf("Error waiting to patch router %s/%s: %s", region, routerName, err)
}
d.SetId("")
return nil
}
func resourceComputeRouterPeerImportState(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
parts := strings.Split(d.Id(), "/")
if len(parts) != 3 {
return nil, fmt.Errorf("Invalid router peer specifier. Expecting {region}/{router}/{peer}")
}
d.Set("region", parts[0])
d.Set("router", parts[1])
d.Set("name", parts[2])
return []*schema.ResourceData{d}, nil
}

View File

@ -0,0 +1,298 @@
package google
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccComputeRouterPeer_basic(t *testing.T) {
testId := acctest.RandString(10)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeRouterPeerDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeRouterPeerBasic(testId),
Check: testAccCheckComputeRouterPeerExists(
"google_compute_router_peer.foobar"),
},
resource.TestStep{
Config: testAccComputeRouterPeerKeepRouter(testId),
Check: testAccCheckComputeRouterPeerDelete(
"google_compute_router_peer.foobar"),
},
},
})
}
func testAccCheckComputeRouterPeerDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
routersService := config.clientCompute.Routers
for _, rs := range s.RootModule().Resources {
if rs.Type != "google_compute_router" {
continue
}
project, err := getTestProject(rs.Primary, config)
if err != nil {
return err
}
region, err := getTestRegion(rs.Primary, config)
if err != nil {
return err
}
routerName := rs.Primary.Attributes["router"]
_, err = routersService.Get(project, region, routerName).Do()
if err == nil {
return fmt.Errorf("Error, Router %s in region %s still exists",
routerName, region)
}
}
return nil
}
func testAccCheckComputeRouterPeerDelete(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
routersService := config.clientCompute.Routers
for _, rs := range s.RootModule().Resources {
if rs.Type != "google_compute_router_peer" {
continue
}
project, err := getTestProject(rs.Primary, config)
if err != nil {
return err
}
region, err := getTestRegion(rs.Primary, config)
if err != nil {
return err
}
name := rs.Primary.Attributes["name"]
routerName := rs.Primary.Attributes["router"]
router, err := routersService.Get(project, region, routerName).Do()
if err != nil {
return fmt.Errorf("Error Reading Router %s: %s", routerName, err)
}
peers := router.BgpPeers
for _, peer := range peers {
if peer.Name == name {
return fmt.Errorf("Peer %s still exists on router %s/%s", name, region, router.Name)
}
}
}
return nil
}
}
func testAccCheckComputeRouterPeerExists(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set")
}
config := testAccProvider.Meta().(*Config)
project, err := getTestProject(rs.Primary, config)
if err != nil {
return err
}
region, err := getTestRegion(rs.Primary, config)
if err != nil {
return err
}
name := rs.Primary.Attributes["name"]
routerName := rs.Primary.Attributes["router"]
routersService := config.clientCompute.Routers
router, err := routersService.Get(project, region, routerName).Do()
if err != nil {
return fmt.Errorf("Error Reading Router %s: %s", routerName, err)
}
for _, peer := range router.BgpPeers {
if peer.Name == name {
return nil
}
}
return fmt.Errorf("Peer %s not found for router %s", name, router.Name)
}
}
func testAccComputeRouterPeerBasic(testId string) string {
return fmt.Sprintf(`
resource "google_compute_network" "foobar" {
name = "router-peer-test-%s"
}
resource "google_compute_subnetwork" "foobar" {
name = "router-peer-test-%s"
network = "${google_compute_network.foobar.self_link}"
ip_cidr_range = "10.0.0.0/16"
region = "us-central1"
}
resource "google_compute_address" "foobar" {
name = "router-peer-test-%s"
region = "${google_compute_subnetwork.foobar.region}"
}
resource "google_compute_vpn_gateway" "foobar" {
name = "router-peer-test-%s"
network = "${google_compute_network.foobar.self_link}"
region = "${google_compute_subnetwork.foobar.region}"
}
resource "google_compute_forwarding_rule" "foobar_esp" {
name = "router-peer-test-%s-1"
region = "${google_compute_vpn_gateway.foobar.region}"
ip_protocol = "ESP"
ip_address = "${google_compute_address.foobar.address}"
target = "${google_compute_vpn_gateway.foobar.self_link}"
}
resource "google_compute_forwarding_rule" "foobar_udp500" {
name = "router-peer-test-%s-2"
region = "${google_compute_forwarding_rule.foobar_esp.region}"
ip_protocol = "UDP"
port_range = "500-500"
ip_address = "${google_compute_address.foobar.address}"
target = "${google_compute_vpn_gateway.foobar.self_link}"
}
resource "google_compute_forwarding_rule" "foobar_udp4500" {
name = "router-peer-test-%s-3"
region = "${google_compute_forwarding_rule.foobar_udp500.region}"
ip_protocol = "UDP"
port_range = "4500-4500"
ip_address = "${google_compute_address.foobar.address}"
target = "${google_compute_vpn_gateway.foobar.self_link}"
}
resource "google_compute_router" "foobar"{
name = "router-peer-test-%s"
region = "${google_compute_forwarding_rule.foobar_udp500.region}"
network = "${google_compute_network.foobar.self_link}"
bgp {
asn = 64514
}
}
resource "google_compute_vpn_tunnel" "foobar" {
name = "router-peer-test-%s"
region = "${google_compute_forwarding_rule.foobar_udp4500.region}"
target_vpn_gateway = "${google_compute_vpn_gateway.foobar.self_link}"
shared_secret = "unguessable"
peer_ip = "8.8.8.8"
router = "${google_compute_router.foobar.name}"
}
resource "google_compute_router_interface" "foobar" {
name = "router-peer-test-%s"
router = "${google_compute_router.foobar.name}"
region = "${google_compute_router.foobar.region}"
ip_range = "169.254.3.1/30"
vpn_tunnel = "${google_compute_vpn_tunnel.foobar.name}"
}
resource "google_compute_router_peer" "foobar" {
name = "router-peer-test-%s"
router = "${google_compute_router.foobar.name}"
region = "${google_compute_router.foobar.region}"
peer_ip_address = "169.254.3.2"
peer_asn = 65515
advertised_route_priority = 100
interface = "${google_compute_router_interface.foobar.name}"
}
`, testId, testId, testId, testId, testId, testId, testId, testId, testId, testId, testId)
}
func testAccComputeRouterPeerKeepRouter(testId string) string {
return fmt.Sprintf(`
resource "google_compute_network" "foobar" {
name = "router-peer-test-%s"
}
resource "google_compute_subnetwork" "foobar" {
name = "router-peer-test-%s"
network = "${google_compute_network.foobar.self_link}"
ip_cidr_range = "10.0.0.0/16"
region = "us-central1"
}
resource "google_compute_address" "foobar" {
name = "router-peer-test-%s"
region = "${google_compute_subnetwork.foobar.region}"
}
resource "google_compute_vpn_gateway" "foobar" {
name = "router-peer-test-%s"
network = "${google_compute_network.foobar.self_link}"
region = "${google_compute_subnetwork.foobar.region}"
}
resource "google_compute_forwarding_rule" "foobar_esp" {
name = "router-peer-test-%s-1"
region = "${google_compute_vpn_gateway.foobar.region}"
ip_protocol = "ESP"
ip_address = "${google_compute_address.foobar.address}"
target = "${google_compute_vpn_gateway.foobar.self_link}"
}
resource "google_compute_forwarding_rule" "foobar_udp500" {
name = "router-peer-test-%s-2"
region = "${google_compute_forwarding_rule.foobar_esp.region}"
ip_protocol = "UDP"
port_range = "500-500"
ip_address = "${google_compute_address.foobar.address}"
target = "${google_compute_vpn_gateway.foobar.self_link}"
}
resource "google_compute_forwarding_rule" "foobar_udp4500" {
name = "router-peer-test-%s-3"
region = "${google_compute_forwarding_rule.foobar_udp500.region}"
ip_protocol = "UDP"
port_range = "4500-4500"
ip_address = "${google_compute_address.foobar.address}"
target = "${google_compute_vpn_gateway.foobar.self_link}"
}
resource "google_compute_router" "foobar"{
name = "router-peer-test-%s"
region = "${google_compute_forwarding_rule.foobar_udp500.region}"
network = "${google_compute_network.foobar.self_link}"
bgp {
asn = 64514
}
}
resource "google_compute_vpn_tunnel" "foobar" {
name = "router-peer-test-%s"
region = "${google_compute_forwarding_rule.foobar_udp4500.region}"
target_vpn_gateway = "${google_compute_vpn_gateway.foobar.self_link}"
shared_secret = "unguessable"
peer_ip = "8.8.8.8"
router = "${google_compute_router.foobar.name}"
}
resource "google_compute_router_interface" "foobar" {
name = "router-peer-test-%s"
router = "${google_compute_router.foobar.name}"
region = "${google_compute_router.foobar.region}"
ip_range = "169.254.3.1/30"
vpn_tunnel = "${google_compute_vpn_tunnel.foobar.name}"
}
`, testId, testId, testId, testId, testId, testId, testId, testId, testId, testId)
}

View File

@ -0,0 +1,202 @@
package google
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccComputeRouter_basic(t *testing.T) {
resourceRegion := "europe-west1"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeRouterDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeRouterBasic(resourceRegion),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeRouterExists(
"google_compute_router.foobar"),
resource.TestCheckResourceAttr(
"google_compute_router.foobar", "region", resourceRegion),
),
},
},
})
}
func TestAccComputeRouter_noRegion(t *testing.T) {
providerRegion := "us-central1"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeRouterDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeRouterNoRegion(providerRegion),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeRouterExists(
"google_compute_router.foobar"),
resource.TestCheckResourceAttr(
"google_compute_router.foobar", "region", providerRegion),
),
},
},
})
}
func TestAccComputeRouter_networkLink(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeRouterDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeRouterNetworkLink(),
Check: testAccCheckComputeRouterExists(
"google_compute_router.foobar"),
},
},
})
}
func testAccCheckComputeRouterDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
routersService := config.clientCompute.Routers
for _, rs := range s.RootModule().Resources {
if rs.Type != "google_compute_router" {
continue
}
project, err := getTestProject(rs.Primary, config)
if err != nil {
return err
}
region, err := getTestRegion(rs.Primary, config)
if err != nil {
return err
}
name := rs.Primary.Attributes["name"]
_, err = routersService.Get(project, region, name).Do()
if err == nil {
return fmt.Errorf("Error, Router %s in region %s still exists",
name, region)
}
}
return nil
}
func testAccCheckComputeRouterExists(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set")
}
config := testAccProvider.Meta().(*Config)
project, err := getTestProject(rs.Primary, config)
if err != nil {
return err
}
region, err := getTestRegion(rs.Primary, config)
if err != nil {
return err
}
name := rs.Primary.Attributes["name"]
routersService := config.clientCompute.Routers
_, err = routersService.Get(project, region, name).Do()
if err != nil {
return fmt.Errorf("Error Reading Router %s: %s", name, err)
}
return nil
}
}
func testAccComputeRouterBasic(resourceRegion string) string {
testId := acctest.RandString(10)
return fmt.Sprintf(`
resource "google_compute_network" "foobar" {
name = "router-test-%s"
}
resource "google_compute_subnetwork" "foobar" {
name = "router-test-%s"
network = "${google_compute_network.foobar.self_link}"
ip_cidr_range = "10.0.0.0/16"
region = "%s"
}
resource "google_compute_router" "foobar" {
name = "router-test-%s"
region = "${google_compute_subnetwork.foobar.region}"
network = "${google_compute_network.foobar.name}"
bgp {
asn = 64514
}
}
`, testId, testId, resourceRegion, testId)
}
func testAccComputeRouterNoRegion(providerRegion string) string {
testId := acctest.RandString(10)
return fmt.Sprintf(`
resource "google_compute_network" "foobar" {
name = "router-test-%s"
}
resource "google_compute_subnetwork" "foobar" {
name = "router-test-%s"
network = "${google_compute_network.foobar.self_link}"
ip_cidr_range = "10.0.0.0/16"
region = "%s"
}
resource "google_compute_router" "foobar" {
name = "router-test-%s"
network = "${google_compute_network.foobar.name}"
bgp {
asn = 64514
}
}
`, testId, testId, providerRegion, testId)
}
func testAccComputeRouterNetworkLink() string {
testId := acctest.RandString(10)
return fmt.Sprintf(`
resource "google_compute_network" "foobar" {
name = "router-test-%s"
}
resource "google_compute_subnetwork" "foobar" {
name = "router-test-%s"
network = "${google_compute_network.foobar.self_link}"
ip_cidr_range = "10.0.0.0/16"
region = "europe-west1"
}
resource "google_compute_router" "foobar" {
name = "router-test-%s"
region = "${google_compute_subnetwork.foobar.region}"
network = "${google_compute_network.foobar.self_link}"
bgp {
asn = 64514
}
}
`, testId, testId, testId)
}

View File

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"net" "net"
"strings"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
@ -75,6 +76,7 @@ func resourceComputeVpnTunnel() *schema.Resource {
Type: schema.TypeSet, Type: schema.TypeSet,
Optional: true, Optional: true,
ForceNew: true, ForceNew: true,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString}, Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString, Set: schema.HashString,
}, },
@ -91,6 +93,12 @@ func resourceComputeVpnTunnel() *schema.Resource {
ForceNew: true, ForceNew: true,
}, },
"router": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"self_link": &schema.Schema{ "self_link": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
Computed: true, Computed: true,
@ -155,6 +163,14 @@ func resourceComputeVpnTunnelCreate(d *schema.ResourceData, meta interface{}) er
vpnTunnel.Description = v.(string) vpnTunnel.Description = v.(string)
} }
if v, ok := d.GetOk("router"); ok {
routerLink, err := getRouterLink(config, project, region, v.(string))
if err != nil {
return err
}
vpnTunnel.Router = routerLink
}
op, err := vpnTunnelsService.Insert(project, region, vpnTunnel).Do() op, err := vpnTunnelsService.Insert(project, region, vpnTunnel).Do()
if err != nil { if err != nil {
return fmt.Errorf("Error Inserting VPN Tunnel %s : %s", name, err) return fmt.Errorf("Error Inserting VPN Tunnel %s : %s", name, err)
@ -325,3 +341,33 @@ var invalidPeerAddrs = []struct {
to: net.ParseIP("255.255.255.255"), to: net.ParseIP("255.255.255.255"),
}, },
} }
func getVpnTunnelLink(config *Config, project string, region string, tunnel string) (string, error) {
if !strings.HasPrefix(tunnel, "https://www.googleapis.com/compute/") {
// Tunnel value provided is just the name, lookup the tunnel SelfLink
tunnelData, err := config.clientCompute.VpnTunnels.Get(
project, region, tunnel).Do()
if err != nil {
return "", fmt.Errorf("Error reading tunnel: %s", err)
}
tunnel = tunnelData.SelfLink
}
return tunnel, nil
}
func getVpnTunnelName(vpntunnel string) (string, error) {
if strings.HasPrefix(vpntunnel, "https://www.googleapis.com/compute/") {
// extract the VPN tunnel name from SelfLink URL
vpntunnelName := vpntunnel[strings.LastIndex(vpntunnel, "/")+1:]
if vpntunnelName == "" {
return "", fmt.Errorf("VPN tunnel url not valid")
}
return vpntunnelName, nil
}
return vpntunnel, nil
}

View File

@ -32,6 +32,26 @@ func TestAccComputeVpnTunnel_basic(t *testing.T) {
}) })
} }
func TestAccComputeVpnTunnel_router(t *testing.T) {
router := fmt.Sprintf("tunnel-test-router-%s", acctest.RandString(10))
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeVpnTunnelDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeVpnTunnelRouter(router),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeVpnTunnelExists(
"google_compute_vpn_tunnel.foobar"),
resource.TestCheckResourceAttr(
"google_compute_vpn_tunnel.foobar", "router", router),
),
},
},
})
}
func TestAccComputeVpnTunnel_defaultTrafficSelectors(t *testing.T) { func TestAccComputeVpnTunnel_defaultTrafficSelectors(t *testing.T) {
resource.Test(t, resource.TestCase{ resource.Test(t, resource.TestCase{
@ -154,6 +174,69 @@ resource "google_compute_vpn_tunnel" "foobar" {
acctest.RandString(10), acctest.RandString(10), acctest.RandString(10), acctest.RandString(10), acctest.RandString(10), acctest.RandString(10),
acctest.RandString(10), acctest.RandString(10)) acctest.RandString(10), acctest.RandString(10))
func testAccComputeVpnTunnelRouter(router string) string {
testId := acctest.RandString(10)
return fmt.Sprintf(`
resource "google_compute_network" "foobar" {
name = "tunnel-test-%s"
}
resource "google_compute_subnetwork" "foobar" {
name = "tunnel-test-%s"
network = "${google_compute_network.foobar.self_link}"
ip_cidr_range = "10.0.0.0/16"
region = "us-central1"
}
resource "google_compute_address" "foobar" {
name = "tunnel-test-%s"
region = "${google_compute_subnetwork.foobar.region}"
}
resource "google_compute_vpn_gateway" "foobar" {
name = "tunnel-test-%s"
network = "${google_compute_network.foobar.self_link}"
region = "${google_compute_subnetwork.foobar.region}"
}
resource "google_compute_forwarding_rule" "foobar_esp" {
name = "tunnel-test-%s-1"
region = "${google_compute_vpn_gateway.foobar.region}"
ip_protocol = "ESP"
ip_address = "${google_compute_address.foobar.address}"
target = "${google_compute_vpn_gateway.foobar.self_link}"
}
resource "google_compute_forwarding_rule" "foobar_udp500" {
name = "tunnel-test-%s-2"
region = "${google_compute_forwarding_rule.foobar_esp.region}"
ip_protocol = "UDP"
port_range = "500-500"
ip_address = "${google_compute_address.foobar.address}"
target = "${google_compute_vpn_gateway.foobar.self_link}"
}
resource "google_compute_forwarding_rule" "foobar_udp4500" {
name = "tunnel-test-%s-3"
region = "${google_compute_forwarding_rule.foobar_udp500.region}"
ip_protocol = "UDP"
port_range = "4500-4500"
ip_address = "${google_compute_address.foobar.address}"
target = "${google_compute_vpn_gateway.foobar.self_link}"
}
resource "google_compute_router" "foobar"{
name = "%s"
region = "${google_compute_forwarding_rule.foobar_udp500.region}"
network = "${google_compute_network.foobar.self_link}"
bgp {
asn = 64514
}
}
resource "google_compute_vpn_tunnel" "foobar" {
name = "tunnel-test-%s"
region = "${google_compute_forwarding_rule.foobar_udp4500.region}"
target_vpn_gateway = "${google_compute_vpn_gateway.foobar.self_link}"
shared_secret = "unguessable"
peer_ip = "8.8.8.8"
router = "${google_compute_router.foobar.name}"
}
`, testId, testId, testId, testId, testId, testId, testId, router, testId)
}
var testAccComputeVpnTunnelDefaultTrafficSelectors = fmt.Sprintf(` var testAccComputeVpnTunnelDefaultTrafficSelectors = fmt.Sprintf(`
resource "google_compute_network" "foobar" { resource "google_compute_network" "foobar" {
name = "tunnel-test-%s" name = "tunnel-test-%s"

View File

@ -0,0 +1,151 @@
---
layout: "google"
page_title: "Google: google_compute_router"
sidebar_current: "docs-google-compute-router"
description: |-
Manages a Cloud Router resource.
---
# google\_compute\_router
Manages a Cloud Router resource. For more info, read the
[documentation](https://cloud.google.com/compute/docs/cloudrouter).
## Example Usage
```hcl
resource "google_compute_network" "foobar" {
name = "network-1"
}
resource "google_compute_subnetwork" "foobar" {
name = "subnet-1"
network = "${google_compute_network.foobar.self_link}"
ip_cidr_range = "10.0.0.0/16"
region = "us-central1"
}
resource "google_compute_address" "foobar" {
name = "vpn-gateway-1-address"
region = "${google_compute_subnetwork.foobar.region}"
}
resource "google_compute_vpn_gateway" "foobar" {
name = "vpn-gateway-1"
network = "${google_compute_network.foobar.self_link}"
region = "${google_compute_subnetwork.foobar.region}"
}
resource "google_compute_forwarding_rule" "foobar_esp" {
name = "vpn-gw-1-esp"
region = "${google_compute_vpn_gateway.foobar.region}"
ip_protocol = "ESP"
ip_address = "${google_compute_address.foobar.address}"
target = "${google_compute_vpn_gateway.foobar.self_link}"
}
resource "google_compute_forwarding_rule" "foobar_udp500" {
name = "vpn-gw-1-udp-500"
region = "${google_compute_forwarding_rule.foobar_esp.region}"
ip_protocol = "UDP"
port_range = "500-500"
ip_address = "${google_compute_address.foobar.address}"
target = "${google_compute_vpn_gateway.foobar.self_link}"
}
resource "google_compute_forwarding_rule" "foobar_udp4500" {
name = "vpn-gw-1-udp-4500"
region = "${google_compute_forwarding_rule.foobar_udp500.region}"
ip_protocol = "UDP"
port_range = "4500-4500"
ip_address = "${google_compute_address.foobar.address}"
target = "${google_compute_vpn_gateway.foobar.self_link}"
}
resource "google_compute_router" "foobar" {
name = "router-1"
region = "${google_compute_forwarding_rule.foobar_udp500.region}"
network = "${google_compute_network.foobar.self_link}"
bgp {
asn = 64512
}
}
resource "google_compute_vpn_tunnel" "foobar" {
name = "vpn-tunnel-1"
region = "${google_compute_forwarding_rule.foobar_udp4500.region}"
target_vpn_gateway = "${google_compute_vpn_gateway.foobar.self_link}"
shared_secret = "unguessable"
peer_ip = "8.8.8.8"
router = "${google_compute_router.foobar.name}"
}
resource "google_compute_router_interface" "foobar" {
name = "interface-1"
router = "${google_compute_router.foobar.name}"
region = "${google_compute_router.foobar.region}"
ip_range = "169.254.1.1/30"
vpn_tunnel = "${google_compute_vpn_tunnel.foobar.name}"
}
resource "google_compute_router_peer" "foobar" {
name = "peer-1"
router = "${google_compute_router.foobar.name}"
region = "${google_compute_router.foobar.region}"
peer_ip_address = "169.254.1.2"
peer_asn = 65513
advertised_route_priority = 100
interface = "${google_compute_router_interface.foobar.name}"
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) A unique name for the router, required by GCE. Changing
this forces a new router to be created.
* `network` - (Required) The name or resource link to the network this Cloud Router
will use to learn and announce routes. Changing this forces a new router to be created.
* `bgp` - (Required) BGP information specific to this router.
Changing this forces a new router to be created.
Structure is documented below.
- - -
* `description` - (Optional) A description of the resource.
Changing this forces a new router to be created.
* `project` - (Optional) The project in which the resource belongs. If it
is not provided, the provider project is used.
Changing this forces a new router to be created.
* `region` - (Optional) The region this router should sit in. If not specified,
the project region will be used. Changing this forces a new router to be
created.
- - -
The `bgp` block supports:
* `asn` - (Required) Local BGP Autonomous System Number (ASN). Must be an
RFC6996 private ASN.
## Attributes Reference
In addition to the arguments listed above, the following computed attributes are
exported:
* `self_link` - The URI of the created resource.
## Import
Routers can be imported using the `region` and `name`, e.g.
```
$ terraform import google_compute_router.router-1 us-central1/router-1
```

View File

@ -0,0 +1,62 @@
---
layout: "google"
page_title: "Google: google_compute_router_interface"
sidebar_current: "docs-google-compute-router-interface"
description: |-
Manages a Cloud Router interface.
---
# google\_compute\_router_interface
Manages a Cloud Router interface. For more info, read the
[documentation](https://cloud.google.com/compute/docs/cloudrouter).
## Example Usage
```hcl
resource "google_compute_router_interface" "foobar" {
name = "interface-1"
router = "router-1"
region = "us-central1"
ip_range = "169.254.1.1/30"
vpn_tunnel = "tunnel-1"
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) A unique name for the interface, required by GCE. Changing
this forces a new interface to be created.
* `router` - (Required) The name of the router this interface will be attached to.
Changing this forces a new interface to be created.
* `vpn_tunnel` - (Required) The name or resource link to the VPN tunnel this
interface will be linked to. Changing this forces a new interface to be created.
- - -
* `ip_range` - (Optional) IP address and range of the interface. The IP range must be
in the RFC3927 link-local IP space. Changing this forces a new interface to be created.
* `project` - (Optional) The project in which this interface's router belongs. If it
is not provided, the provider project is used. Changing this forces a new interface to be created.
* `region` - (Optional) The region this interface's router sits in. If not specified,
the project region will be used. Changing this forces a new interface to be
created.
## Attributes Reference
Only the arguments listed above are exposed as attributes.
## Import
Router interfaces can be imported using the `region`, `router` and `name`, e.g.
```
$ terraform import google_compute_router_interface.interface-1 us-central1/router-1/interface-1
```

View File

@ -0,0 +1,72 @@
---
layout: "google"
page_title: "Google: google_compute_router_peer"
sidebar_current: "docs-google-compute-router-peer"
description: |-
Manages a Cloud Router BGP peer.
---
# google\_compute\_router
Manages a Cloud Router BGP peer. For more info, read the
[documentation](https://cloud.google.com/compute/docs/cloudrouter).
## Example Usage
```hcl
resource "google_compute_router_peer" "foobar" {
name = "peer-1"
router = "router-1"
region = "us-central1"
peer_ip_address = "169.254.1.2"
peer_asn = 65513
advertised_route_priority = 100
interface = "interface-1"
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) A unique name for BGP peer, required by GCE. Changing
this forces a new peer to be created.
* `router` - (Required) The name of the router in which this BGP peer will be configured.
Changing this forces a new peer to be created.
* `interface` - (Required) The name of the interface the BGP peer is associated with.
Changing this forces a new peer to be created.
* `peer_ip_address` - (Required) IP address of the BGP interface outside Google Cloud.
Changing this forces a new peer to be created.
* `peer_asn` - (Required) Peer BGP Autonomous System Number (ASN).
Changing this forces a new peer to be created.
- - -
* `advertised_route_priority` - (Optional) The priority of routes advertised to this BGP peer.
Changing this forces a new peer to be created.
* `project` - (Optional) The project in which this peer's router belongs. If it
is not provided, the provider project is used. Changing this forces a new peer to be created.
* `region` - (Optional) The region this peer's router sits in. If not specified,
the project region will be used. Changing this forces a new peer to be
created.
## Attributes Reference
In addition to the arguments listed above, the following computed attributes are
exported:
* `ip_address` - IP address of the interface inside Google Cloud Platform.
## Import
Router BGP peers can be imported using the `region`, `router` and `name`, e.g.
```
$ terraform import google_compute_router_peer.peer-1 us-central1/router-1/peer-1
```

View File

@ -120,6 +120,10 @@ The following arguments are supported:
custom subnetted network. Refer to Google documentation for more custom subnetted network. Refer to Google documentation for more
information. information.
* `router` - (Optional) Name of a Cloud Router in the same region
to be used for dynamic routing. Refer to Google documentation for more
information.
* `project` - (Optional) The project in which the resource belongs. If it * `project` - (Optional) The project in which the resource belongs. If it
is not provided, the provider project is used. is not provided, the provider project is used.

View File

@ -150,6 +150,18 @@
<a href="/docs/providers/google/r/compute_route.html">google_compute_route</a> <a href="/docs/providers/google/r/compute_route.html">google_compute_route</a>
</li> </li>
<li<%= sidebar_current("docs-google-compute-router") %>>
<a href="/docs/providers/google/r/compute_router.html">google_compute_router</a>
</li>
<li<%= sidebar_current("docs-google-compute-router-interface") %>>
<a href="/docs/providers/google/r/compute_router_interface.html">google_compute_router_interface</a>
</li>
<li<%= sidebar_current("docs-google-compute-router-peer") %>>
<a href="/docs/providers/google/r/compute_router_peer.html">google_compute_router_peer</a>
</li>
<li<%= sidebar_current("docs-google-compute-snapshot") %>> <li<%= sidebar_current("docs-google-compute-snapshot") %>>
<a href="/docs/providers/google/r/compute_snapshot.html">google_compute_snapshot</a> <a href="/docs/providers/google/r/compute_snapshot.html">google_compute_snapshot</a>
</li> </li>