From bb90373db68956a1ac1424f3de2b1b9d4fef2d2b Mon Sep 17 00:00:00 2001 From: Swamita Gupta Date: Wed, 22 May 2024 06:53:14 +0000 Subject: [PATCH] Add Delayed Deletion to Vmwareengine Private Cloud --- mmv1/products/vmwareengine/PrivateCloud.yaml | 17 ++- .../vmwareengine_private_cloud.go.erb | 33 +++++ .../vmwareengine_private_cloud_type.go.erb | 6 - .../vmware_engine_private_cloud_basic.tf.erb | 1 - .../vmware_engine_private_cloud_full.tf.erb | 3 +- .../post_delete/private_cloud.go.erb | 7 + .../vmwareengine_private_cloud.go.erb | 8 ++ ..._vmwareengine_external_access_rule_test.go | 2 + ...urce_vmwareengine_external_address_test.go | 2 + ...esource_vmwareengine_private_cloud_test.go | 129 +++++++++++------- .../resource_vmwareengine_subnet_test.go | 2 + 11 files changed, 149 insertions(+), 61 deletions(-) create mode 100644 mmv1/templates/terraform/constants/vmwareengine_private_cloud.go.erb delete mode 100644 mmv1/templates/terraform/constants/vmwareengine_private_cloud_type.go.erb create mode 100644 mmv1/templates/terraform/pre_delete/vmwareengine_private_cloud.go.erb diff --git a/mmv1/products/vmwareengine/PrivateCloud.yaml b/mmv1/products/vmwareengine/PrivateCloud.yaml index 3d7e3db1381f..3910ae34e454 100644 --- a/mmv1/products/vmwareengine/PrivateCloud.yaml +++ b/mmv1/products/vmwareengine/PrivateCloud.yaml @@ -15,7 +15,7 @@ name: 'PrivateCloud' base_url: 'projects/{{project}}/locations/{{location}}/privateClouds' self_link: 'projects/{{project}}/locations/{{location}}/privateClouds/{{name}}' -delete_url: 'projects/{{project}}/locations/{{location}}/privateClouds/{{name}}?delay_hours=0' +delete_url: 'projects/{{project}}/locations/{{location}}/privateClouds/{{name}}' create_url: 'projects/{{project}}/locations/{{location}}/privateClouds?privateCloudId={{name}}' update_verb: :PATCH references: !ruby/object:Api::Resource::ReferenceLinks @@ -45,11 +45,12 @@ async: !ruby/object:Api::OpAsync import_format: ["projects/{{project}}/locations/{{location}}/privateClouds/{{name}}"] autogen_async: true custom_code: !ruby/object:Provider::Terraform::CustomCode - post_delete: "templates/terraform/post_delete/private_cloud.go.erb" + constants: templates/terraform/constants/vmwareengine_private_cloud.go.erb decoder: "templates/terraform/decoders/private_cloud.go.erb" + pre_delete: templates/terraform/pre_delete/vmwareengine_private_cloud.go.erb + post_delete: "templates/terraform/post_delete/private_cloud.go.erb" post_update: "templates/terraform/post_update/private_cloud.go.erb" update_encoder: "templates/terraform/update_encoder/private_cloud.go.erb" - constants: templates/terraform/constants/vmwareengine_private_cloud_type.go.erb examples: - !ruby/object:Provider::Terraform::Examples name: "vmware_engine_private_cloud_basic" @@ -90,6 +91,16 @@ parameters: description: | The ID of the PrivateCloud. +virtual_fields: + - !ruby/object:Api::Type::Integer + name: "deletion_delay_hours" + description: | + The number of hours to delay this request. You can set this value to an hour between 0 to 8, where setting it to 0 starts the deletion request immediately. If no value is set, a default value is set at the API Level. + - !ruby/object:Api::Type::Boolean + name: "send_deletion_delay_hours_if_zero" + description: | + While set true, deletion_delay_hours value will be sent in the request even for zero value of the field. This field is only useful for setting 0 value to the deletion_delay_hours field. It can be used both alone and together with deletion_delay_hours. + properties: - !ruby/object:Api::Type::String name: 'description' diff --git a/mmv1/templates/terraform/constants/vmwareengine_private_cloud.go.erb b/mmv1/templates/terraform/constants/vmwareengine_private_cloud.go.erb new file mode 100644 index 000000000000..b2941d68a57c --- /dev/null +++ b/mmv1/templates/terraform/constants/vmwareengine_private_cloud.go.erb @@ -0,0 +1,33 @@ +func vmwareenginePrivateCloudStandardTypeDiffSuppressFunc(_, old, new string, d *schema.ResourceData) bool { + if (old == "STANDARD" && new == "") || (old == "" && new == "STANDARD") { + return true + } + if (isMultiNodePrivateCloud(d) && old == "TIME_LIMITED" && new == "STANDARD") { + log.Printf("[DEBUG] Multinode Private Cloud found, facilitating TYPE change to STANDARD") + return true + } + return false +} + +func isMultiNodePrivateCloud(d *schema.ResourceData) bool { + nodeConfigMap := d.Get("management_cluster.0.node_type_configs").(*schema.Set).List() + totalNodeCount := 0 + for _, nodeConfig := range nodeConfigMap { + configMap, ok := nodeConfig.(map[string]interface{}) + if !ok { + log.Printf("[DEBUG] Invalid node configuration format for private cloud.") + continue + } + nodeCount, ok := configMap["node_count"].(int) + if !ok { + log.Printf("[DEBUG] Invalid node_count format for private cloud.") + continue + } + totalNodeCount += nodeCount + } + log.Printf("[DEBUG] The node count of the private cloud is found to be %v nodes.", totalNodeCount) + if totalNodeCount > 2 { + return true + } + return false +} diff --git a/mmv1/templates/terraform/constants/vmwareengine_private_cloud_type.go.erb b/mmv1/templates/terraform/constants/vmwareengine_private_cloud_type.go.erb deleted file mode 100644 index 7ad51797adc9..000000000000 --- a/mmv1/templates/terraform/constants/vmwareengine_private_cloud_type.go.erb +++ /dev/null @@ -1,6 +0,0 @@ -func vmwareenginePrivateCloudStandardTypeDiffSuppressFunc(_, old, new string, _ *schema.ResourceData) bool { - if (old == "STANDARD" && new == "") || (old == "" && new == "STANDARD") { - return true - } - return false -} \ No newline at end of file diff --git a/mmv1/templates/terraform/examples/vmware_engine_private_cloud_basic.tf.erb b/mmv1/templates/terraform/examples/vmware_engine_private_cloud_basic.tf.erb index 03e7508c7bb3..697f1d713f2d 100644 --- a/mmv1/templates/terraform/examples/vmware_engine_private_cloud_basic.tf.erb +++ b/mmv1/templates/terraform/examples/vmware_engine_private_cloud_basic.tf.erb @@ -6,7 +6,6 @@ resource "google_vmwareengine_private_cloud" "<%= ctx[:primary_resource_id] %>" management_cidr = "192.168.30.0/24" vmware_engine_network = google_vmwareengine_network.pc-nw.id } - management_cluster { cluster_id = "<%= ctx[:vars]['management_cluster_id'] %>" node_type_configs { diff --git a/mmv1/templates/terraform/examples/vmware_engine_private_cloud_full.tf.erb b/mmv1/templates/terraform/examples/vmware_engine_private_cloud_full.tf.erb index ee12d6bfdd24..20ada7f3f902 100644 --- a/mmv1/templates/terraform/examples/vmware_engine_private_cloud_full.tf.erb +++ b/mmv1/templates/terraform/examples/vmware_engine_private_cloud_full.tf.erb @@ -7,7 +7,6 @@ resource "google_vmwareengine_private_cloud" "<%= ctx[:primary_resource_id] %>" management_cidr = "192.168.30.0/24" vmware_engine_network = google_vmwareengine_network.pc-nw.id } - management_cluster { cluster_id = "<%= ctx[:vars]['management_cluster_id'] %>" node_type_configs { @@ -16,6 +15,8 @@ resource "google_vmwareengine_private_cloud" "<%= ctx[:primary_resource_id] %>" custom_core_count = 32 } } + deletion_delay_hours = 0 + send_deletion_delay_hours_if_zero = true } resource "google_vmwareengine_network" "pc-nw" { diff --git a/mmv1/templates/terraform/post_delete/private_cloud.go.erb b/mmv1/templates/terraform/post_delete/private_cloud.go.erb index 669377a2f9ab..b7bad0c11c37 100644 --- a/mmv1/templates/terraform/post_delete/private_cloud.go.erb +++ b/mmv1/templates/terraform/post_delete/private_cloud.go.erb @@ -29,6 +29,13 @@ privateCloudPollRead := func(d *schema.ResourceData, meta interface{}) transport if err != nil { return res, err } + // if resource exists but is marked for deletion + log.Printf("[DEBUG] Fetching state of the private cloud.") + v, ok := res["state"] + if ok && v.(string) == "DELETED" { + log.Printf("[DEBUG] The Private cloud has been successfully marked for delayed deletion.") + return nil, nil + } return res, nil } } diff --git a/mmv1/templates/terraform/pre_delete/vmwareengine_private_cloud.go.erb b/mmv1/templates/terraform/pre_delete/vmwareengine_private_cloud.go.erb new file mode 100644 index 000000000000..459238f69f00 --- /dev/null +++ b/mmv1/templates/terraform/pre_delete/vmwareengine_private_cloud.go.erb @@ -0,0 +1,8 @@ +// Delay deletion of the Private Cloud if delationDelayHours value is set +delationDelayHours := d.Get("deletion_delay_hours").(int) +if delationDelayHours > 0 || (delationDelayHours == 0 && d.Get("send_deletion_delay_hours_if_zero").(bool) == true) { + log.Printf("[DEBUG] Triggering delete of the Private Cloud with a delay of %v hours.\n", delationDelayHours) + url = url + "?delay_hours=" + fmt.Sprintf("%v", delationDelayHours) +} else { + log.Printf("[DEBUG] No deletion delay provided, triggering DELETE API without setting delay hours.\n") +} \ No newline at end of file diff --git a/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_external_access_rule_test.go b/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_external_access_rule_test.go index a7af23b2a88c..f28b2970c404 100644 --- a/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_external_access_rule_test.go +++ b/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_external_access_rule_test.go @@ -9,6 +9,8 @@ import ( ) func TestAccVmwareengineExternalAccessRule_vmwareEngineExternalAccessRuleUpdate(t *testing.T) { + // Temporarily skipping so that this test does not run and consume resources during PR pushes. It is bound to fail and is being fixed by PR #10992 + acctest.SkipIfVcr(t) t.Parallel() context := map[string]interface{}{ diff --git a/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_external_address_test.go b/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_external_address_test.go index 3d81180a0a93..0a7681e81649 100644 --- a/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_external_address_test.go +++ b/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_external_address_test.go @@ -14,6 +14,8 @@ import ( ) func TestAccVmwareengineExternalAddress_vmwareEngineExternalAddressUpdate(t *testing.T) { + // Temporarily skipping so that this test does not run and consume resources during PR pushes. It is bound to fail and is being fixed by PR #10992 + acctest.SkipIfVcr(t) t.Parallel() context := map[string]interface{}{ diff --git a/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_private_cloud_test.go b/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_private_cloud_test.go index 162cad8d35c5..4423b9106d93 100644 --- a/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_private_cloud_test.go +++ b/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_private_cloud_test.go @@ -18,7 +18,7 @@ func TestAccVmwareenginePrivateCloud_vmwareEnginePrivateCloudUpdate(t *testing.T t.Parallel() context := map[string]interface{}{ - "region": "southamerica-west1", + "region": "me-west1", "random_suffix": acctest.RandString(t, 10), "org_id": envvar.GetTestOrgFromEnv(t), "billing_account": envvar.GetTestBillingAccountFromEnv(t), @@ -33,82 +33,66 @@ func TestAccVmwareenginePrivateCloud_vmwareEnginePrivateCloudUpdate(t *testing.T CheckDestroy: testAccCheckVmwareenginePrivateCloudDestroyProducer(t), Steps: []resource.TestStep{ { - Config: testPrivateCloudUpdateConfig(context, "description1", 1), + Config: testPrivateCloudCreateConfig(context), Check: resource.ComposeTestCheckFunc( - acctest.CheckDataSourceStateMatchesResourceStateWithIgnores("data.google_vmwareengine_private_cloud.ds", "google_vmwareengine_private_cloud.vmw-engine-pc", map[string]struct{}{"type": {}}), + acctest.CheckDataSourceStateMatchesResourceStateWithIgnores( + "data.google_vmwareengine_private_cloud.ds", + "google_vmwareengine_private_cloud.vmw-engine-pc", + map[string]struct{}{ + "type": {}, + "deletion_delay_hours": {}, + "send_deletion_delay_hours_if_zero": {}, + }), testAccCheckGoogleVmwareengineNsxCredentialsMeta("data.google_vmwareengine_nsx_credentials.nsx-ds"), testAccCheckGoogleVmwareengineVcenterCredentialsMeta("data.google_vmwareengine_vcenter_credentials.vcenter-ds"), ), }, + { ResourceName: "google_vmwareengine_private_cloud.vmw-engine-pc", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"location", "name", "update_time", "type"}, - }, - { - Config: testPrivateCloudUpdateConfig(context, "description2", 4), // Expand PC - }, - { - ResourceName: "google_vmwareengine_private_cloud.vmw-engine-pc", - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"location", "name", "update_time", "type"}, + ImportStateVerifyIgnore: []string{"location", "name", "update_time", "type", "deletion_parameters"}, }, { - Config: testPrivateCloudUpdateConfig(context, "description2", 3), // Shrink PC + Config: testPrivateCloudUpdateConfig(context), + Check: resource.ComposeTestCheckFunc( + acctest.CheckDataSourceStateMatchesResourceStateWithIgnores( + "data.google_vmwareengine_private_cloud.ds", + "google_vmwareengine_private_cloud.vmw-engine-pc", + map[string]struct{}{ + "type": {}, + "deletion_delay_hours": {}, + "send_deletion_delay_hours_if_zero": {}, + }), + ), }, + { ResourceName: "google_vmwareengine_private_cloud.vmw-engine-pc", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"location", "name", "update_time", "type"}, + ImportStateVerifyIgnore: []string{"location", "name", "update_time", "type", "deletion_parameters"}, }, }, }) } -func testPrivateCloudUpdateConfig(context map[string]interface{}, description string, nodeCount int) string { - context["node_count"] = nodeCount - context["description"] = description - +func testPrivateCloudCreateConfig(context map[string]interface{}) string { return acctest.Nprintf(` -resource "google_project" "project" { - project_id = "tf-test%{random_suffix}" - name = "tf-test%{random_suffix}" - org_id = "%{org_id}" - billing_account = "%{billing_account}" -} - -resource "google_project_service" "vmwareengine" { - project = google_project.project.project_id - service = "vmwareengine.googleapis.com" -} - -resource "time_sleep" "sleep" { - create_duration = "1m" - depends_on = [ - google_project_service.vmwareengine, - ] -} - resource "google_vmwareengine_network" "default-nw" { - project = google_project.project.project_id name = "tf-test-pc-nw-%{random_suffix}" location = "global" type = "STANDARD" description = "PC network description." - depends_on = [ - time_sleep.sleep # Sleep allows permissions in the new project to propagate - ] } resource "google_vmwareengine_private_cloud" "vmw-engine-pc" { - project = google_project.project.project_id - location = "%{region}-a" + location = "%{region}-b" name = "tf-test-sample-pc%{random_suffix}" - description = "%{description}" + description = "test description" type = "TIME_LIMITED" + deletion_delay_hours = 1 network_config { management_cidr = "192.168.30.0/24" vmware_engine_network = google_vmwareengine_network.default-nw.id @@ -117,15 +101,14 @@ resource "google_vmwareengine_private_cloud" "vmw-engine-pc" { cluster_id = "tf-test-sample-mgmt-cluster-custom-core-count%{random_suffix}" node_type_configs { node_type_id = "standard-72" - node_count = "%{node_count}" + node_count = 1 custom_core_count = 32 } } } data "google_vmwareengine_private_cloud" "ds" { - project = google_project.project.project_id - location = "%{region}-a" + location = "%{region}-b" name = "tf-test-sample-pc%{random_suffix}" depends_on = [ google_vmwareengine_private_cloud.vmw-engine-pc, @@ -144,6 +127,46 @@ data "google_vmwareengine_vcenter_credentials" "vcenter-ds" { `, context) } +func testPrivateCloudUpdateConfig(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_vmwareengine_network" "default-nw" { + name = "tf-test-pc-nw-%{random_suffix}" + location = "global" + type = "STANDARD" + description = "PC network description." +} + +resource "google_vmwareengine_private_cloud" "vmw-engine-pc" { + location = "%{region}-b" + name = "tf-test-sample-pc%{random_suffix}" + description = "updated description" + type = "STANDARD" + deletion_delay_hours = 0 + send_deletion_delay_hours_if_zero = true + network_config { + management_cidr = "192.168.30.0/24" + vmware_engine_network = google_vmwareengine_network.default-nw.id + } + management_cluster { + cluster_id = "tf-test-sample-mgmt-cluster-custom-core-count%{random_suffix}" + node_type_configs { + node_type_id = "standard-72" + node_count = 3 + custom_core_count = 32 + } + } +} + +data "google_vmwareengine_private_cloud" "ds" { + location = "%{region}-b" + name = "tf-test-sample-pc%{random_suffix}" + depends_on = [ + google_vmwareengine_private_cloud.vmw-engine-pc, + ] +} +`, context) +} + func testAccCheckGoogleVmwareengineNsxCredentialsMeta(n string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -198,7 +221,7 @@ func testAccCheckVmwareenginePrivateCloudDestroyProducer(t *testing.T) func(s *t if config.BillingProject != "" { billingProject = config.BillingProject } - _, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ Config: config, Method: "GET", Project: billingProject, @@ -206,7 +229,13 @@ func testAccCheckVmwareenginePrivateCloudDestroyProducer(t *testing.T) func(s *t UserAgent: config.UserAgent, }) if err == nil { - return fmt.Errorf("VmwareenginePrivateCloud still exists at %s", url) + pcState, ok := res["state"] + if !ok { + return fmt.Errorf("Unable to fetch state for existing VmwareenginePrivateCloud %s", url) + } + if pcState.(string) != "DELETED" { + return fmt.Errorf("VmwareenginePrivateCloud still exists at %s", url) + } } } return nil diff --git a/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_subnet_test.go b/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_subnet_test.go index 1ad5612d5a28..dfe7c476e6cd 100644 --- a/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_subnet_test.go +++ b/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_subnet_test.go @@ -8,6 +8,8 @@ import ( ) func TestAccVmwareengineSubnet_vmwareEngineUserDefinedSubnetUpdate(t *testing.T) { + // Temporarily skipping so that this test does not run and consume resources during PR pushes. It is bound to fail and is being fixed by PR #10992 + acctest.SkipIfVcr(t) t.Parallel() context := map[string]interface{}{