Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for inter-VPC communications in router_nat resource #9007

Merged
merged 8 commits into from
Sep 25, 2023
61 changes: 60 additions & 1 deletion mmv1/products/compute/RouterNat.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,18 @@ examples:
address_name1: 'nat-address1'
address_name2: 'nat-address2'
address_name3: 'nat-address3'
- !ruby/object:Provider::Terraform::Examples
name: 'router_nat_private'
primary_resource_id: 'nat_type'
skip_test: true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reason to skip this test?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeap. There is no generated_test for this resource. All tests are like this one and I put one test here to serve as an example and documentation for the users who may use this new feature.

min_version: beta
vars:
router_name: 'my-router'
nat_name: 'my-router-nat'
network_name: 'my-network'
subnet_name: 'my-subnetwork'
hub_name: 'my-hub'
spoke_name: 'my-spoke'
custom_code: !ruby/object:Provider::Terraform::CustomCode
constants: 'templates/terraform/constants/router_nat.go.erb'
custom_diff: [
Expand Down Expand Up @@ -128,7 +140,7 @@ properties:
function: 'verify.ValidateRFC1035Name(2, 63)'
- !ruby/object:Api::Type::Enum
name: 'natIpAllocateOption'
required: true
required: false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this field is no longer required, what is the default behaviour for an empty field?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say not sending the field through the request. For example: if you try to set this field while using the "type=PRIVATE" the api return an error with the following message: "Error creating RouterNat: googleapi: Error 400: Invalid value for field 'resource.nats[0].natIpAllocateOption': 'MANUAL_ONLY'. Setting natIpAllocateOption is unsupported with type=PRIVATE."

description: |
How external IPs should be allocated for this NAT. Valid values are
`AUTO_ONLY` for only allowing NAT IPs allocated by Google Cloud
Expand Down Expand Up @@ -368,10 +380,57 @@ properties:
description:
'A reference to an address associated with this NAT'
custom_expand: 'templates/terraform/custom_expand/array_resourceref_with_validation.go.erb'
- !ruby/object:Api::Type::Array
name: 'sourceNatActiveRanges'
min_version: beta
description: |
A list of URLs of the subnetworks used as source ranges for this NAT Rule.
These subnetworks must have purpose set to PRIVATE_NAT.
This field is used for private NAT.
is_set: true
set_hash_func: computeRouterNatRulesSubnetHash
custom_flatten: 'templates/terraform/custom_flatten/nat_rules_subnets_set.erb'
item_type: !ruby/object:Api::Type::ResourceRef
name: 'subnet'
resource: 'Subnetwork'
imports: 'selfLink'
description:
'A reference to a subnetwork address associated with this NAT'
custom_expand: 'templates/terraform/custom_expand/array_resourceref_with_validation.go.erb'
- !ruby/object:Api::Type::Array
name: 'sourceNatDrainRanges'
min_version: beta
description: |
A list of URLs of subnetworks representing source ranges to be drained.
This is only supported on patch/update, and these subnetworks must have previously been used as active ranges in this NAT Rule.
This field is used for private NAT.
is_set: true
set_hash_func: computeRouterNatRulesSubnetHash
custom_flatten: 'templates/terraform/custom_flatten/nat_rules_subnets_set.erb'
item_type: !ruby/object:Api::Type::ResourceRef
name: 'subnet'
resource: 'Subnetwork'
imports: 'selfLink'
description:
'A reference to a subnetwork address associated with this NAT'
custom_expand: 'templates/terraform/custom_expand/array_resourceref_with_validation.go.erb'
- !ruby/object:Api::Type::Boolean
name: enableEndpointIndependentMapping
description: |
Specifies if endpoint independent mapping is enabled. This is enabled by default. For more information
see the [official documentation](https://cloud.google.com/nat/docs/overview#specs-rfcs).
default_value: true
send_empty_value: true
- !ruby/object:Api::Type::Enum
name: 'type'
immutable: true
min_version: beta
description: |
Indicates whether this NAT is used for public or private IP translation.
If unspecified, it defaults to PUBLIC.
If `PUBLIC` NAT used for public IP translation.
If `PRIVATE` NAT used for private IP translation.
values:
- :PUBLIC
- :PRIVATE
default_value: :PUBLIC
38 changes: 37 additions & 1 deletion mmv1/templates/terraform/constants/router_nat.go.erb
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ func computeRouterNatIPsHash(v interface{}) int {
return schema.HashString(tpgresource.GetResourceNameFromSelfLink(val))
}

<% unless version == 'ga' -%>
func computeRouterNatRulesSubnetHash(v interface{}) int {
return computeRouterNatIPsHash(v)
}
<% end -%>

func computeRouterNatRulesHash(v interface{}) int {
obj := v.(map[string]interface{})
ruleNumber := obj["rule_number"].(int)
Expand All @@ -112,6 +118,12 @@ func computeRouterNatRulesHash(v interface{}) int {

sourceNatActiveIpHash := 0
sourceNatDrainIpHash := 0
<% unless version == 'ga' -%>
sourceNatActiveRangeHash := 0
sourceNatDrainRangeHash := 0
<% end -%>
routerNatRulesHash := 0

if obj["action"] != nil {
actions := obj["action"].([]interface{})
if len(actions) != 0 && actions[0] != nil {
Expand All @@ -134,8 +146,32 @@ func computeRouterNatRulesHash(v interface{}) int {
sourceNatDrainIpHash += schema.HashString(sourceNatDrainIpStr)
}
}

<% unless version == 'ga' -%>
sourceNatActiveRanges := action["source_nat_active_ranges"]
if sourceNatActiveRanges != nil {
sourceNatActiveRangesSet := sourceNatActiveRanges.(*schema.Set)
for _, sourceNatActiveRange := range sourceNatActiveRangesSet.List() {
sourceNatActiveRangeStr := fmt.Sprintf("source_nat_active_ranges-%d", computeRouterNatRulesSubnetHash(sourceNatActiveRange.(string)))
sourceNatActiveRangeHash += schema.HashString(sourceNatActiveRangeStr)
}
}

sourceNatDrainRanges := action["source_nat_drain_ranges"]
if sourceNatDrainRanges != nil {
sourceNatDrainRangesSet := sourceNatDrainRanges.(*schema.Set)
for _, sourceNatDrainRange := range sourceNatDrainRangesSet.List() {
sourceNatDrainRangeStr := fmt.Sprintf("source_nat_drain_ranges-%d", computeRouterNatRulesSubnetHash(sourceNatDrainRange.(string)))
sourceNatDrainRangeHash += schema.HashString(sourceNatDrainRangeStr)
}
}
<% end -%>
}
}

return ruleNumber + descriptionHash + schema.HashString(match) + sourceNatActiveIpHash + sourceNatDrainIpHash
routerNatRulesHash = ruleNumber + descriptionHash + schema.HashString(match) + sourceNatActiveIpHash + sourceNatDrainIpHash
<% unless version == 'ga' -%>
routerNatRulesHash += sourceNatActiveRangeHash + sourceNatDrainRangeHash
<% end -%>
return routerNatRulesHash
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
func flatten<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
if v == nil {
return v
}
return schema.NewSet(computeRouterNatRulesSubnetHash, tpgresource.ConvertStringArrToInterface(tpgresource.ConvertAndMapStringArr(v.([]interface{}), tpgresource.ConvertSelfLinkToV1)))
}
73 changes: 73 additions & 0 deletions mmv1/templates/terraform/examples/router_nat_private.tf.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
resource "google_compute_network" "net" {
provider = google-beta

name = "<%= ctx[:vars]['network_name'] %>"
}

resource "google_compute_subnetwork" "subnet" {
provider = google-beta

name = "<%= ctx[:vars]['subnet_name'] %>"
network = google_compute_network.net.id
ip_cidr_range = "10.0.0.0/16"
region = "us-central1"
purpose = "PRIVATE_NAT"
}

resource "google_compute_router" "router" {
provider = google-beta

name = "<%= ctx[:vars]['router_name'] %>"
region = google_compute_subnetwork.subnet.region
network = google_compute_network.net.id
}

resource "google_network_connectivity_hub" "hub" {
provider = google-beta

name = "<%= ctx[:vars]['hub_name'] %>"
description = "vpc hub for inter vpc nat"
}

resource "google_network_connectivity_spoke" "spoke" {
provider = google-beta

name = "<%= ctx[:vars]['spoke_name'] %>"
location = "global"
description = "vpc spoke for inter vpc nat"
hub = google_network_connectivity_hub.hub.id
linked_vpc_network {
exclude_export_ranges = [
"198.51.100.0/24",
"10.10.0.0/16"
]
uri = google_compute_network.net.self_link
}
}

resource "google_compute_router_nat" "<%= ctx[:primary_resource_id] %>" {
provider = google-beta

name = "<%= ctx[:vars]['nat_name'] %>"
router = google_compute_router.router.name
region = google_compute_router.router.region
source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS"
enable_dynamic_port_allocation = false
enable_endpoint_independent_mapping = false
min_ports_per_vm = 32
type = "PRIVATE"
subnetwork {
name = google_compute_subnetwork.subnet.id
source_ip_ranges_to_nat = ["ALL_IP_RANGES"]
}
rules {
rule_number = 100
description = "rule for private nat"
match = "nexthop.hub == \"//networkconnectivity.googleapis.com/projects/acm-test-proj-123/locations/global/hubs/<%= ctx[:vars]['hub_name'] %>\""
action {
source_nat_active_ranges = [
google_compute_subnetwork.subnet.self_link
]
}
}
}
Loading