From b7b79b745b9598cf7320dfd4d0dec22fe30c49a1 Mon Sep 17 00:00:00 2001 From: Ty Larrabee Date: Fri, 24 May 2019 21:28:15 +0000 Subject: [PATCH] Add interconnect_attachment to router_interface Signed-off-by: Modular Magician --- google/resource_compute_router_interface.go | 48 +++++++-- .../resource_compute_router_interface_test.go | 97 ++++++++++++++++--- google/resource_compute_vpn_tunnel.go | 2 +- google/utils.go | 13 +++ .../r/compute_router_interface.html.markdown | 14 ++- 5 files changed, 151 insertions(+), 23 deletions(-) diff --git a/google/resource_compute_router_interface.go b/google/resource_compute_router_interface.go index c7e55d21b18..93ba9c1f1ef 100644 --- a/google/resource_compute_router_interface.go +++ b/google/resource_compute_router_interface.go @@ -20,6 +20,8 @@ func resourceComputeRouterInterface() *schema.Resource { State: resourceComputeRouterInterfaceImportState, }, + CustomizeDiff: routerInterfaceDiffOneOfCheck, + Schema: map[string]*schema.Schema{ "name": { Type: schema.TypeString, @@ -33,11 +35,18 @@ func resourceComputeRouterInterface() *schema.Resource { }, "vpn_tunnel": { Type: schema.TypeString, - Required: true, + ConflictsWith: []string{"interconnect_attachment"}, + Optional: true, + ForceNew: true, + DiffSuppressFunc: linkDiffSuppress, + }, + "interconnect_attachment": { + Type: schema.TypeString, + ConflictsWith: []string{"vpn_tunnel"}, + Optional: true, ForceNew: true, DiffSuppressFunc: linkDiffSuppress, }, - "ip_range": { Type: schema.TypeString, Optional: true, @@ -102,16 +111,26 @@ func resourceComputeRouterInterfaceCreate(d *schema.ResourceData, meta interface } } - vpnTunnel, err := getVpnTunnelLink(config, project, region, d.Get("vpn_tunnel").(string)) - if err != nil { - return err + iface := &compute.RouterInterface{Name: ifaceName} + + if ipVal, ok := d.GetOk("ip_range"); ok { + iface.IpRange = ipVal.(string) } - iface := &compute.RouterInterface{Name: ifaceName, - LinkedVpnTunnel: vpnTunnel} + if vpnVal, ok := d.GetOk("vpn_tunnel"); ok { + vpnTunnel, err := getVpnTunnelLink(config, project, region, vpnVal.(string)) + if err != nil { + return err + } + iface.LinkedVpnTunnel = vpnTunnel + } - if v, ok := d.GetOk("ip_range"); ok { - iface.IpRange = v.(string) + if icVal, ok := d.GetOk("interconnect_attachment"); ok { + interconnectAttachment, err := getInterconnectAttachmentLink(config, project, region, icVal.(string)) + if err != nil { + return err + } + iface.LinkedInterconnectAttachment = interconnectAttachment } log.Printf("[INFO] Adding interface %s", ifaceName) @@ -170,6 +189,7 @@ func resourceComputeRouterInterfaceRead(d *schema.ResourceData, meta interface{} if iface.Name == ifaceName { d.SetId(fmt.Sprintf("%s/%s/%s", region, routerName, ifaceName)) d.Set("vpn_tunnel", iface.LinkedVpnTunnel) + d.Set("interconnect_attachment", iface.LinkedInterconnectAttachment) d.Set("ip_range", iface.IpRange) d.Set("region", region) d.Set("project", project) @@ -271,3 +291,13 @@ func resourceComputeRouterInterfaceImportState(d *schema.ResourceData, meta inte return []*schema.ResourceData{d}, nil } + +func routerInterfaceDiffOneOfCheck(d *schema.ResourceDiff, meta interface{}) error { + _, ipOk := d.GetOk("ip_range") + _, vpnOk := d.GetOk("vpn_tunnel") + _, icOk := d.GetOk("interconnect_attachment") + if !(ipOk || vpnOk || icOk) { + return fmt.Errorf("Each interface requires one linked resource or an ip range, or both.") + } + return nil +} diff --git a/google/resource_compute_router_interface_test.go b/google/resource_compute_router_interface_test.go index c284d2c9fec..030cc3b1364 100644 --- a/google/resource_compute_router_interface_test.go +++ b/google/resource_compute_router_interface_test.go @@ -37,6 +37,29 @@ func TestAccComputeRouterInterface_basic(t *testing.T) { }) } +func TestAccComputeRouterInterface_withTunnel(t *testing.T) { + t.Parallel() + + testId := acctest.RandString(10) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRouterInterfaceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeRouterInterfaceWithTunnel(testId), + Check: testAccCheckComputeRouterInterfaceExists( + "google_compute_router_interface.foobar"), + }, + { + ResourceName: "google_compute_router_interface.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccCheckComputeRouterInterfaceDestroy(s *terraform.State) error { config := testAccProvider.Meta().(*Config) @@ -208,25 +231,70 @@ func testAccComputeRouterInterfaceBasic(testId string) string { 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) + `, 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-subnetwork-%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 + } + } + `, testId, testId, testId, testId, testId, testId, testId, testId) +} + +func testAccComputeRouterInterfaceWithTunnel(testId string) string { return fmt.Sprintf(` resource "google_compute_network" "foobar" { name = "router-interface-test-%s" @@ -285,5 +353,12 @@ func testAccComputeRouterInterfaceKeepRouter(testId string) string { peer_ip = "8.8.8.8" router = "${google_compute_router.foobar.name}" } - `, testId, testId, testId, testId, testId, testId, testId, testId, testId) + 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) } diff --git a/google/resource_compute_vpn_tunnel.go b/google/resource_compute_vpn_tunnel.go index 4c624a2b8b9..1f5a6bd8203 100644 --- a/google/resource_compute_vpn_tunnel.go +++ b/google/resource_compute_vpn_tunnel.go @@ -115,7 +115,7 @@ var invalidPeerAddrs = []struct { } func getVpnTunnelLink(config *Config, project string, region string, tunnel string) (string, error) { - if !strings.HasPrefix(tunnel, "https://www.googleapis.com/compute/") { + if !strings.Contains(tunnel, "/") { // Tunnel value provided is just the name, lookup the tunnel SelfLink tunnelData, err := config.clientCompute.VpnTunnels.Get( project, region, tunnel).Do() diff --git a/google/utils.go b/google/utils.go index a9a2404b2f0..b1032587920 100644 --- a/google/utils.go +++ b/google/utils.go @@ -466,3 +466,16 @@ func paginatedListRequest(baseUrl string, config *Config, flattener func(map[str return ls, nil } + +func getInterconnectAttachmentLink(config *Config, project, region, ic string) (string, error) { + if !strings.Contains(ic, "/") { + icData, err := config.clientCompute.InterconnectAttachments.Get( + project, region, ic).Do() + if err != nil { + return "", fmt.Errorf("Error reading interconnect attachment: %s", err) + } + ic = icData.SelfLink + } + + return ic, nil +} diff --git a/website/docs/r/compute_router_interface.html.markdown b/website/docs/r/compute_router_interface.html.markdown index 31417432a47..01f1df92507 100644 --- a/website/docs/r/compute_router_interface.html.markdown +++ b/website/docs/r/compute_router_interface.html.markdown @@ -35,14 +35,24 @@ The following arguments are supported: * `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. +In addition to the above required fields, a router interface must have specified +either `ip_range` or exactly one of `vpn_tunnel` or `interconnect_attachment`, +or both. - - - * `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. +* `vpn_tunnel` - (Optional) The name or resource link to the VPN tunnel this + interface will be linked to. Changing this forces a new interface to be created. Only + one of `vpn_tunnel` and `interconnect_attachment` can be specified. + +* `interconnect_attachment` - (Optional) The name or resource link to the + VLAN interconnect for this interface. Changing this forces a new interface to + be created. Only one of `vpn_tunnel` and `interconnect_attachment` can be + specified. + * `project` - (Optional) The ID of 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.