From 71edccc94c58bd069efa67786a5adb39446bdb9b Mon Sep 17 00:00:00 2001 From: The Magician Date: Tue, 3 Jan 2023 09:09:11 -0800 Subject: [PATCH] [#11206] Add BGP Peer Router Appliance instance argument (#6874) (#13373) Co-authored-by: Luca Prete Signed-off-by: Modular Magician Signed-off-by: Modular Magician Co-authored-by: Luca Prete --- .changelog/6874.txt | 3 + .../resource_compute_router_bgp_peer_test.go | 132 ++++++++++++ google/resource_compute_router_interface.go | 6 + google/resource_compute_router_peer.go | 39 ++++ ...urce_compute_router_peer_generated_test.go | 191 ++++++++++++++++++ .../docs/r/compute_router_peer.html.markdown | 121 +++++++++++ 6 files changed, 492 insertions(+) create mode 100644 .changelog/6874.txt create mode 100644 google/resource_compute_router_peer_generated_test.go diff --git a/.changelog/6874.txt b/.changelog/6874.txt new file mode 100644 index 00000000000..fe080cfb597 --- /dev/null +++ b/.changelog/6874.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +compute: added `router_appliance_instance` field to `google_compute_router_bgp_peer` +``` diff --git a/google/resource_compute_router_bgp_peer_test.go b/google/resource_compute_router_bgp_peer_test.go index 2693bae8905..f5818f13774 100644 --- a/google/resource_compute_router_bgp_peer_test.go +++ b/google/resource_compute_router_bgp_peer_test.go @@ -155,6 +155,29 @@ func TestAccComputeRouterPeer_bfd(t *testing.T) { }) } +func TestAccComputeRouterPeer_routerApplianceInstance(t *testing.T) { + t.Parallel() + + routerName := fmt.Sprintf("tf-test-router-%s", randString(t, 10)) + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRouterPeerDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRouterPeerRouterApplianceInstance(routerName), + Check: testAccCheckComputeRouterPeerExists( + t, "google_compute_router_peer.foobar"), + }, + { + ResourceName: "google_compute_router_peer.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccCheckComputeRouterPeerDestroyProducer(t *testing.T) func(s *terraform.State) error { return func(s *terraform.State) error { config := googleProviderConfig(t) @@ -493,6 +516,115 @@ resource "google_compute_router_peer" "foobar" { `, routerName, routerName, routerName, routerName, routerName, routerName, routerName, routerName, routerName) } +func testAccComputeRouterPeerRouterApplianceInstance(routerName string) string { + return fmt.Sprintf(` +resource "google_compute_network" "foobar" { + name = "%s-net" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "foobar" { + name = "%s-sub" + network = google_compute_network.foobar.self_link + ip_cidr_range = "10.0.0.0/16" + region = "us-central1" +} + +resource "google_compute_address" "addr_intf" { + name = "%s-addr-intf" + region = google_compute_subnetwork.foobar.region + subnetwork = google_compute_subnetwork.foobar.id + address_type = "INTERNAL" +} + +resource "google_compute_address" "addr_intf_red" { + name = "%s-addr-intf-red" + region = google_compute_subnetwork.foobar.region + subnetwork = google_compute_subnetwork.foobar.id + address_type = "INTERNAL" +} + +resource "google_compute_address" "addr_peer" { + name = "%s-addr-peer" + region = google_compute_subnetwork.foobar.region + subnetwork = google_compute_subnetwork.foobar.id + address_type = "INTERNAL" +} + +resource "google_compute_instance" "foobar" { + name = "%s-vm" + machine_type = "e2-medium" + zone = "us-central1-a" + can_ip_forward = true + + boot_disk { + initialize_params { + image = "debian-cloud/debian-11" + } + } + + network_interface { + network_ip = google_compute_address.addr_peer.address + subnetwork = google_compute_subnetwork.foobar.self_link + } +} + +resource "google_network_connectivity_hub" "foobar" { + name = "%s-hub" +} + +resource "google_network_connectivity_spoke" "foobar" { + name = "%s-spoke" + location = google_compute_subnetwork.foobar.region + hub = google_network_connectivity_hub.foobar.id + + linked_router_appliance_instances { + instances { + virtual_machine = google_compute_instance.foobar.self_link + ip_address = google_compute_address.addr_peer.address + } + site_to_site_data_transfer = false + } +} + +resource "google_compute_router" "foobar" { + name = "%s-ra" + region = google_compute_subnetwork.foobar.region + network = google_compute_network.foobar.self_link + bgp { + asn = 64514 + } +} + +resource "google_compute_router_interface" "foobar_redundant" { + name = "%s-intf-red" + region = google_compute_router.foobar.region + router = google_compute_router.foobar.name + subnetwork = google_compute_subnetwork.foobar.self_link + private_ip_address = google_compute_address.addr_intf_red.address +} + +resource "google_compute_router_interface" "foobar" { + name = "%s-intf" + region = google_compute_router.foobar.region + router = google_compute_router.foobar.name + subnetwork = google_compute_subnetwork.foobar.self_link + private_ip_address = google_compute_address.addr_intf.address + redundant_interface = google_compute_router_interface.foobar_redundant.name +} + +resource "google_compute_router_peer" "foobar" { + name = "%s-peer" + router = google_compute_router.foobar.name + region = google_compute_router.foobar.region + peer_ip_address = google_compute_address.addr_peer.address + peer_asn = 65515 + interface = google_compute_router_interface.foobar.name + router_appliance_instance = google_compute_instance.foobar.self_link +} +`, routerName, routerName, routerName, routerName, routerName, routerName, routerName, routerName, routerName, routerName, routerName, routerName) +} + func testAccComputeRouterPeerAdvertiseModeUpdate(routerName string) string { return fmt.Sprintf(` resource "google_compute_network" "foobar" { diff --git a/google/resource_compute_router_interface.go b/google/resource_compute_router_interface.go index 26d08f4a7e9..b7488836375 100644 --- a/google/resource_compute_router_interface.go +++ b/google/resource_compute_router_interface.go @@ -99,6 +99,7 @@ func resourceComputeRouterInterface() *schema.Resource { "redundant_interface": { Type: schema.TypeString, Optional: true, + Computed: true, ForceNew: true, Description: `The name of the interface that is redundant to this interface. Changing this forces a new interface to be created.`, }, @@ -323,6 +324,11 @@ func resourceComputeRouterInterfaceDelete(d *schema.ResourceData, meta interface ifaceFound = true continue } else { + // If this is a redundant interface, + // remove its reference from other interfaces as well + if iface.RedundantInterface == ifaceName { + iface.RedundantInterface = "" + } newIfaces = append(newIfaces, iface) } } diff --git a/google/resource_compute_router_peer.go b/google/resource_compute_router_peer.go index 9d582c4ce76..ce8cbec6586 100644 --- a/google/resource_compute_router_peer.go +++ b/google/resource_compute_router_peer.go @@ -211,6 +211,15 @@ Only IPv4 is supported.`, DiffSuppressFunc: compareSelfLinkOrResourceName, Description: `Region where the router and BgpPeer reside. If it is not provided, the provider region is used.`, + }, + "router_appliance_instance": { + Type: schema.TypeString, + Optional: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + Description: `The URI of the VM instance that is used as third-party router appliances +such as Next Gen Firewalls, Virtual Routers, or Router Appliances. +The VM instance must be located in zones contained in the same region as +this Cloud Router. The VM instance is the peer side of the BGP session.`, }, "management_type": { Type: schema.TypeString, @@ -311,6 +320,12 @@ func resourceComputeRouterBgpPeerCreate(d *schema.ResourceData, meta interface{} } else if v, ok := d.GetOkExists("enable"); ok || !reflect.DeepEqual(v, enableProp) { obj["enable"] = enableProp } + routerApplianceInstanceProp, err := expandNestedComputeRouterBgpPeerRouterApplianceInstance(d.Get("router_appliance_instance"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("router_appliance_instance"); !isEmptyValue(reflect.ValueOf(routerApplianceInstanceProp)) && (ok || !reflect.DeepEqual(v, routerApplianceInstanceProp)) { + obj["routerApplianceInstance"] = routerApplianceInstanceProp + } lockName, err := replaceVars(d, config, "router/{{region}}/{{router}}") if err != nil { @@ -452,6 +467,9 @@ func resourceComputeRouterBgpPeerRead(d *schema.ResourceData, meta interface{}) if err := d.Set("enable", flattenNestedComputeRouterBgpPeerEnable(res["enable"], d, config)); err != nil { return fmt.Errorf("Error reading RouterBgpPeer: %s", err) } + if err := d.Set("router_appliance_instance", flattenNestedComputeRouterBgpPeerRouterApplianceInstance(res["routerApplianceInstance"], d, config)); err != nil { + return fmt.Errorf("Error reading RouterBgpPeer: %s", err) + } return nil } @@ -526,6 +544,12 @@ func resourceComputeRouterBgpPeerUpdate(d *schema.ResourceData, meta interface{} } else if v, ok := d.GetOkExists("enable"); ok || !reflect.DeepEqual(v, enableProp) { obj["enable"] = enableProp } + routerApplianceInstanceProp, err := expandNestedComputeRouterBgpPeerRouterApplianceInstance(d.Get("router_appliance_instance"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("router_appliance_instance"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, routerApplianceInstanceProp)) { + obj["routerApplianceInstance"] = routerApplianceInstanceProp + } lockName, err := replaceVars(d, config, "router/{{region}}/{{router}}") if err != nil { @@ -827,6 +851,13 @@ func flattenNestedComputeRouterBgpPeerEnable(v interface{}, d *schema.ResourceDa return b } +func flattenNestedComputeRouterBgpPeerRouterApplianceInstance(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return ConvertSelfLinkToV1(v.(string)) +} + func expandNestedComputeRouterBgpPeerName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } @@ -960,6 +991,14 @@ func expandNestedComputeRouterBgpPeerEnable(v interface{}, d TerraformResourceDa return strings.ToUpper(strconv.FormatBool(v.(bool))), nil } +func expandNestedComputeRouterBgpPeerRouterApplianceInstance(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + f, err := parseZonalFieldValue("instances", v.(string), "project", "zone", d, config, true) + if err != nil { + return nil, fmt.Errorf("Invalid value for router_appliance_instance: %s", err) + } + return f.RelativeLink(), nil +} + func flattenNestedComputeRouterBgpPeer(d *schema.ResourceData, meta interface{}, res map[string]interface{}) (map[string]interface{}, error) { var v interface{} var ok bool diff --git a/google/resource_compute_router_peer_generated_test.go b/google/resource_compute_router_peer_generated_test.go new file mode 100644 index 00000000000..309abce7383 --- /dev/null +++ b/google/resource_compute_router_peer_generated_test.go @@ -0,0 +1,191 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccComputeRouterBgpPeer_routerPeerRouterApplianceExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRouterBgpPeerDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRouterBgpPeer_routerPeerRouterApplianceExample(context), + }, + { + ResourceName: "google_compute_router_peer.peer", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"router_appliance_instance", "router", "region"}, + }, + }, + }) +} + +func testAccComputeRouterBgpPeer_routerPeerRouterApplianceExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_compute_network" "network" { + name = "tf-test-my-router%{random_suffix}-net" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "subnetwork" { + name = "tf-test-my-router%{random_suffix}-sub" + network = google_compute_network.network.self_link + ip_cidr_range = "10.0.0.0/16" + region = "us-central1" +} + +resource "google_compute_address" "addr_intf" { + name = "tf-test-my-router%{random_suffix}-addr-intf" + region = google_compute_subnetwork.subnetwork.region + subnetwork = google_compute_subnetwork.subnetwork.id + address_type = "INTERNAL" +} + +resource "google_compute_address" "addr_intf_redundant" { + name = "tf-test-my-router%{random_suffix}-addr-intf-red" + region = google_compute_subnetwork.subnetwork.region + subnetwork = google_compute_subnetwork.subnetwork.id + address_type = "INTERNAL" +} + +resource "google_compute_address" "addr_peer" { + name = "tf-test-my-router%{random_suffix}-addr-peer" + region = google_compute_subnetwork.subnetwork.region + subnetwork = google_compute_subnetwork.subnetwork.id + address_type = "INTERNAL" +} + +resource "google_compute_instance" "instance" { + name = "router-appliance" + zone = "us-central1-a" + machine_type = "e2-medium" + can_ip_forward = true + + boot_disk { + initialize_params { + image = "debian-cloud/debian-11" + } + } + + network_interface { + network_ip = google_compute_address.addr_peer.address + subnetwork = google_compute_subnetwork.subnetwork.self_link + } +} + +resource "google_network_connectivity_hub" "hub" { + name = "tf-test-my-router%{random_suffix}-hub" +} + +resource "google_network_connectivity_spoke" "spoke" { + name = "tf-test-my-router%{random_suffix}-spoke" + location = google_compute_subnetwork.subnetwork.region + hub = google_network_connectivity_hub.hub.id + + linked_router_appliance_instances { + instances { + virtual_machine = google_compute_instance.instance.self_link + ip_address = google_compute_address.addr_peer.address + } + site_to_site_data_transfer = false + } +} + +resource "google_compute_router" "router" { + name = "tf-test-my-router%{random_suffix}-router" + region = google_compute_subnetwork.subnetwork.region + network = google_compute_network.network.self_link + bgp { + asn = 64514 + } +} + +resource "google_compute_router_interface" "interface_redundant" { + name = "tf-test-my-router%{random_suffix}-intf-red" + region = google_compute_router.router.region + router = google_compute_router.router.name + subnetwork = google_compute_subnetwork.subnetwork.self_link + private_ip_address = google_compute_address.addr_intf_redundant.address +} + +resource "google_compute_router_interface" "interface" { + name = "tf-test-my-router%{random_suffix}-intf" + region = google_compute_router.router.region + router = google_compute_router.router.name + subnetwork = google_compute_subnetwork.subnetwork.self_link + private_ip_address = google_compute_address.addr_intf.address + redundant_interface = google_compute_router_interface.interface_redundant.name +} + +resource "google_compute_router_peer" "peer" { + name = "tf-test-my-router-peer%{random_suffix}" + router = google_compute_router.router.name + region = google_compute_router.router.region + interface = google_compute_router_interface.interface.name + router_appliance_instance = google_compute_instance.instance.self_link + peer_asn = 65513 + peer_ip_address = google_compute_address.addr_peer.address +} +`, context) +} + +func testAccCheckComputeRouterBgpPeerDestroyProducer(t *testing.T) func(s *terraform.State) error { + return func(s *terraform.State) error { + for name, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_router_peer" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := googleProviderConfig(t) + + url, err := replaceVarsForTest(config, rs, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/routers/{{router}}") + if err != nil { + return err + } + + billingProject := "" + + if config.BillingProject != "" { + billingProject = config.BillingProject + } + + _, err = sendRequest(config, "GET", billingProject, url, config.userAgent, nil) + if err == nil { + return fmt.Errorf("ComputeRouterBgpPeer still exists at %s", url) + } + } + + return nil + } +} diff --git a/website/docs/r/compute_router_peer.html.markdown b/website/docs/r/compute_router_peer.html.markdown index b9be3b20278..57844681a65 100644 --- a/website/docs/r/compute_router_peer.html.markdown +++ b/website/docs/r/compute_router_peer.html.markdown @@ -83,6 +83,120 @@ resource "google_compute_router_peer" "peer" { } } ``` + +## Example Usage - Router Peer Router Appliance + + +```hcl +resource "google_compute_network" "network" { + name = "my-router-net" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "subnetwork" { + name = "my-router-sub" + network = google_compute_network.network.self_link + ip_cidr_range = "10.0.0.0/16" + region = "us-central1" +} + +resource "google_compute_address" "addr_intf" { + name = "my-router-addr-intf" + region = google_compute_subnetwork.subnetwork.region + subnetwork = google_compute_subnetwork.subnetwork.id + address_type = "INTERNAL" +} + +resource "google_compute_address" "addr_intf_redundant" { + name = "my-router-addr-intf-red" + region = google_compute_subnetwork.subnetwork.region + subnetwork = google_compute_subnetwork.subnetwork.id + address_type = "INTERNAL" +} + +resource "google_compute_address" "addr_peer" { + name = "my-router-addr-peer" + region = google_compute_subnetwork.subnetwork.region + subnetwork = google_compute_subnetwork.subnetwork.id + address_type = "INTERNAL" +} + +resource "google_compute_instance" "instance" { + name = "router-appliance" + zone = "us-central1-a" + machine_type = "e2-medium" + can_ip_forward = true + + boot_disk { + initialize_params { + image = "debian-cloud/debian-11" + } + } + + network_interface { + network_ip = google_compute_address.addr_peer.address + subnetwork = google_compute_subnetwork.subnetwork.self_link + } +} + +resource "google_network_connectivity_hub" "hub" { + name = "my-router-hub" +} + +resource "google_network_connectivity_spoke" "spoke" { + name = "my-router-spoke" + location = google_compute_subnetwork.subnetwork.region + hub = google_network_connectivity_hub.hub.id + + linked_router_appliance_instances { + instances { + virtual_machine = google_compute_instance.instance.self_link + ip_address = google_compute_address.addr_peer.address + } + site_to_site_data_transfer = false + } +} + +resource "google_compute_router" "router" { + name = "my-router-router" + region = google_compute_subnetwork.subnetwork.region + network = google_compute_network.network.self_link + bgp { + asn = 64514 + } +} + +resource "google_compute_router_interface" "interface_redundant" { + name = "my-router-intf-red" + region = google_compute_router.router.region + router = google_compute_router.router.name + subnetwork = google_compute_subnetwork.subnetwork.self_link + private_ip_address = google_compute_address.addr_intf_redundant.address +} + +resource "google_compute_router_interface" "interface" { + name = "my-router-intf" + region = google_compute_router.router.region + router = google_compute_router.router.name + subnetwork = google_compute_subnetwork.subnetwork.self_link + private_ip_address = google_compute_address.addr_intf.address + redundant_interface = google_compute_router_interface.interface_redundant.name +} + +resource "google_compute_router_peer" "peer" { + name = "my-router-peer" + router = google_compute_router.router.name + region = google_compute_router.router.region + interface = google_compute_router_interface.interface.name + router_appliance_instance = google_compute_instance.instance.self_link + peer_asn = 65513 + peer_ip_address = google_compute_address.addr_peer.address +} +``` ## Argument Reference @@ -172,6 +286,13 @@ The following arguments are supported: If set to true, the peer connection can be established with routing information. The default is true. +* `router_appliance_instance` - + (Optional) + The URI of the VM instance that is used as third-party router appliances + such as Next Gen Firewalls, Virtual Routers, or Router Appliances. + The VM instance must be located in zones contained in the same region as + this Cloud Router. The VM instance is the peer side of the BGP session. + * `region` - (Optional) Region where the router and BgpPeer reside.