diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 57d346dc6..7f1b8f9bc 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -18,6 +18,7 @@ import ( "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/schedulerhints" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups" + "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach" "github.com/rackspace/gophercloud/openstack/compute/v2/flavors" @@ -318,6 +319,11 @@ func resourceComputeInstanceV2() *schema.Resource { }, Set: resourceComputeInstancePersonalityHash, }, + "stop_before_destroy": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: false, + }, }, } } @@ -808,6 +814,27 @@ func resourceComputeInstanceV2Delete(d *schema.ResourceData, meta interface{}) e return fmt.Errorf("Error creating OpenStack compute client: %s", err) } + if d.Get("stop_before_destroy").(bool) { + err = startstop.Stop(computeClient, d.Id()).ExtractErr() + if err != nil { + log.Printf("[WARN] Error stopping OpenStack instance: %s", err) + } else { + stopStateConf := &resource.StateChangeConf{ + Pending: []string{"ACTIVE"}, + Target: []string{"SHUTOFF"}, + Refresh: ServerV2StateRefreshFunc(computeClient, d.Id()), + Timeout: 3 * time.Minute, + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + log.Printf("[DEBUG] Waiting for instance (%s) to stop", d.Id()) + _, err = stopStateConf.WaitForState() + if err != nil { + log.Printf("[WARN] Error waiting for instance (%s) to stop: %s, proceeding to delete", d.Id(), err) + } + } + } + err = servers.Delete(computeClient, d.Id()).ExtractErr() if err != nil { return fmt.Errorf("Error deleting OpenStack server: %s", err) @@ -817,7 +844,7 @@ func resourceComputeInstanceV2Delete(d *schema.ResourceData, meta interface{}) e log.Printf("[DEBUG] Waiting for instance (%s) to delete", d.Id()) stateConf := &resource.StateChangeConf{ - Pending: []string{"ACTIVE"}, + Pending: []string{"ACTIVE", "SHUTOFF"}, Target: []string{"DELETED"}, Refresh: ServerV2StateRefreshFunc(computeClient, d.Id()), Timeout: 30 * time.Minute, diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go index fd08536a7..cc87bf707 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go @@ -840,3 +840,31 @@ func testAccCheckComputeV2InstanceInstanceIDsDoNotMatch( return nil } } + +func TestAccComputeV2Instance_stop_before_destroy(t *testing.T) { + var instance servers.Server + var testAccComputeV2Instance_stop_before_destroy = fmt.Sprintf(` + resource "openstack_compute_instance_v2" "foo" { + name = "terraform-test" + security_groups = ["default"] + network { + uuid = "%s" + } + stop_before_destroy = true + }`, + os.Getenv("OS_NETWORK_ID")) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeV2InstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeV2Instance_stop_before_destroy, + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeV2InstanceExists(t, "openstack_compute_instance_v2.foo", &instance), + ), + }, + }, + }) +} diff --git a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown index af7783122..a14d84ecf 100644 --- a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown +++ b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown @@ -264,6 +264,11 @@ The following arguments are supported: defining one or more files and their contents. The personality structure is described below. +* `stop_before_destroy` - (Optional) Whether to try stop instance gracefully + before destroying it, thus giving chance for guest OS daemons to stop correctly. + If instance doesn't stop within timeout, it will be destroyed anyway. + + The `network` block supports: * `uuid` - (Required unless `port` or `name` is provided) The network UUID to