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

Support Delayed Deletion for Vmwareengine Private Cloud #10764

Merged
merged 1 commit into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions mmv1/products/vmwareengine/PrivateCloud.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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.
Copy link
Member

Choose a reason for hiding this comment

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

What is the API default value for deletion delay? Is it 0? How often do you think users will need to additionally set send_deletion_delay_hours_if_zero?

Copy link
Member Author

Choose a reason for hiding this comment

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

Currently, the default delay at the API level is set to 3 hours. However, it might be modified in the near future.

Users would need to set send_deletion_delay_hours_if_zero if they want to send delay_hours=0 through the API call and delete the PC immediately. I have added this boolean field after a discussion with Stephen in a previous thread since terraform does not differentiate between "no value set" and "0 value set".

Copy link
Member

Choose a reason for hiding this comment

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

Okay, thank you for the context.

Copy link
Member

@c2thorn c2thorn Jul 10, 2024

Choose a reason for hiding this comment

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

Just noting that I do not like the need for send_deletion_delay_hours_if_zero but cannot think of another way to force send 0 only when the user has specified 0 due to TF limitations. I am sure this was discussed previously

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, this is the previous discussion thread for implementing this change.

Another approach which I could think of was using delay_hours inside a nested object deletion_parameters but nested objects are not yet supported for virtual_fields.

- !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'
Expand Down
Original file line number Diff line number Diff line change
@@ -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")
Copy link
Member

Choose a reason for hiding this comment

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

Can you give more context as to why a type diff from TIME_LIMITED -> STANDARD needs to be suppressed with Multinode? Also, is this related or separate to delayed deletion?

Copy link
Member Author

Choose a reason for hiding this comment

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

This change is separate from delayed deletion.

The TIME_LIMITED -> STANDARD PC update behavior is explained in go/upgrade-pc-in-terraform

A feature request was raised where the user wanted to change the PC type from their end for this use case. Since API does not support this change in its update call (it automatically changes it in the backend after a node count 1 -> 3&+ node count increase), I have added this suppress function to enable this type change at the terraform level.

Copy link
Member

Choose a reason for hiding this comment

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

I also see that type is set to ignore_read, so at least it will remain consistent. Thanks for the context.

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
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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" {
Expand Down
7 changes: 7 additions & 0 deletions mmv1/templates/terraform/post_delete/private_cloud.go.erb
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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")
}
Original file line number Diff line number Diff line change
Expand Up @@ -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{}{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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{}{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func TestAccVmwareenginePrivateCloud_vmwareEnginePrivateCloudUpdate(t *testing.T
t.Parallel()

context := map[string]interface{}{
"region": "southamerica-west1",
"region": "me-west1",
Copy link
Member

Choose a reason for hiding this comment

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

can you provide more context as to why this is being updated?

Copy link
Member Author

Choose a reason for hiding this comment

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

Some months back, we exhausted the number of nodes in the southamerca-west1 region which were being used for this test, leading to PC creation failures in multiple terraform tests.

As a result, go/gcve-resource-optimisation-for-tf-tests was created and additional nodes were reserved in me-west1 region for terraform test projects. This PR is part of its implementation efforts along with PR #10992.

"random_suffix": acctest.RandString(t, 10),
"org_id": envvar.GetTestOrgFromEnv(t),
"billing_account": envvar.GetTestBillingAccountFromEnv(t),
Expand All @@ -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
Expand All @@ -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,
Expand All @@ -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]
Expand Down Expand Up @@ -198,15 +221,21 @@ 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,
RawURL: url,
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

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

we will remove these before submitting right?

Copy link
Member Author

Choose a reason for hiding this comment

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

We can keep these commented till PR #10992 gets submitted. That way, the consumption of nodes for tests which are bound to fail would be prevented.

These tests would be modified and combined by PR #10992 which is built on the design go/gcve-resource-optimisation-for-tf-tests

Copy link
Member

@c2thorn c2thorn Jul 10, 2024

Choose a reason for hiding this comment

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

Understood, so the relevant test for these code changes are all in mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_private_cloud_test.go

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, thats correct

acctest.SkipIfVcr(t)
t.Parallel()

context := map[string]interface{}{
Expand Down
Loading