openstack abandon stack op and unit test
diff --git a/openstack/orchestration/v1/stacks/fixtures.go b/openstack/orchestration/v1/stacks/fixtures.go
index 31814cc..72e327d 100644
--- a/openstack/orchestration/v1/stacks/fixtures.go
+++ b/openstack/orchestration/v1/stacks/fixtures.go
@@ -227,7 +227,7 @@
 }
 
 // HandleDeleteSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87`
-// on the test handler mux that responds with an `Delete` response.
+// on the test handler mux that responds with a `Delete` response.
 func HandleDeleteSuccessfully(t *testing.T) {
 	th.Mux.HandleFunc("/stacks/gophercloud-test-stack-2/db6977b2-27aa-4775-9ae7-6213212d4ada", func(w http.ResponseWriter, r *http.Request) {
 		th.TestMethod(t, r, "DELETE")
@@ -265,7 +265,7 @@
 }
 
 // HandlePreviewSuccessfully creates an HTTP handler at `/stacks/preview`
-// on the test handler mux that responds with an `Preview` response.
+// on the test handler mux that responds with a `Preview` response.
 func HandlePreviewSuccessfully(t *testing.T, output string) {
 	th.Mux.HandleFunc("/stacks/preview", func(w http.ResponseWriter, r *http.Request) {
 		th.TestMethod(t, r, "POST")
@@ -277,3 +277,99 @@
 		fmt.Fprintf(w, output)
 	})
 }
+
+// AbandonExpected represents the expected object from an Abandon request.
+var AbandonExpected = &AbandonedStack{
+	Status: "COMPLETE",
+	Name:   "postman_stack",
+	Template: `
+  {
+    "heat_template_version": "2013-05-23",
+    "description": "Simple template to test heat commands",
+    "parameters": {
+      "flavor": {
+        "default": "m1.tiny",
+        "type": "string"
+      }
+    },
+    "resources": {
+      "hello_world": {
+        "type": "OS::Nova::Server",
+        "properties": {
+          "key_name": "heat_key",
+          "flavor": {
+            "get_param": "flavor"
+          },
+          "image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
+          "user_data": "#!/bin/bash -xv\necho \"hello world\" > /root/hello-world.txt\n"
+        }
+      }
+    }
+  }`,
+	Action: "CREATE",
+	ID:     "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
+	Resources: map[string]interface{}{
+		"hello_world": map[string]interface{}{
+			"status":      "COMPLETE",
+			"name":        "hello_world",
+			"resource_id": "8a310d36-46fc-436f-8be4-37a696b8ac63",
+			"action":      "CREATE",
+			"type":        "OS::Nova::Server",
+		},
+	},
+}
+
+// AbandonOutput represents the response body from an Abandon request.
+const AbandonOutput = `
+{
+  "status": "COMPLETE",
+  "name": "postman_stack",
+  "template": {
+    "heat_template_version": "2013-05-23",
+    "description": "Simple template to test heat commands",
+    "parameters": {
+      "flavor": {
+        "default": "m1.tiny",
+        "type": "string"
+      }
+    },
+    "resources": {
+      "hello_world": {
+        "type": "OS::Nova::Server",
+        "properties": {
+          "key_name": "heat_key",
+          "flavor": {
+            "get_param": "flavor"
+          },
+          "image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
+          "user_data": "#!/bin/bash -xv\necho \"hello world\" > /root/hello-world.txt\n"
+        }
+      }
+    }
+  },
+  "action": "CREATE",
+  "id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
+  "resources": {
+    "hello_world": {
+      "status": "COMPLETE",
+      "name": "hello_world",
+      "resource_id": "8a310d36-46fc-436f-8be4-37a696b8ac63",
+      "action": "CREATE",
+      "type": "OS::Nova::Server",
+    }
+  }
+}`
+
+// HandleAbandonSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87/abandon`
+// on the test handler mux that responds with an `Abandon` response.
+func HandleAbandonSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87/abandon", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, AbandonOutput)
+	})
+}
diff --git a/openstack/orchestration/v1/stacks/requests.go b/openstack/orchestration/v1/stacks/requests.go
index 81b4ea0..49f9749 100644
--- a/openstack/orchestration/v1/stacks/requests.go
+++ b/openstack/orchestration/v1/stacks/requests.go
@@ -8,13 +8,16 @@
 	"github.com/rackspace/gophercloud/pagination"
 )
 
+// Rollback is used to specify whether or not a stack can be rolled back.
 type Rollback *bool
 
 var (
-	disable          = true
+	disable = true
+	// Disable is used to specify that a stack cannot be rolled back.
 	Disable Rollback = &disable
 	enable           = false
-	Enable  Rollback = &enable
+	// Enable is used to specify that a stack can be rolled back.
+	Enable Rollback = &enable
 )
 
 // CreateOptsBuilder is the interface options structs have to satisfy in order
@@ -517,7 +520,7 @@
 	var res AbandonResult
 
 	// Send request to API
-	_, res.Err = perigee.Request("POST", abandonURL(c, stackName, stackID), perigee.Options{
+	_, res.Err = perigee.Request("DELETE", abandonURL(c, stackName, stackID), perigee.Options{
 		MoreHeaders: c.AuthenticatedHeaders(),
 		Results:     &res.Body,
 		OkCodes:     []int{200},
diff --git a/openstack/orchestration/v1/stacks/requests_test.go b/openstack/orchestration/v1/stacks/requests_test.go
index 1e32ca2..8217529 100644
--- a/openstack/orchestration/v1/stacks/requests_test.go
+++ b/openstack/orchestration/v1/stacks/requests_test.go
@@ -215,3 +215,15 @@
 	expected := PreviewExpected
 	th.AssertDeepEquals(t, expected, actual)
 }
+
+func TestAbandonStack(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleAbandonSuccessfully(t)
+
+	actual, err := Abandon(fake.ServiceClient(), "postman_stack", "16ef0584-4458-41eb-87c8-0dc8d5f66c87").Extract()
+	th.AssertNoErr(t, err)
+
+	expected := AbandonExpected
+	th.AssertDeepEquals(t, expected, actual)
+}
diff --git a/openstack/orchestration/v1/stacks/results.go b/openstack/orchestration/v1/stacks/results.go
index b648fb2..84d6f9e 100644
--- a/openstack/orchestration/v1/stacks/results.go
+++ b/openstack/orchestration/v1/stacks/results.go
@@ -253,9 +253,33 @@
 	return res.Stack, err
 }
 
+// AbandonedStack represents the result of an Abandon operation.
 type AbandonedStack struct {
+	Status    string                 `mapstructure:"status"`
+	Name      string                 `mapstructure:"name"`
+	Template  string                 `mapstructure:"template"`
+	Action    string                 `mapstructure:"action"`
+	ID        string                 `mapstructure:"id"`
+	Resources map[string]interface{} `mapstructure:"resources"`
 }
 
+// AbandonResult represents the result of an Abandon operation.
 type AbandonResult struct {
 	gophercloud.Result
 }
+
+// Extract returns a pointer to an AbandonedStack object and is called after an
+// Abandon operation.
+func (r AbandonResult) Extract() (*AbandonedStack, error) {
+	if r.Err != nil {
+		return nil, r.Err
+	}
+
+	var res AbandonedStack
+
+	if err := mapstructure.Decode(r.Body, &res); err != nil {
+		return nil, err
+	}
+
+	return &res, nil
+}