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

Per instance config delete underlying instance #3635

Merged
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
21 changes: 18 additions & 3 deletions products/compute/terraform.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1271,7 +1271,7 @@ overrides: !ruby/object:Overrides::ResourceOverrides

PerInstanceConfig: !ruby/object:Overrides::Terraform::ResourceOverride
id_format: "{{project}}/{{zone}}/{{instance_group_manager}}/{{name}}"
mutex: instangeGroupManager/{{project}}/{{zone}}/{{instance_group_manager}}
mutex: instanceGroupManager/{{project}}/{{zone}}/{{instance_group_manager}}
# Fine-grained resources don't actually exist as standalone GCP resource
# in Cloud Asset Inventory
exclude_validator: true
Expand Down Expand Up @@ -1323,14 +1323,22 @@ overrides: !ruby/object:Overrides::ResourceOverrides
- :REFRESH
- :NONE
default_value: :REPLACE
- !ruby/object:Api::Type::Boolean
name: 'remove_instance_state_on_destroy'
description: |
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.
default_value: false
custom_code: !ruby/object:Provider::Terraform::CustomCode
encoder: templates/terraform/encoders/compute_per_instance_config.go.erb
update_encoder: templates/terraform/update_encoder/compute_per_instance_config.go.erb
pre_delete: templates/terraform/pre_delete/compute_per_instance_config.go.erb
post_update: templates/terraform/post_update/compute_per_instance_config.go.erb
custom_delete: templates/terraform/custom_delete/per_instance_config.go.erb
RegionPerInstanceConfig: !ruby/object:Overrides::Terraform::ResourceOverride
id_format: "{{project}}/{{region}}/{{region_instance_group_manager}}/{{name}}"
mutex: instangeGroupManager/{{project}}/{{region}}/{{region_instance_group_manager}}
mutex: instanceGroupManager/{{project}}/{{region}}/{{region_instance_group_manager}}
# Fine-grained resources don't actually exist as standalone GCP resource
# in Cloud Asset Inventory
exclude_validator: true
Expand Down Expand Up @@ -1382,12 +1390,19 @@ overrides: !ruby/object:Overrides::ResourceOverrides
- :REFRESH
- :NONE
default_value: :REPLACE
- !ruby/object:Api::Type::Boolean
name: 'remove_instance_state_on_destroy'
description: |
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.
default_value: false
custom_code: !ruby/object:Provider::Terraform::CustomCode
encoder: templates/terraform/encoders/compute_per_instance_config.go.erb
update_encoder: templates/terraform/update_encoder/compute_per_instance_config.go.erb
pre_delete: templates/terraform/pre_delete/compute_per_instance_config.go.erb
post_update: templates/terraform/post_update/compute_region_per_instance_config.go.erb

custom_delete: templates/terraform/custom_delete/region_per_instance_config.go.erb
ProjectInfo: !ruby/object:Overrides::Terraform::ResourceOverride
exclude: true
Region: !ruby/object:Overrides::Terraform::ResourceOverride
Expand Down
78 changes: 78 additions & 0 deletions templates/terraform/custom_delete/per_instance_config.go.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
config := meta.(*Config)

project, err := getProject(d, config)
if err != nil {
return err
}

lockName, err := replaceVars(d, config, "instanceGroupManager/{{project}}/{{zone}}/{{instance_group_manager}}")
if err != nil {
return err
}
mutexKV.Lock(lockName)
defer mutexKV.Unlock(lockName)

url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/zones/{{zone}}/instanceGroupManagers/{{instance_group_manager}}/deletePerInstanceConfigs")
if err != nil {
return err
}

var obj map[string]interface{}
obj = map[string]interface{}{
"names": [1]string{d.Get("name").(string)},
}
log.Printf("[DEBUG] Deleting PerInstanceConfig %q", d.Id())

res, err := sendRequestWithTimeout(config, "POST", project, url, obj, d.Timeout(schema.TimeoutDelete))
if err != nil {
return handleNotFoundError(err, d, "PerInstanceConfig")
}

err = computeOperationWaitTime(
config, res, project, "Deleting PerInstanceConfig",
d.Timeout(schema.TimeoutDelete))

if err != nil {
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
config := meta.(*Config)

project, err := getProject(d, config)
if err != nil {
return err
}

lockName, err := replaceVars(d, config, "instanceGroupManager/{{project}}/{{region}}/{{region_instance_group_manager}}")
if err != nil {
return err
}
mutexKV.Lock(lockName)
defer mutexKV.Unlock(lockName)

url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/instanceGroupManagers/{{region_instance_group_manager}}/deletePerInstanceConfigs")
if err != nil {
return err
}

var obj map[string]interface{}
obj = map[string]interface{}{
"names": [1]string{d.Get("name").(string)},
}
log.Printf("[DEBUG] Deleting RegionPerInstanceConfig %q", d.Id())

res, err := sendRequestWithTimeout(config, "POST", project, url, obj, d.Timeout(schema.TimeoutDelete))
if err != nil {
return handleNotFoundError(err, d, "RegionPerInstanceConfig")
}

err = computeOperationWaitTime(
config, res, project, "Deleting RegionPerInstanceConfig",
d.Timeout(schema.TimeoutDelete))

if err != nil {
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// 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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func TestAccComputePerInstanceConfig_statefulBasic(t *testing.T) {
ResourceName: "google_compute_per_instance_config.default",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"remove_instance_state_on_destroy"},
},
{
// Force-recreate old config
Expand All @@ -47,6 +48,7 @@ func TestAccComputePerInstanceConfig_statefulBasic(t *testing.T) {
ResourceName: "google_compute_per_instance_config.default",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"remove_instance_state_on_destroy"},
},
{
// Add two new endpoints
Expand All @@ -56,40 +58,96 @@ func TestAccComputePerInstanceConfig_statefulBasic(t *testing.T) {
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,
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"
}
}
}
Expand All @@ -102,6 +160,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"
Expand All @@ -117,6 +176,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"
Expand All @@ -130,6 +190,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"
Expand Down
Loading