Skip to content

Commit

Permalink
Support Delayed Deletion for Vmwareengine Private Cloud (#10764) (#7710)
Browse files Browse the repository at this point in the history
[upstream:b8a463edcd5a423613ca5aa1adf95f59095c2d73]

Signed-off-by: Modular Magician <[email protected]>
  • Loading branch information
modular-magician authored Jul 10, 2024
1 parent bfb8b90 commit a782f70
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 54 deletions.
6 changes: 6 additions & 0 deletions .changelog/10764.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
```release-note:enhancement
vmwareengine: added `deletion_delay_hours` field to `google_vmwareengine_private_cloud` resource
```
```release-note:enhancement
vmwareengine: support type change from `TIME_LIMITED` to `STANDARD` for multi-node `google_vmwareengine_private_cloud` resource
```
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,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 @@ -16,6 +16,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 @@ -33,10 +33,37 @@ import (
"github.com/hashicorp/terraform-provider-google-beta/google-beta/verify"
)

func vmwareenginePrivateCloudStandardTypeDiffSuppressFunc(_, old, new string, _ *schema.ResourceData) bool {
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
}

Expand Down Expand Up @@ -299,6 +326,16 @@ the form: projects/{project_number}/locations/{location}/vmwareEngineNetworks/{v
},
},
},
"deletion_delay_hours": {
Type: schema.TypeInt,
Optional: true,
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.`,
},
"send_deletion_delay_hours_if_zero": {
Type: schema.TypeBool,
Optional: true,
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.`,
},
"project": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -451,6 +488,7 @@ func resourceVmwareenginePrivateCloudRead(d *schema.ResourceData, meta interface
return nil
}

// Explicitly set virtual fields to default values if unset
if err := d.Set("project", project); err != nil {
return fmt.Errorf("Error reading PrivateCloud: %s", err)
}
Expand Down Expand Up @@ -628,7 +666,7 @@ func resourceVmwareenginePrivateCloudDelete(d *schema.ResourceData, meta interfa
}
billingProject = project

url, err := tpgresource.ReplaceVars(d, config, "{{VmwareengineBasePath}}projects/{{project}}/locations/{{location}}/privateClouds/{{name}}?delay_hours=0")
url, err := tpgresource.ReplaceVars(d, config, "{{VmwareengineBasePath}}projects/{{project}}/locations/{{location}}/privateClouds/{{name}}")
if err != nil {
return err
}
Expand All @@ -641,6 +679,14 @@ func resourceVmwareenginePrivateCloudDelete(d *schema.ResourceData, meta interfa
}

headers := make(http.Header)
// 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")
}

log.Printf("[DEBUG] Deleting PrivateCloud %q", d.Id())
res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
Expand Down Expand Up @@ -696,6 +742,13 @@ func resourceVmwareenginePrivateCloudDelete(d *schema.ResourceData, meta interfa
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 Expand Up @@ -726,6 +779,8 @@ func resourceVmwareenginePrivateCloudImport(d *schema.ResourceData, meta interfa
}
d.SetId(id)

// Explicitly set virtual fields to default values on import

return []*schema.ResourceData{d}, nil
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,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),
Expand All @@ -35,82 +35,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 @@ -119,15 +103,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 @@ -146,6 +129,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 @@ -200,15 +223,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 @@ -10,6 +10,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{}{
Expand Down
8 changes: 6 additions & 2 deletions website/docs/r/vmwareengine_private_cloud.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ resource "google_vmwareengine_private_cloud" "vmw-engine-pc" {
management_cidr = "192.168.30.0/24"
vmware_engine_network = google_vmwareengine_network.pc-nw.id
}
management_cluster {
cluster_id = "sample-mgmt-cluster"
node_type_configs {
Expand Down Expand Up @@ -68,7 +67,6 @@ resource "google_vmwareengine_private_cloud" "vmw-engine-pc" {
management_cidr = "192.168.30.0/24"
vmware_engine_network = google_vmwareengine_network.pc-nw.id
}
management_cluster {
cluster_id = "sample-mgmt-cluster"
node_type_configs {
Expand All @@ -77,6 +75,8 @@ resource "google_vmwareengine_private_cloud" "vmw-engine-pc" {
custom_core_count = 32
}
}
deletion_delay_hours = 0
send_deletion_delay_hours_if_zero = true
}
resource "google_vmwareengine_network" "pc-nw" {
Expand Down Expand Up @@ -204,6 +204,10 @@ The following arguments are supported:
* `project` - (Optional) The ID of the project in which the resource belongs.
If it is not provided, the provider project is used.

* `deletion_delay_hours` - (Optional) 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.

* `send_deletion_delay_hours_if_zero` - (Optional) 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.


## Attributes Reference

Expand Down

0 comments on commit a782f70

Please sign in to comment.