diff --git a/mmv1/products/compute/RouterNat.yaml b/mmv1/products/compute/RouterNat.yaml index e260b5c67fea..9b0d414391a1 100644 --- a/mmv1/products/compute/RouterNat.yaml +++ b/mmv1/products/compute/RouterNat.yaml @@ -105,6 +105,8 @@ examples: spoke_name: 'my-spoke' custom_code: !ruby/object:Provider::Terraform::CustomCode constants: 'templates/terraform/constants/router_nat.go.erb' + pre_create: 'templates/terraform/constants/router_nat_validate_action_active_range.go.erb' + pre_update: 'templates/terraform/constants/router_nat_validate_action_active_range.go.erb' custom_diff: [ 'resourceComputeRouterNatDrainNatIpsCustomDiff', ] diff --git a/mmv1/templates/terraform/constants/router_nat_validate_action_active_range.go.erb b/mmv1/templates/terraform/constants/router_nat_validate_action_active_range.go.erb new file mode 100644 index 000000000000..635e90f85472 --- /dev/null +++ b/mmv1/templates/terraform/constants/router_nat_validate_action_active_range.go.erb @@ -0,0 +1,29 @@ +<% unless version == 'ga' -%> +// validates if the field action.source_nat_active_ranges is filled when the type is PRIVATE. +natType := d.Get("type").(string) +if natType == "PRIVATE" { + rules := d.Get("rules").(*schema.Set) + for _, rule := range rules.List() { + objRule := rule.(map[string]interface{}) + actions := objRule["action"].([]interface{}) + + containAction := len(actions) != 0 && actions[0] != nil + containActiveRange := true + + if containAction { + action := actions[0].(map[string]interface{}) + sourceNatActiveRanges := action["source_nat_active_ranges"] + if sourceNatActiveRanges != nil { + sourceNatActiveRangesSet := sourceNatActiveRanges.(*schema.Set) + if len(sourceNatActiveRangesSet.List()) == 0 { + containActiveRange = false + } + } + } + + if !containAction || !containActiveRange { + return fmt.Errorf("The rule for PRIVATE nat type must contain an action with source_nat_active_ranges set") + } + } +} +<% end -%> \ No newline at end of file diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_router_nat_test.go.erb b/mmv1/third_party/terraform/services/compute/resource_compute_router_nat_test.go.erb index 8c0c032a716a..b1c17157506f 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_router_nat_test.go.erb +++ b/mmv1/third_party/terraform/services/compute/resource_compute_router_nat_test.go.erb @@ -533,6 +533,86 @@ func TestAccComputeRouterNat_withPrivateNatAndRules(t *testing.T) { }, }) } + +func TestAccComputeRouterNat_withPrivateNatAndEmptyAction(t *testing.T) { + t.Parallel() + + testId := acctest.RandString(t, 10) + routerName := fmt.Sprintf("tf-test-router-private-nat-%s", testId) + hubName := fmt.Sprintf("%s-hub", routerName) + pEnv := envvar.GetTestProjectFromEnv() + ruleDescription := acctest.RandString(t, 10) + match := fmt.Sprintf("nexthop.hub == '//networkconnectivity.googleapis.com/projects/%s/locations/global/hubs/%s'", pEnv, hubName) + activeRangesNetworkOne := "google_compute_subnetwork.subnet1.self_link" + drainRangesEmpty := "" + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckComputeRouterNatDestroyProducer(t), + Steps: []resource.TestStep{ + // (ERROR): Creation with empty action should fail + { + Config: testAccComputeRouterNatRulesBasic_privateNatWithRuleAndEmptyAction(routerName, hubName, 100, ruleDescription, match), + ExpectError: regexp.MustCompile("The rule for PRIVATE nat type must contain an action with source_nat_active_ranges set"), + }, + // Create NAT with action and active ranges set + { + Config: testAccComputeRouterNatRulesBasic_privateNatWithRuleAndActiveDrainRange(routerName, hubName, 100, ruleDescription, match, activeRangesNetworkOne, drainRangesEmpty), + }, + { + ResourceName: "google_compute_router_nat.foobar", + ImportState: true, + ImportStateVerify: true, + }, + // (ERROR) - Updating the rule by removing the action should fail + { + Config: testAccComputeRouterNatRulesBasic_privateNatWithRuleAndEmptyAction(routerName, hubName, 100, ruleDescription, match), + ExpectError: regexp.MustCompile("The rule for PRIVATE nat type must contain an action with source_nat_active_ranges set"), + }, + }, + }) +} + +func TestAccComputeRouterNat_withPrivateNatAndEmptyActionActiveRanges(t *testing.T) { + t.Parallel() + + testId := acctest.RandString(t, 10) + routerName := fmt.Sprintf("tf-test-router-private-nat-%s", testId) + hubName := fmt.Sprintf("%s-hub", routerName) + pEnv := envvar.GetTestProjectFromEnv() + ruleDescription := acctest.RandString(t, 10) + match := fmt.Sprintf("nexthop.hub == '//networkconnectivity.googleapis.com/projects/%s/locations/global/hubs/%s'", pEnv, hubName) + activeRangesNetworkOne := "google_compute_subnetwork.subnet1.self_link" + drainRangesEmpty := "" + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckComputeRouterNatDestroyProducer(t), + Steps: []resource.TestStep{ + // (ERROR): Creation with empty action active ranges should fail + { + Config: testAccComputeRouterNatRulesBasic_privateNatWithRuleAndEmptyActionActiveRanges(routerName, hubName, 100, ruleDescription, match), + ExpectError: regexp.MustCompile("The rule for PRIVATE nat type must contain an action with source_nat_active_ranges set"), + }, + // Create NAT with action and active ranges set + { + Config: testAccComputeRouterNatRulesBasic_privateNatWithRuleAndActiveDrainRange(routerName, hubName, 100, ruleDescription, match, activeRangesNetworkOne, drainRangesEmpty), + }, + { + ResourceName: "google_compute_router_nat.foobar", + ImportState: true, + ImportStateVerify: true, + }, + // (ERROR) - Updating the rule by erasing the action active ranges should fail + { + Config: testAccComputeRouterNatRulesBasic_privateNatWithRuleAndEmptyActionActiveRanges(routerName, hubName, 100, ruleDescription, match), + ExpectError: regexp.MustCompile("The rule for PRIVATE nat type must contain an action with source_nat_active_ranges set"), + }, + }, + }) +} <% end -%> func testAccCheckComputeRouterNatDestroyProducer(t *testing.T) func(s *terraform.State) error { @@ -1655,4 +1735,62 @@ resource "google_compute_router_nat" "foobar" { `, testAccComputeRouterNatBaseResourcesWithPrivateNatSubnetworks(routerName, hubName), routerName, ruleNumber, ruleDescription, match, activeRanges, drainRanges) } +func testAccComputeRouterNatRulesBasic_privateNatWithRuleAndEmptyAction(routerName, hubName string, ruleNumber int, ruleDescription, match string) string { + return fmt.Sprintf(` +%s + +resource "google_compute_router_nat" "foobar" { + name = "%s" + router = google_compute_router.foobar.name + region = google_compute_router.foobar.region + source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS" + type = "PRIVATE" + enable_dynamic_port_allocation = false + enable_endpoint_independent_mapping = false + min_ports_per_vm = 32 + subnetwork { + name = google_compute_subnetwork.subnet1.id + source_ip_ranges_to_nat = ["ALL_IP_RANGES"] + } + + rules { + rule_number = %d + description = "%s" + match = "%s" + # action is missing + } +} +`, testAccComputeRouterNatBaseResourcesWithPrivateNatSubnetworks(routerName, hubName), routerName, ruleNumber, ruleDescription, match) +} + +func testAccComputeRouterNatRulesBasic_privateNatWithRuleAndEmptyActionActiveRanges(routerName, hubName string, ruleNumber int, ruleDescription, match string) string { + return fmt.Sprintf(` +%s + +resource "google_compute_router_nat" "foobar" { + name = "%s" + router = google_compute_router.foobar.name + region = google_compute_router.foobar.region + source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS" + type = "PRIVATE" + enable_dynamic_port_allocation = false + enable_endpoint_independent_mapping = false + min_ports_per_vm = 32 + subnetwork { + name = google_compute_subnetwork.subnet1.id + source_ip_ranges_to_nat = ["ALL_IP_RANGES"] + } + + rules { + rule_number = %d + description = "%s" + match = "%s" + action { + source_nat_active_ranges = [] + } + } +} +`, testAccComputeRouterNatBaseResourcesWithPrivateNatSubnetworks(routerName, hubName), routerName, ruleNumber, ruleDescription, match) +} + <% end -%> \ No newline at end of file