diff --git a/.changelog/3635.txt b/.changelog/3635.txt new file mode 100644 index 0000000000..0f55cbaa97 --- /dev/null +++ b/.changelog/3635.txt @@ -0,0 +1,6 @@ +```release-note:enhancement +compute: Added `remove_instance_state_on_destroy` to `google_compute_region_per_instance_config` to control deletion of underlying instance state. (beta only) +``` +```release-note:enhancement +compute: Added `remove_instance_state_on_destroy` to `google_compute_per_instance_config` to control deletion of underlying instance state. (beta only) +``` diff --git a/google-beta/resource_compute_per_instance_config.go b/google-beta/resource_compute_per_instance_config.go index b2d692bbb5..08eb7ff6cb 100644 --- a/google-beta/resource_compute_per_instance_config.go +++ b/google-beta/resource_compute_per_instance_config.go @@ -95,6 +95,11 @@ func resourceComputePerInstanceConfig() *schema.Resource { Optional: true, Default: "REPLACE", }, + "remove_instance_state_on_destroy": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, "project": { Type: schema.TypeString, Optional: true, @@ -163,7 +168,7 @@ func resourceComputePerInstanceConfigCreate(d *schema.ResourceData, meta interfa return err } - lockName, err := replaceVars(d, config, "instangeGroupManager/{{project}}/{{zone}}/{{instance_group_manager}}") + lockName, err := replaceVars(d, config, "instanceGroupManager/{{project}}/{{zone}}/{{instance_group_manager}}") if err != nil { return err } @@ -243,6 +248,9 @@ func resourceComputePerInstanceConfigRead(d *schema.ResourceData, meta interface if _, ok := d.GetOk("most_disruptive_allowed_action"); !ok { d.Set("most_disruptive_allowed_action", "REPLACE") } + if _, ok := d.GetOk("remove_instance_state_on_destroy"); !ok { + d.Set("remove_instance_state_on_destroy", false) + } if err := d.Set("project", project); err != nil { return fmt.Errorf("Error reading PerInstanceConfig: %s", err) } @@ -284,7 +292,7 @@ func resourceComputePerInstanceConfigUpdate(d *schema.ResourceData, meta interfa return err } - lockName, err := replaceVars(d, config, "instangeGroupManager/{{project}}/{{zone}}/{{instance_group_manager}}") + lockName, err := replaceVars(d, config, "instanceGroupManager/{{project}}/{{zone}}/{{instance_group_manager}}") if err != nil { return err } @@ -362,7 +370,7 @@ func resourceComputePerInstanceConfigDelete(d *schema.ResourceData, meta interfa return err } - lockName, err := replaceVars(d, config, "instangeGroupManager/{{project}}/{{zone}}/{{instance_group_manager}}") + lockName, err := replaceVars(d, config, "instanceGroupManager/{{project}}/{{zone}}/{{instance_group_manager}}") if err != nil { return err } @@ -393,6 +401,44 @@ func resourceComputePerInstanceConfigDelete(d *schema.ResourceData, meta interfa return err } + // Potentially delete the state managed by this config + if d.Get("remove_instance_state_on_destroy").(bool) { + // Instance name in applyUpdatesToInstances request must include zone + instanceName, err := replaceVars(d, config, "zones/{{zone}}/instances/{{name}}") + if err != nil { + return err + } + + obj = make(map[string]interface{}) + obj["instances"] = []string{instanceName} + + // The deletion must be applied to the instance after the PerInstanceConfig is deleted + url, err = replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/zones/{{zone}}/instanceGroupManagers/{{instance_group_manager}}/applyUpdatesToInstances") + if err != nil { + return err + } + + log.Printf("[DEBUG] Applying updates to PerInstanceConfig %q: %#v", d.Id(), obj) + res, err = sendRequestWithTimeout(config, "POST", project, url, obj, d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return fmt.Errorf("Error deleting PerInstanceConfig %q: %s", d.Id(), err) + } + + err = computeOperationWaitTime( + config, res, project, "Applying update to PerInstanceConfig", + d.Timeout(schema.TimeoutUpdate)) + if err != nil { + return fmt.Errorf("Error deleting PerInstanceConfig %q: %s", d.Id(), err) + } + + // PerInstanceConfig goes into "DELETING" state while the instance is actually deleted + err = PollingWaitTime(resourceComputePerInstanceConfigPollRead(d, meta), PollCheckInstanceConfigDeleted, "Deleting PerInstanceConfig", d.Timeout(schema.TimeoutDelete)) + if err != nil { + return fmt.Errorf("Error waiting for delete on PerInstanceConfig %q: %s", d.Id(), err) + } + } + log.Printf("[DEBUG] Finished deleting PerInstanceConfig %q: %#v", d.Id(), res) return nil } @@ -418,6 +464,7 @@ func resourceComputePerInstanceConfigImport(d *schema.ResourceData, meta interfa // Explicitly set virtual fields to default values on import d.Set("minimal_action", "NONE") d.Set("most_disruptive_allowed_action", "REPLACE") + d.Set("remove_instance_state_on_destroy", false) return []*schema.ResourceData{d}, nil } diff --git a/google-beta/resource_compute_per_instance_config_test.go b/google-beta/resource_compute_per_instance_config_test.go index 1a5854aba1..45861af40f 100644 --- a/google-beta/resource_compute_per_instance_config_test.go +++ b/google-beta/resource_compute_per_instance_config_test.go @@ -30,9 +30,10 @@ func TestAccComputePerInstanceConfig_statefulBasic(t *testing.T) { Config: testAccComputePerInstanceConfig_statefulBasic(context), }, { - ResourceName: "google_compute_per_instance_config.default", - ImportState: true, - ImportStateVerify: true, + ResourceName: "google_compute_per_instance_config.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"remove_instance_state_on_destroy"}, }, { // Force-recreate old config @@ -42,52 +43,109 @@ func TestAccComputePerInstanceConfig_statefulBasic(t *testing.T) { ), }, { - ResourceName: "google_compute_per_instance_config.default", - ImportState: true, - ImportStateVerify: true, + ResourceName: "google_compute_per_instance_config.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"remove_instance_state_on_destroy"}, }, { // Add two new endpoints Config: testAccComputePerInstanceConfig_statefulAdditional(context), }, { - ResourceName: "google_compute_per_instance_config.default", - ImportState: true, - ImportStateVerify: true, + ResourceName: "google_compute_per_instance_config.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"remove_instance_state_on_destroy"}, }, { ResourceName: "google_compute_per_instance_config.with_disks", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"most_disruptive_allowed_action", "minimal_action"}, + ImportStateVerifyIgnore: []string{"most_disruptive_allowed_action", "minimal_action", "remove_instance_state_on_destroy"}, }, { - ResourceName: "google_compute_per_instance_config.add2", - ImportState: true, - ImportStateVerify: true, + ResourceName: "google_compute_per_instance_config.add2", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"remove_instance_state_on_destroy"}, }, { // delete all configs Config: testAccComputePerInstanceConfig_igm(context), Check: resource.ComposeTestCheckFunc( + // Config with remove_instance_state_on_destroy = false won't be destroyed (config4) testAccCheckComputePerInstanceConfigDestroyed(t, igmId, context["config_name2"].(string)), testAccCheckComputePerInstanceConfigDestroyed(t, igmId, context["config_name3"].(string)), - testAccCheckComputePerInstanceConfigDestroyed(t, igmId, context["config_name4"].(string)), ), }, }, }) } +func TestAccComputePerInstanceConfig_update(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + "config_name": fmt.Sprintf("instance-%s", randString(t, 10)), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + // Create one config + Config: testAccComputePerInstanceConfig_statefulBasic(context), + }, + { + ResourceName: "google_compute_per_instance_config.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"remove_instance_state_on_destroy"}, + }, + { + // Update an existing config + Config: testAccComputePerInstanceConfig_update(context), + }, + { + ResourceName: "google_compute_per_instance_config.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"remove_instance_state_on_destroy"}, + }, + }, + }) +} + func testAccComputePerInstanceConfig_statefulBasic(context map[string]interface{}) string { return Nprintf(` resource "google_compute_per_instance_config" "default" { zone = google_compute_instance_group_manager.igm.zone instance_group_manager = google_compute_instance_group_manager.igm.name name = "%{config_name}" + remove_instance_state_on_destroy = true + preserved_state { + metadata = { + asdf = "asdf" + } + } +} +`, context) + testAccComputePerInstanceConfig_igm(context) +} + +func testAccComputePerInstanceConfig_update(context map[string]interface{}) string { + return Nprintf(` +resource "google_compute_per_instance_config" "default" { + zone = google_compute_instance_group_manager.igm.zone + instance_group_manager = google_compute_instance_group_manager.igm.name + name = "%{config_name}" + remove_instance_state_on_destroy = true preserved_state { metadata = { asdf = "asdf" + update = "12345" } } } @@ -100,6 +158,7 @@ resource "google_compute_per_instance_config" "default" { zone = google_compute_instance_group_manager.igm.zone instance_group_manager = google_compute_instance_group_manager.igm.name name = "%{config_name2}" + remove_instance_state_on_destroy = true preserved_state { metadata = { asdf = "asdf" @@ -115,6 +174,7 @@ resource "google_compute_per_instance_config" "default" { zone = google_compute_instance_group_manager.igm.zone instance_group_manager = google_compute_instance_group_manager.igm.name name = "%{config_name2}" + remove_instance_state_on_destroy = true preserved_state { metadata = { asdf = "asdf" @@ -128,6 +188,7 @@ resource "google_compute_per_instance_config" "with_disks" { name = "%{config_name3}" most_disruptive_allowed_action = "REFRESH" minimal_action = "REFRESH" + remove_instance_state_on_destroy = true preserved_state { metadata = { meta = "123" diff --git a/google-beta/resource_compute_region_per_instance_config.go b/google-beta/resource_compute_region_per_instance_config.go index 6ffff09b08..388fa95b26 100644 --- a/google-beta/resource_compute_region_per_instance_config.go +++ b/google-beta/resource_compute_region_per_instance_config.go @@ -95,6 +95,11 @@ func resourceComputeRegionPerInstanceConfig() *schema.Resource { Optional: true, Default: "REPLACE", }, + "remove_instance_state_on_destroy": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, "project": { Type: schema.TypeString, Optional: true, @@ -163,7 +168,7 @@ func resourceComputeRegionPerInstanceConfigCreate(d *schema.ResourceData, meta i return err } - lockName, err := replaceVars(d, config, "instangeGroupManager/{{project}}/{{region}}/{{region_instance_group_manager}}") + lockName, err := replaceVars(d, config, "instanceGroupManager/{{project}}/{{region}}/{{region_instance_group_manager}}") if err != nil { return err } @@ -243,6 +248,9 @@ func resourceComputeRegionPerInstanceConfigRead(d *schema.ResourceData, meta int if _, ok := d.GetOk("most_disruptive_allowed_action"); !ok { d.Set("most_disruptive_allowed_action", "REPLACE") } + if _, ok := d.GetOk("remove_instance_state_on_destroy"); !ok { + d.Set("remove_instance_state_on_destroy", false) + } if err := d.Set("project", project); err != nil { return fmt.Errorf("Error reading RegionPerInstanceConfig: %s", err) } @@ -284,7 +292,7 @@ func resourceComputeRegionPerInstanceConfigUpdate(d *schema.ResourceData, meta i return err } - lockName, err := replaceVars(d, config, "instangeGroupManager/{{project}}/{{region}}/{{region_instance_group_manager}}") + lockName, err := replaceVars(d, config, "instanceGroupManager/{{project}}/{{region}}/{{region_instance_group_manager}}") if err != nil { return err } @@ -311,8 +319,8 @@ func resourceComputeRegionPerInstanceConfigUpdate(d *schema.ResourceData, meta i return err } - // Instance name in applyUpdatesToInstances request must include region - instanceName, err := replaceVars(d, config, "regoins/{{region}}/instances/{{name}}") + // Instance name in applyUpdatesToInstances request must include zone + instanceName, err := findInstanceName(d, config) if err != nil { return err } @@ -362,7 +370,7 @@ func resourceComputeRegionPerInstanceConfigDelete(d *schema.ResourceData, meta i return err } - lockName, err := replaceVars(d, config, "instangeGroupManager/{{project}}/{{region}}/{{region_instance_group_manager}}") + lockName, err := replaceVars(d, config, "instanceGroupManager/{{project}}/{{region}}/{{region_instance_group_manager}}") if err != nil { return err } @@ -393,6 +401,45 @@ func resourceComputeRegionPerInstanceConfigDelete(d *schema.ResourceData, meta i return err } + // Potentially delete the state managed by this config + if d.Get("remove_instance_state_on_destroy").(bool) { + // Instance name in applyUpdatesToInstances request must include zone + instanceName, err := findInstanceName(d, config) + if err != nil { + return err + } + + obj = make(map[string]interface{}) + obj["instances"] = []string{instanceName} + + // Updates must be applied to the instance after deleting the PerInstanceConfig + url, err = replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/instanceGroupManagers/{{region_instance_group_manager}}/applyUpdatesToInstances") + if err != nil { + return err + } + + log.Printf("[DEBUG] Applying updates to PerInstanceConfig %q: %#v", d.Id(), obj) + res, err = sendRequestWithTimeout(config, "POST", project, url, obj, d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return fmt.Errorf("Error updating PerInstanceConfig %q: %s", d.Id(), err) + } + + err = computeOperationWaitTime( + config, res, project, "Applying update to PerInstanceConfig", + d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return fmt.Errorf("Error deleting PerInstanceConfig %q: %s", d.Id(), err) + } + + // RegionPerInstanceConfig goes into "DELETING" state while the instance is actually deleted + err = PollingWaitTime(resourceComputeRegionPerInstanceConfigPollRead(d, meta), PollCheckInstanceConfigDeleted, "Deleting RegionPerInstanceConfig", d.Timeout(schema.TimeoutDelete)) + if err != nil { + return fmt.Errorf("Error waiting for delete on RegionPerInstanceConfig %q: %s", d.Id(), err) + } + } + log.Printf("[DEBUG] Finished deleting RegionPerInstanceConfig %q: %#v", d.Id(), res) return nil } @@ -418,6 +465,7 @@ func resourceComputeRegionPerInstanceConfigImport(d *schema.ResourceData, meta i // Explicitly set virtual fields to default values on import d.Set("minimal_action", "NONE") d.Set("most_disruptive_allowed_action", "REPLACE") + d.Set("remove_instance_state_on_destroy", false) return []*schema.ResourceData{d}, nil } diff --git a/google-beta/resource_compute_region_per_instance_config_test.go b/google-beta/resource_compute_region_per_instance_config_test.go index 60bd6d756a..43b0af6e76 100644 --- a/google-beta/resource_compute_region_per_instance_config_test.go +++ b/google-beta/resource_compute_region_per_instance_config_test.go @@ -30,9 +30,10 @@ func TestAccComputeRegionPerInstanceConfig_statefulBasic(t *testing.T) { Config: testAccComputeRegionPerInstanceConfig_statefulBasic(context), }, { - ResourceName: "google_compute_region_per_instance_config.default", - ImportState: true, - ImportStateVerify: true, + ResourceName: "google_compute_region_per_instance_config.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"remove_instance_state_on_destroy"}, }, { // Force-recreate old config @@ -42,49 +43,89 @@ func TestAccComputeRegionPerInstanceConfig_statefulBasic(t *testing.T) { ), }, { - ResourceName: "google_compute_region_per_instance_config.default", - ImportState: true, - ImportStateVerify: true, + ResourceName: "google_compute_region_per_instance_config.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"remove_instance_state_on_destroy"}, }, { // Add two new endpoints Config: testAccComputeRegionPerInstanceConfig_statefulAdditional(context), }, { - ResourceName: "google_compute_region_per_instance_config.default", - ImportState: true, - ImportStateVerify: true, + ResourceName: "google_compute_region_per_instance_config.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"remove_instance_state_on_destroy"}, }, { ResourceName: "google_compute_region_per_instance_config.with_disks", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"most_disruptive_allowed_action", "minimal_action"}, + ImportStateVerifyIgnore: []string{"most_disruptive_allowed_action", "minimal_action", "remove_instance_state_on_destroy"}, }, { - ResourceName: "google_compute_region_per_instance_config.add2", - ImportState: true, - ImportStateVerify: true, + ResourceName: "google_compute_region_per_instance_config.add2", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"remove_instance_state_on_destroy"}, }, { // delete all configs Config: testAccComputeRegionPerInstanceConfig_rigm(context), Check: resource.ComposeTestCheckFunc( + // Config with remove_instance_state_on_destroy = false won't be destroyed (config4) testAccCheckComputeRegionPerInstanceConfigDestroyed(t, rigmId, context["config_name2"].(string)), testAccCheckComputeRegionPerInstanceConfigDestroyed(t, rigmId, context["config_name3"].(string)), - testAccCheckComputeRegionPerInstanceConfigDestroyed(t, rigmId, context["config_name4"].(string)), ), }, }, }) } +func TestAccComputeRegionPerInstanceConfig_update(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + "config_name": fmt.Sprintf("instance-%s", randString(t, 10)), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + // Create one config + Config: testAccComputeRegionPerInstanceConfig_statefulBasic(context), + }, + { + ResourceName: "google_compute_region_per_instance_config.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"remove_instance_state_on_destroy"}, + }, + { + // Update an existing config + Config: testAccComputeRegionPerInstanceConfig_update(context), + }, + { + ResourceName: "google_compute_region_per_instance_config.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"remove_instance_state_on_destroy"}, + }, + }, + }) +} + func testAccComputeRegionPerInstanceConfig_statefulBasic(context map[string]interface{}) string { return Nprintf(` resource "google_compute_region_per_instance_config" "default" { region = google_compute_region_instance_group_manager.rigm.region region_instance_group_manager = google_compute_region_instance_group_manager.rigm.name name = "%{config_name}" + remove_instance_state_on_destroy = true preserved_state { metadata = { asdf = "asdf" @@ -94,12 +135,30 @@ resource "google_compute_region_per_instance_config" "default" { `, context) + testAccComputeRegionPerInstanceConfig_rigm(context) } +func testAccComputeRegionPerInstanceConfig_update(context map[string]interface{}) string { + return Nprintf(` +resource "google_compute_region_per_instance_config" "default" { + region = google_compute_region_instance_group_manager.rigm.region + region_instance_group_manager = google_compute_region_instance_group_manager.rigm.name + name = "%{config_name}" + remove_instance_state_on_destroy = true + preserved_state { + metadata = { + asdf = "foo" + updated = "12345" + } + } +} +`, context) + testAccComputeRegionPerInstanceConfig_rigm(context) +} + func testAccComputeRegionPerInstanceConfig_statefulModified(context map[string]interface{}) string { return Nprintf(` resource "google_compute_region_per_instance_config" "default" { region = google_compute_region_instance_group_manager.rigm.region region_instance_group_manager = google_compute_region_instance_group_manager.rigm.name name = "%{config_name2}" + remove_instance_state_on_destroy = true preserved_state { metadata = { asdf = "asdf" @@ -115,6 +174,7 @@ resource "google_compute_region_per_instance_config" "default" { region = google_compute_region_instance_group_manager.rigm.region region_instance_group_manager = google_compute_region_instance_group_manager.rigm.name name = "%{config_name2}" + remove_instance_state_on_destroy = true preserved_state { metadata = { asdf = "asdf" @@ -128,6 +188,7 @@ resource "google_compute_region_per_instance_config" "with_disks" { name = "%{config_name3}" most_disruptive_allowed_action = "REFRESH" minimal_action = "REFRESH" + remove_instance_state_on_destroy = true preserved_state { metadata = { meta = "123" diff --git a/google-beta/stateful_mig_polling.go b/google-beta/stateful_mig_polling.go new file mode 100644 index 0000000000..f0a087a800 --- /dev/null +++ b/google-beta/stateful_mig_polling.go @@ -0,0 +1,137 @@ +package google + +import ( + "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +// PerInstanceConfig needs both regular operation polling AND custom polling for deletion which is why this is not generated +func resourceComputePerInstanceConfigPollRead(d *schema.ResourceData, meta interface{}) PollReadFunc { + return func() (map[string]interface{}, error) { + config := meta.(*Config) + + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/zones/{{zone}}/instanceGroupManagers/{{instance_group_manager}}/listPerInstanceConfigs") + if err != nil { + return nil, err + } + + project, err := getProject(d, config) + if err != nil { + return nil, err + } + res, err := sendRequest(config, "POST", project, url, nil) + if err != nil { + return res, err + } + res, err = flattenNestedComputePerInstanceConfig(d, meta, res) + if err != nil { + return nil, err + } + + // Returns nil res if nested object is not found + return res, nil + } +} + +// RegionPerInstanceConfig needs both regular operation polling AND custom polling for deletion which is why this is not generated +func resourceComputeRegionPerInstanceConfigPollRead(d *schema.ResourceData, meta interface{}) PollReadFunc { + return func() (map[string]interface{}, error) { + config := meta.(*Config) + + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/instanceGroupManagers/{{region_instance_group_manager}}/listPerInstanceConfigs") + if err != nil { + return nil, err + } + + project, err := getProject(d, config) + if err != nil { + return nil, err + } + res, err := sendRequest(config, "POST", project, url, nil) + if err != nil { + return res, err + } + res, err = flattenNestedComputeRegionPerInstanceConfig(d, meta, res) + if err != nil { + return nil, err + } + + // Returns nil res if nested object is not found + return res, nil + } +} + +// Returns an instance name in the form zones/{zone}/instances/{instance} for the managed +// instance matching the name of a PerInstanceConfig +func findInstanceName(d *schema.ResourceData, config *Config) (string, error) { + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/instanceGroupManagers/{{region_instance_group_manager}}/listManagedInstances") + + if err != nil { + return "", err + } + + project, err := getProject(d, config) + if err != nil { + return "", err + } + instanceNameToFind := fmt.Sprintf("/%s", d.Get("name").(string)) + + token := "" + for paginate := true; paginate; { + urlWithToken := "" + if token != "" { + urlWithToken = fmt.Sprintf("%s?maxResults=1&pageToken=%s", url, token) + } else { + urlWithToken = fmt.Sprintf("%s?maxResults=1", url) + } + res, err := sendRequest(config, "POST", project, urlWithToken, nil) + if err != nil { + return "", err + } + + managedInstances, ok := res["managedInstances"] + if !ok { + return "", fmt.Errorf("Failed to parse response for listManagedInstances for %s", d.Id()) + } + + managedInstancesArr := managedInstances.([]interface{}) + for _, managedInstanceRaw := range managedInstancesArr { + instance := managedInstanceRaw.(map[string]interface{}) + name, ok := instance["instance"] + if !ok { + return "", fmt.Errorf("Failed to read instance name for managed instance: %#v", instance) + } + if strings.HasSuffix(name.(string), instanceNameToFind) { + return name.(string), nil + } + } + + tokenRaw, paginate := res["nextPageToken"] + if paginate { + token = tokenRaw.(string) + } + } + + return "", fmt.Errorf("Failed to find managed instance with name: %s", instanceNameToFind) +} + +func PollCheckInstanceConfigDeleted(resp map[string]interface{}, respErr error) PollResult { + if respErr != nil { + return ErrorPollResult(respErr) + } + + // Nested object 404 appears as nil response + if resp == nil { + // Config no longer exists + return SuccessPollResult() + } + + // Read status + status := resp["status"].(string) + if status == "DELETING" { + return PendingStatusPollResult("Still deleting") + } + return ErrorPollResult(fmt.Errorf("Expected PerInstanceConfig to be deleting but status is: %s", status)) +} diff --git a/website/docs/r/compute_per_instance_config.html.markdown b/website/docs/r/compute_per_instance_config.html.markdown index aad3ace821..e0f263882c 100644 --- a/website/docs/r/compute_per_instance_config.html.markdown +++ b/website/docs/r/compute_per_instance_config.html.markdown @@ -144,6 +144,9 @@ Default is `REPLACE`. Possible values are: * RESTART * REFRESH * NONE +* `remove_instance_state_on_destroy` - (Optional) When true, deleting this config will immediately remove any specified state from the underlying instance. +When false, deleting this config will *not* immediately remove any state from the underlying instance. +State will be removed on the next instance recreation or update. The `preserved_state` block supports: diff --git a/website/docs/r/compute_region_per_instance_config.html.markdown b/website/docs/r/compute_region_per_instance_config.html.markdown index f935496be6..9969ba70ab 100644 --- a/website/docs/r/compute_region_per_instance_config.html.markdown +++ b/website/docs/r/compute_region_per_instance_config.html.markdown @@ -145,6 +145,9 @@ Default is `REPLACE`. Possible values are: * RESTART * REFRESH * NONE +* `remove_instance_state_on_destroy` - (Optional) When true, deleting this config will immediately remove any specified state from the underlying instance. +When false, deleting this config will *not* immediately remove any state from the underlying instance. +State will be removed on the next instance recreation or update. The `preserved_state` block supports: