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 #18698

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
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/google/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