Merge pull request #276 from jamiehannaford/streaming-downloads

Ensuring that reader is closed after being read
diff --git a/acceptance/openstack/blockstorage/v1/snapshots_test.go b/acceptance/openstack/blockstorage/v1/snapshots_test.go
index 5835048..bd6a961 100644
--- a/acceptance/openstack/blockstorage/v1/snapshots_test.go
+++ b/acceptance/openstack/blockstorage/v1/snapshots_test.go
@@ -47,8 +47,8 @@
 
 	t.Logf("Created snapshot: %+v\n", ss)
 
-	err = snapshots.Delete(client, ss.ID)
-	if err != nil {
+	res = snapshots.Delete(client, ss.ID)
+	if res.Err != nil {
 		t.Fatalf("Failed to delete snapshot: %v", err)
 	}
 
@@ -66,8 +66,8 @@
 
 	t.Log("Deleted snapshot\n")
 
-	err = volumes.Delete(client, v.ID)
-	if err != nil {
+	res = volumes.Delete(client, v.ID)
+	if res.Err != nil {
 		t.Errorf("Failed to delete volume: %v", err)
 	}
 
diff --git a/acceptance/openstack/blockstorage/v1/volumes_test.go b/acceptance/openstack/blockstorage/v1/volumes_test.go
index 6739a99..d2281e6 100644
--- a/acceptance/openstack/blockstorage/v1/volumes_test.go
+++ b/acceptance/openstack/blockstorage/v1/volumes_test.go
@@ -47,8 +47,8 @@
 		if err != nil {
 			t.Error(err)
 		}
-		err = volumes.Delete(client, cv.ID)
-		if err != nil {
+		res = volumes.Delete(client, cv.ID)
+		if res.Err != nil {
 			t.Error(err)
 			return
 		}
diff --git a/acceptance/rackspace/blockstorage/v1/snapshot_test.go b/acceptance/rackspace/blockstorage/v1/snapshot_test.go
index be1314b..25b2cfe 100644
--- a/acceptance/rackspace/blockstorage/v1/snapshot_test.go
+++ b/acceptance/rackspace/blockstorage/v1/snapshot_test.go
@@ -76,7 +76,7 @@
 }
 
 func testSnapshotDelete(t *testing.T, client *gophercloud.ServiceClient, id string) {
-	err := snapshots.Delete(client, id)
-	th.AssertNoErr(t, err)
+	res := snapshots.Delete(client, id)
+	th.AssertNoErr(t, res.Err)
 	t.Logf("Deleted snapshot %s", id)
 }
diff --git a/acceptance/rackspace/blockstorage/v1/volume_test.go b/acceptance/rackspace/blockstorage/v1/volume_test.go
index 5a52ac7..f86f9ad 100644
--- a/acceptance/rackspace/blockstorage/v1/volume_test.go
+++ b/acceptance/rackspace/blockstorage/v1/volume_test.go
@@ -65,7 +65,7 @@
 }
 
 func testVolumeDelete(t *testing.T, client *gophercloud.ServiceClient, id string) {
-	err := volumes.Delete(client, id)
-	th.AssertNoErr(t, err)
+	res := volumes.Delete(client, id)
+	th.AssertNoErr(t, res.Err)
 	t.Logf("Deleted volume %s", id)
 }
diff --git a/acceptance/rackspace/compute/v2/servers_test.go b/acceptance/rackspace/compute/v2/servers_test.go
index 5359450..58b5fa3 100644
--- a/acceptance/rackspace/compute/v2/servers_test.go
+++ b/acceptance/rackspace/compute/v2/servers_test.go
@@ -166,8 +166,8 @@
 func deleteServer(t *testing.T, client *gophercloud.ServiceClient, server *os.Server) {
 	t.Logf("> servers.Delete")
 
-	err := servers.Delete(client, server.ID)
-	th.AssertNoErr(t, err)
+	res := servers.Delete(client, server.ID)
+	th.AssertNoErr(t, res.Err)
 
 	t.Logf("Server deleted successfully.")
 }
diff --git a/openstack/blockstorage/v1/snapshots/requests.go b/openstack/blockstorage/v1/snapshots/requests.go
index 8cb130d..443f696 100644
--- a/openstack/blockstorage/v1/snapshots/requests.go
+++ b/openstack/blockstorage/v1/snapshots/requests.go
@@ -79,12 +79,13 @@
 }
 
 // Delete will delete the existing Snapshot with the provided ID.
-func Delete(client *gophercloud.ServiceClient, id string) error {
-	_, err := perigee.Request("DELETE", deleteURL(client, id), perigee.Options{
+func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
+	var res DeleteResult
+	_, res.Err = perigee.Request("DELETE", deleteURL(client, id), perigee.Options{
 		MoreHeaders: client.AuthenticatedHeaders(),
 		OkCodes:     []int{202, 204},
 	})
-	return err
+	return res
 }
 
 // Get retrieves the Snapshot with the provided ID. To extract the Snapshot
diff --git a/openstack/blockstorage/v1/snapshots/requests_test.go b/openstack/blockstorage/v1/snapshots/requests_test.go
index 8db55d9..d0f9e88 100644
--- a/openstack/blockstorage/v1/snapshots/requests_test.go
+++ b/openstack/blockstorage/v1/snapshots/requests_test.go
@@ -99,6 +99,6 @@
 
 	MockDeleteResponse(t)
 
-	err := Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-	th.AssertNoErr(t, err)
+	res := Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+	th.AssertNoErr(t, res.Err)
 }
diff --git a/openstack/blockstorage/v1/snapshots/results.go b/openstack/blockstorage/v1/snapshots/results.go
index d414a7d..c531a04 100644
--- a/openstack/blockstorage/v1/snapshots/results.go
+++ b/openstack/blockstorage/v1/snapshots/results.go
@@ -59,6 +59,11 @@
 	commonResult
 }
 
+// DeleteResult contains the response body and error from a Delete request.
+type DeleteResult struct {
+	gophercloud.ExtractErrResult
+}
+
 // ListResult is a pagination.Pager that is returned from a call to the List function.
 type ListResult struct {
 	pagination.SinglePageBase
diff --git a/openstack/blockstorage/v1/volumes/requests.go b/openstack/blockstorage/v1/volumes/requests.go
index fa2202c..f4332de 100644
--- a/openstack/blockstorage/v1/volumes/requests.go
+++ b/openstack/blockstorage/v1/volumes/requests.go
@@ -95,12 +95,13 @@
 }
 
 // Delete will delete the existing Volume with the provided ID.
-func Delete(client *gophercloud.ServiceClient, id string) error {
-	_, err := perigee.Request("DELETE", deleteURL(client, id), perigee.Options{
+func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
+	var res DeleteResult
+	_, res.Err = perigee.Request("DELETE", deleteURL(client, id), perigee.Options{
 		MoreHeaders: client.AuthenticatedHeaders(),
 		OkCodes:     []int{202, 204},
 	})
-	return err
+	return res
 }
 
 // Get retrieves the Volume with the provided ID. To extract the Volume object
diff --git a/openstack/blockstorage/v1/volumes/requests_test.go b/openstack/blockstorage/v1/volumes/requests_test.go
index 11b950e..067f89b 100644
--- a/openstack/blockstorage/v1/volumes/requests_test.go
+++ b/openstack/blockstorage/v1/volumes/requests_test.go
@@ -78,8 +78,8 @@
 
 	MockDeleteResponse(t)
 
-	err := Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-	th.AssertNoErr(t, err)
+	res := Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+	th.AssertNoErr(t, res.Err)
 }
 
 func TestUpdate(t *testing.T) {
diff --git a/openstack/blockstorage/v1/volumes/results.go b/openstack/blockstorage/v1/volumes/results.go
index ca322d1..0572558 100644
--- a/openstack/blockstorage/v1/volumes/results.go
+++ b/openstack/blockstorage/v1/volumes/results.go
@@ -59,6 +59,11 @@
 	commonResult
 }
 
+// DeleteResult contains the response body and error from a Delete request.
+type DeleteResult struct {
+	gophercloud.ExtractErrResult
+}
+
 // ListResult is a pagination.pager that is returned from a call to the List function.
 type ListResult struct {
 	pagination.SinglePageBase
diff --git a/openstack/compute/v2/extensions/keypairs/requests.go b/openstack/compute/v2/extensions/keypairs/requests.go
index 01ee12a..7d1a2ac 100644
--- a/openstack/compute/v2/extensions/keypairs/requests.go
+++ b/openstack/compute/v2/extensions/keypairs/requests.go
@@ -82,7 +82,6 @@
 	var res DeleteResult
 	_, res.Err = perigee.Request("DELETE", deleteURL(client, name), perigee.Options{
 		MoreHeaders: client.AuthenticatedHeaders(),
-		Results:     &res.Body,
 		OkCodes:     []int{202},
 	})
 	return res
diff --git a/openstack/compute/v2/extensions/keypairs/results.go b/openstack/compute/v2/extensions/keypairs/results.go
index 96b96ea..dc49f99 100644
--- a/openstack/compute/v2/extensions/keypairs/results.go
+++ b/openstack/compute/v2/extensions/keypairs/results.go
@@ -90,10 +90,5 @@
 // DeleteResult is the response from a Delete operation. Call its Extract method to determine if
 // the call succeeded or failed.
 type DeleteResult struct {
-	gophercloud.Result
-}
-
-// Extract determines whether or not a deletion request was accepted.
-func (r DeleteResult) Extract() error {
-	return r.Err
+	gophercloud.ExtractErrResult
 }
diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go
index 544f816..3465cbc 100644
--- a/openstack/compute/v2/servers/requests.go
+++ b/openstack/compute/v2/servers/requests.go
@@ -206,12 +206,13 @@
 }
 
 // Delete requests that a server previously provisioned be removed from your account.
-func Delete(client *gophercloud.ServiceClient, id string) error {
-	_, err := perigee.Request("DELETE", deleteURL(client, id), perigee.Options{
+func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
+	var res DeleteResult
+	_, res.Err = perigee.Request("DELETE", deleteURL(client, id), perigee.Options{
 		MoreHeaders: client.AuthenticatedHeaders(),
 		OkCodes:     []int{204},
 	})
-	return err
+	return res
 }
 
 // Get requests details on a single server, by ID.
@@ -283,7 +284,6 @@
 
 	_, res.Err = perigee.Request("POST", actionURL(client, id), perigee.Options{
 		ReqBody:     req,
-		Results:     &res.Body,
 		MoreHeaders: client.AuthenticatedHeaders(),
 		OkCodes:     []int{202},
 	})
diff --git a/openstack/compute/v2/servers/requests_test.go b/openstack/compute/v2/servers/requests_test.go
index 23fe781..392e2d8 100644
--- a/openstack/compute/v2/servers/requests_test.go
+++ b/openstack/compute/v2/servers/requests_test.go
@@ -59,8 +59,8 @@
 	defer th.TeardownHTTP()
 	HandleServerDeletionSuccessfully(t)
 
-	err := Delete(client.ServiceClient(), "asdfasdfasdf")
-	th.AssertNoErr(t, err)
+	res := Delete(client.ServiceClient(), "asdfasdfasdf")
+	th.AssertNoErr(t, res.Err)
 }
 
 func TestGetServer(t *testing.T) {
diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go
index 74a221f..63cc257 100644
--- a/openstack/compute/v2/servers/results.go
+++ b/openstack/compute/v2/servers/results.go
@@ -39,6 +39,11 @@
 	serverResult
 }
 
+// DeleteResult temporarily contains the response from an Delete call.
+type DeleteResult struct {
+	gophercloud.ExtractErrResult
+}
+
 // RebuildResult temporarily contains the response from a Rebuild call.
 type RebuildResult struct {
 	serverResult
@@ -46,12 +51,7 @@
 
 // ActionResult represents the result of server action operations, like reboot
 type ActionResult struct {
-	gophercloud.Result
-}
-
-// Extract is a function that extracts error information from a result
-func (r ActionResult) Extract() error {
-	return r.Err
+	gophercloud.ExtractErrResult
 }
 
 // Server exposes only the standard OpenStack fields corresponding to a given server on the user's account.
@@ -82,8 +82,7 @@
 	Progress int
 
 	// AccessIPv4 and AccessIPv6 contain the IP addresses of the server, suitable for remote access for administration.
-	AccessIPv4 string
-	AccessIPv6 string
+	AccessIPv4, AccessIPv6 string
 
 	// Image refers to a JSON object, which itself indicates the OS image used to deploy the server.
 	Image map[string]interface{}
diff --git a/openstack/identity/v3/endpoints/requests.go b/openstack/identity/v3/endpoints/requests.go
index 4bec427..66bdb0e 100644
--- a/openstack/identity/v3/endpoints/requests.go
+++ b/openstack/identity/v3/endpoints/requests.go
@@ -134,10 +134,11 @@
 }
 
 // Delete removes an endpoint from the service catalog.
-func Delete(client *gophercloud.ServiceClient, endpointID string) error {
-	_, err := perigee.Request("DELETE", endpointURL(client, endpointID), perigee.Options{
+func Delete(client *gophercloud.ServiceClient, endpointID string) DeleteResult {
+	var res DeleteResult
+	_, res.Err = perigee.Request("DELETE", endpointURL(client, endpointID), perigee.Options{
 		MoreHeaders: client.AuthenticatedHeaders(),
 		OkCodes:     []int{204},
 	})
-	return err
+	return res
 }
diff --git a/openstack/identity/v3/endpoints/requests_test.go b/openstack/identity/v3/endpoints/requests_test.go
index 381461c..80687c4 100644
--- a/openstack/identity/v3/endpoints/requests_test.go
+++ b/openstack/identity/v3/endpoints/requests_test.go
@@ -221,8 +221,6 @@
 		w.WriteHeader(http.StatusNoContent)
 	})
 
-	err := Delete(client.ServiceClient(), "34")
-	if err != nil {
-		t.Fatalf("Unexpected error from Delete: %v", err)
-	}
+	res := Delete(client.ServiceClient(), "34")
+	testhelper.AssertNoErr(t, res.Err)
 }
diff --git a/openstack/identity/v3/endpoints/results.go b/openstack/identity/v3/endpoints/results.go
index f559b9a..2aa0483 100644
--- a/openstack/identity/v3/endpoints/results.go
+++ b/openstack/identity/v3/endpoints/results.go
@@ -41,6 +41,11 @@
 	commonResult
 }
 
+// DeleteResult is the deferred result of an Delete call.
+type DeleteResult struct {
+	gophercloud.ExtractErrResult
+}
+
 // Endpoint describes the entry point for another service's API.
 type Endpoint struct {
 	ID           string                   `mapstructure:"id" json:"id"`
diff --git a/openstack/identity/v3/services/requests.go b/openstack/identity/v3/services/requests.go
index 425a67c..bf027e8 100644
--- a/openstack/identity/v3/services/requests.go
+++ b/openstack/identity/v3/services/requests.go
@@ -89,10 +89,11 @@
 
 // Delete removes an existing service.
 // It either deletes all associated endpoints, or fails until all endpoints are deleted.
-func Delete(client *gophercloud.ServiceClient, serviceID string) error {
-	_, err := perigee.Request("DELETE", serviceURL(client, serviceID), perigee.Options{
+func Delete(client *gophercloud.ServiceClient, serviceID string) DeleteResult {
+	var res DeleteResult
+	_, res.Err = perigee.Request("DELETE", serviceURL(client, serviceID), perigee.Options{
 		MoreHeaders: client.AuthenticatedHeaders(),
 		OkCodes:     []int{204},
 	})
-	return err
+	return res
 }
diff --git a/openstack/identity/v3/services/requests_test.go b/openstack/identity/v3/services/requests_test.go
index 337647c..32e6d1b 100644
--- a/openstack/identity/v3/services/requests_test.go
+++ b/openstack/identity/v3/services/requests_test.go
@@ -204,8 +204,6 @@
 		w.WriteHeader(http.StatusNoContent)
 	})
 
-	err := Delete(client.ServiceClient(), "12345")
-	if err != nil {
-		t.Fatalf("Unable to delete service: %v", err)
-	}
+	res := Delete(client.ServiceClient(), "12345")
+	testhelper.AssertNoErr(t, res.Err)
 }
diff --git a/openstack/identity/v3/services/results.go b/openstack/identity/v3/services/results.go
index dcd7fe2..d90b1bd 100644
--- a/openstack/identity/v3/services/results.go
+++ b/openstack/identity/v3/services/results.go
@@ -42,6 +42,11 @@
 	commonResult
 }
 
+// DeleteResult is the deferred result of an Delete call.
+type DeleteResult struct {
+	gophercloud.ExtractErrResult
+}
+
 // Service is the result of a list or information query.
 type Service struct {
 	Description *string `json:"description,omitempty"`
diff --git a/openstack/identity/v3/tokens/requests.go b/openstack/identity/v3/tokens/requests.go
index 351d7d6..5ca1031 100644
--- a/openstack/identity/v3/tokens/requests.go
+++ b/openstack/identity/v3/tokens/requests.go
@@ -276,10 +276,11 @@
 }
 
 // Revoke immediately makes specified token invalid.
-func Revoke(c *gophercloud.ServiceClient, token string) error {
-	_, err := perigee.Request("DELETE", tokenURL(c), perigee.Options{
+func Revoke(c *gophercloud.ServiceClient, token string) RevokeResult {
+	var res RevokeResult
+	_, res.Err = perigee.Request("DELETE", tokenURL(c), perigee.Options{
 		MoreHeaders: subjectTokenHeaders(c, token),
 		OkCodes:     []int{204},
 	})
-	return err
+	return res
 }
diff --git a/openstack/identity/v3/tokens/requests_test.go b/openstack/identity/v3/tokens/requests_test.go
index a61bb2c..2b26e4a 100644
--- a/openstack/identity/v3/tokens/requests_test.go
+++ b/openstack/identity/v3/tokens/requests_test.go
@@ -498,10 +498,8 @@
 	defer testhelper.TeardownHTTP()
 	client := prepareAuthTokenHandler(t, "DELETE", http.StatusNoContent)
 
-	err := Revoke(&client, "abcdef12345")
-	if err != nil {
-		t.Errorf("Unexpected error from Revoke: %v", err)
-	}
+	res := Revoke(&client, "abcdef12345")
+	testhelper.AssertNoErr(t, res.Err)
 }
 
 func TestRevokeRequestError(t *testing.T) {
@@ -509,8 +507,8 @@
 	defer testhelper.TeardownHTTP()
 	client := prepareAuthTokenHandler(t, "DELETE", http.StatusNotFound)
 
-	err := Revoke(&client, "abcdef12345")
-	if err == nil {
+	res := Revoke(&client, "abcdef12345")
+	if res.Err == nil {
 		t.Errorf("Missing expected error from Revoke")
 	}
 }
diff --git a/openstack/identity/v3/tokens/results.go b/openstack/identity/v3/tokens/results.go
index c31c52f..d1fff4c 100644
--- a/openstack/identity/v3/tokens/results.go
+++ b/openstack/identity/v3/tokens/results.go
@@ -57,6 +57,11 @@
 	commonResult
 }
 
+// RevokeResult is the deferred response from a Revoke call.
+type RevokeResult struct {
+	commonResult
+}
+
 // Token is a string that grants a user access to a controlled set of services in an OpenStack provider.
 // Each Token is valid for a set length of time.
 type Token struct {
diff --git a/openstack/networking/v2/extensions/layer3/floatingips/results.go b/openstack/networking/v2/extensions/layer3/floatingips/results.go
index df3a63f..84719fd 100644
--- a/openstack/networking/v2/extensions/layer3/floatingips/results.go
+++ b/openstack/networking/v2/extensions/layer3/floatingips/results.go
@@ -77,7 +77,9 @@
 }
 
 // DeleteResult represents the result of an update operation.
-type DeleteResult commonResult
+type DeleteResult struct {
+	gophercloud.ExtractErrResult
+}
 
 // FloatingIPPage is the page returned by a pager when traversing over a
 // collection of floating IPs.
diff --git a/openstack/networking/v2/extensions/layer3/routers/results.go b/openstack/networking/v2/extensions/layer3/routers/results.go
index ba2f757..2ce1e6c 100755
--- a/openstack/networking/v2/extensions/layer3/routers/results.go
+++ b/openstack/networking/v2/extensions/layer3/routers/results.go
@@ -121,7 +121,9 @@
 }
 
 // DeleteResult represents the result of a delete operation.
-type DeleteResult commonResult
+type DeleteResult struct {
+	gophercloud.ExtractErrResult
+}
 
 // InterfaceInfo represents information about a particular router interface. As
 // mentioned above, in order for a router to forward to a subnet, it needs an
diff --git a/openstack/networking/v2/extensions/lbaas/members/results.go b/openstack/networking/v2/extensions/lbaas/members/results.go
index a4408e1..447fe29 100644
--- a/openstack/networking/v2/extensions/lbaas/members/results.go
+++ b/openstack/networking/v2/extensions/lbaas/members/results.go
@@ -117,4 +117,6 @@
 }
 
 // DeleteResult represents the result of a delete operation.
-type DeleteResult commonResult
+type DeleteResult struct {
+	gophercloud.ExtractErrResult
+}
diff --git a/openstack/networking/v2/extensions/lbaas/monitors/results.go b/openstack/networking/v2/extensions/lbaas/monitors/results.go
index bd711f7..7f7ff97 100644
--- a/openstack/networking/v2/extensions/lbaas/monitors/results.go
+++ b/openstack/networking/v2/extensions/lbaas/monitors/results.go
@@ -142,4 +142,6 @@
 }
 
 // DeleteResult represents the result of a delete operation.
-type DeleteResult commonResult
+type DeleteResult struct {
+	gophercloud.ExtractErrResult
+}
diff --git a/openstack/networking/v2/extensions/lbaas/pools/results.go b/openstack/networking/v2/extensions/lbaas/pools/results.go
index 3b1c66c..0f95e15 100644
--- a/openstack/networking/v2/extensions/lbaas/pools/results.go
+++ b/openstack/networking/v2/extensions/lbaas/pools/results.go
@@ -136,7 +136,9 @@
 }
 
 // DeleteResult represents the result of a delete operation.
-type DeleteResult commonResult
+type DeleteResult struct {
+	gophercloud.ExtractErrResult
+}
 
 // AssociateResult represents the result of an association operation.
 type AssociateResult struct {
diff --git a/openstack/networking/v2/extensions/lbaas/vips/results.go b/openstack/networking/v2/extensions/lbaas/vips/results.go
index 731edef..39c980a 100644
--- a/openstack/networking/v2/extensions/lbaas/vips/results.go
+++ b/openstack/networking/v2/extensions/lbaas/vips/results.go
@@ -161,4 +161,6 @@
 }
 
 // DeleteResult represents the result of a delete operation.
-type DeleteResult commonResult
+type DeleteResult struct {
+	gophercloud.ExtractErrResult
+}
diff --git a/openstack/networking/v2/extensions/security/groups/results.go b/openstack/networking/v2/extensions/security/groups/results.go
index 3b256ef..9df3268 100644
--- a/openstack/networking/v2/extensions/security/groups/results.go
+++ b/openstack/networking/v2/extensions/security/groups/results.go
@@ -103,4 +103,6 @@
 }
 
 // DeleteResult represents the result of a delete operation.
-type DeleteResult commonResult
+type DeleteResult struct {
+	gophercloud.ExtractErrResult
+}
diff --git a/openstack/networking/v2/extensions/security/rules/results.go b/openstack/networking/v2/extensions/security/rules/results.go
index 29e8676..6684272 100644
--- a/openstack/networking/v2/extensions/security/rules/results.go
+++ b/openstack/networking/v2/extensions/security/rules/results.go
@@ -128,4 +128,6 @@
 }
 
 // DeleteResult represents the result of a delete operation.
-type DeleteResult commonResult
+type DeleteResult struct {
+	gophercloud.ExtractErrResult
+}
diff --git a/openstack/networking/v2/networks/results.go b/openstack/networking/v2/networks/results.go
index 6c32ebe..9b39e8c 100644
--- a/openstack/networking/v2/networks/results.go
+++ b/openstack/networking/v2/networks/results.go
@@ -41,7 +41,9 @@
 }
 
 // DeleteResult represents the result of a delete operation.
-type DeleteResult commonResult
+type DeleteResult struct {
+	gophercloud.ExtractErrResult
+}
 
 // Network represents, well, a network.
 type Network struct {
diff --git a/openstack/networking/v2/ports/results.go b/openstack/networking/v2/ports/results.go
index 6681123..2d72c35 100644
--- a/openstack/networking/v2/ports/results.go
+++ b/openstack/networking/v2/ports/results.go
@@ -41,7 +41,9 @@
 }
 
 // DeleteResult represents the result of a delete operation.
-type DeleteResult commonResult
+type DeleteResult struct {
+	gophercloud.ExtractErrResult
+}
 
 // IP is a sub-struct that represents an individual IP.
 type IP struct {
diff --git a/openstack/networking/v2/subnets/results.go b/openstack/networking/v2/subnets/results.go
index c87504c..70aa543 100644
--- a/openstack/networking/v2/subnets/results.go
+++ b/openstack/networking/v2/subnets/results.go
@@ -41,7 +41,9 @@
 }
 
 // DeleteResult represents the result of a delete operation.
-type DeleteResult commonResult
+type DeleteResult struct {
+	gophercloud.ExtractErrResult
+}
 
 // AllocationPool represents a sub-range of cidr available for dynamic
 // allocation to ports, e.g. {Start: "10.0.0.2", End: "10.0.0.254"}
diff --git a/openstack/objectstorage/v1/containers/requests.go b/openstack/objectstorage/v1/containers/requests.go
index 3fae4d9..9f3b2af 100644
--- a/openstack/objectstorage/v1/containers/requests.go
+++ b/openstack/objectstorage/v1/containers/requests.go
@@ -123,12 +123,10 @@
 // Delete is a function that deletes a container.
 func Delete(c *gophercloud.ServiceClient, containerName string) DeleteResult {
 	var res DeleteResult
-	resp, err := perigee.Request("DELETE", deleteURL(c, containerName), perigee.Options{
+	_, res.Err = perigee.Request("DELETE", deleteURL(c, containerName), perigee.Options{
 		MoreHeaders: c.AuthenticatedHeaders(),
 		OkCodes:     []int{202, 204},
 	})
-	res.Header = resp.HttpResponse.Header
-	res.Err = err
 	return res
 }
 
diff --git a/openstack/objectstorage/v1/containers/results.go b/openstack/objectstorage/v1/containers/results.go
index c00a4bc..f7f84c3 100644
--- a/openstack/objectstorage/v1/containers/results.go
+++ b/openstack/objectstorage/v1/containers/results.go
@@ -139,5 +139,5 @@
 // the headers from the HTTP response, you can invoke the 'ExtractHeaders'
 // method on the result struct.
 type DeleteResult struct {
-	commonResult
+	gophercloud.ExtractErrResult
 }
diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go
index 56004b2..f5ca296 100644
--- a/openstack/objectstorage/v1/objects/requests.go
+++ b/openstack/objectstorage/v1/objects/requests.go
@@ -303,12 +303,11 @@
 		url += query
 	}
 
-	resp, err := perigee.Request("DELETE", url, perigee.Options{
+	_, res.Err = perigee.Request("DELETE", url, perigee.Options{
 		MoreHeaders: c.AuthenticatedHeaders(),
 		OkCodes:     []int{204},
 	})
-	res.Header = resp.HttpResponse.Header
-	res.Err = err
+
 	return res
 }
 
diff --git a/openstack/objectstorage/v1/objects/results.go b/openstack/objectstorage/v1/objects/results.go
index f7ee75c..97020df 100644
--- a/openstack/objectstorage/v1/objects/results.go
+++ b/openstack/objectstorage/v1/objects/results.go
@@ -157,7 +157,7 @@
 
 // DeleteResult represents the result of a delete operation.
 type DeleteResult struct {
-	commonResult
+	gophercloud.ExtractErrResult
 }
 
 // CopyResult represents the result of a copy operation.
diff --git a/rackspace/blockstorage/v1/snapshots/delegate.go b/rackspace/blockstorage/v1/snapshots/delegate.go
index 3ae2438..b338c36 100644
--- a/rackspace/blockstorage/v1/snapshots/delegate.go
+++ b/rackspace/blockstorage/v1/snapshots/delegate.go
@@ -67,7 +67,7 @@
 }
 
 // Delete will delete the existing Snapshot with the provided ID.
-func Delete(client *gophercloud.ServiceClient, id string) error {
+func Delete(client *gophercloud.ServiceClient, id string) os.DeleteResult {
 	return os.Delete(client, id)
 }
 
diff --git a/rackspace/blockstorage/v1/snapshots/delegate_test.go b/rackspace/blockstorage/v1/snapshots/delegate_test.go
index fad7636..1a02b46 100644
--- a/rackspace/blockstorage/v1/snapshots/delegate_test.go
+++ b/rackspace/blockstorage/v1/snapshots/delegate_test.go
@@ -92,6 +92,6 @@
 
 	os.MockDeleteResponse(t)
 
-	err := Delete(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-	th.AssertNoErr(t, err)
+	res := Delete(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+	th.AssertNoErr(t, res.Err)
 }
diff --git a/rackspace/blockstorage/v1/volumes/delegate.go b/rackspace/blockstorage/v1/volumes/delegate.go
index 4f14454..4383494 100644
--- a/rackspace/blockstorage/v1/volumes/delegate.go
+++ b/rackspace/blockstorage/v1/volumes/delegate.go
@@ -28,7 +28,7 @@
 }
 
 // Delete will delete the existing Volume with the provided ID.
-func Delete(client *gophercloud.ServiceClient, id string) error {
+func Delete(client *gophercloud.ServiceClient, id string) os.DeleteResult {
 	return os.Delete(client, id)
 }
 
diff --git a/rackspace/blockstorage/v1/volumes/delegate_test.go b/rackspace/blockstorage/v1/volumes/delegate_test.go
index 2383c54..b44564c 100644
--- a/rackspace/blockstorage/v1/volumes/delegate_test.go
+++ b/rackspace/blockstorage/v1/volumes/delegate_test.go
@@ -89,8 +89,8 @@
 
 	os.MockDeleteResponse(t)
 
-	err := Delete(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
-	th.AssertNoErr(t, err)
+	res := Delete(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+	th.AssertNoErr(t, res.Err)
 }
 
 func TestUpdate(t *testing.T) {
diff --git a/rackspace/compute/v2/networks/results.go b/rackspace/compute/v2/networks/results.go
index 823c56e..558df47 100644
--- a/rackspace/compute/v2/networks/results.go
+++ b/rackspace/compute/v2/networks/results.go
@@ -36,7 +36,9 @@
 }
 
 // DeleteResult represents the result of a delete operation.
-type DeleteResult commonResult
+type DeleteResult struct {
+	gophercloud.ExtractErrResult
+}
 
 // Network represents, well, a network.
 type Network struct {
diff --git a/rackspace/compute/v2/servers/delegate.go b/rackspace/compute/v2/servers/delegate.go
index cbf5384..4c7b249 100644
--- a/rackspace/compute/v2/servers/delegate.go
+++ b/rackspace/compute/v2/servers/delegate.go
@@ -17,7 +17,7 @@
 }
 
 // Delete requests that a server previously provisioned be removed from your account.
-func Delete(client *gophercloud.ServiceClient, id string) error {
+func Delete(client *gophercloud.ServiceClient, id string) os.DeleteResult {
 	return os.Delete(client, id)
 }
 
diff --git a/rackspace/compute/v2/servers/delegate_test.go b/rackspace/compute/v2/servers/delegate_test.go
index 0c331eb..7f41404 100644
--- a/rackspace/compute/v2/servers/delegate_test.go
+++ b/rackspace/compute/v2/servers/delegate_test.go
@@ -56,8 +56,8 @@
 	defer th.TeardownHTTP()
 	os.HandleServerDeletionSuccessfully(t)
 
-	err := Delete(client.ServiceClient(), "asdfasdfasdf")
-	th.AssertNoErr(t, err)
+	res := Delete(client.ServiceClient(), "asdfasdfasdf")
+	th.AssertNoErr(t, res.Err)
 }
 
 func TestGetServer(t *testing.T) {
diff --git a/rackspace/compute/v2/virtualinterfaces/results.go b/rackspace/compute/v2/virtualinterfaces/results.go
index 6818fcb..14c7a47 100644
--- a/rackspace/compute/v2/virtualinterfaces/results.go
+++ b/rackspace/compute/v2/virtualinterfaces/results.go
@@ -31,7 +31,9 @@
 }
 
 // DeleteResult represents the result of a delete operation.
-type DeleteResult commonResult
+type DeleteResult struct {
+	gophercloud.ExtractErrResult
+}
 
 // IPAddress represents a vitual address attached to a VirtualInterface.
 type IPAddress struct {
diff --git a/results.go b/results.go
index a8e3705..e60088e 100644
--- a/results.go
+++ b/results.go
@@ -28,6 +28,18 @@
 	return string(pretty)
 }
 
+// ExtractErrResult represents results that only contain a potential error and
+// nothing else. Usually if the operation executed successfully, the Err field
+// will be nil; otherwise it will be stocked with a relevant error.
+type ExtractErrResult struct {
+	Err error
+}
+
+// Extract is a function that extracts error information from a result
+func (r ExtractErrResult) Extract() error {
+	return r.Err
+}
+
 // RFC3339Milli describes a time format used by API responses.
 const RFC3339Milli = "2006-01-02T15:04:05.999999Z"
 
diff --git a/util_test.go b/util_test.go
index 6fbd920..52ab801 100644
--- a/util_test.go
+++ b/util_test.go
@@ -2,12 +2,14 @@
 
 import (
 	"testing"
+	"time"
 
 	th "github.com/rackspace/gophercloud/testhelper"
 )
 
 func TestWaitFor(t *testing.T) {
 	err := WaitFor(0, func() (bool, error) {
+		time.Sleep(1 * time.Second)
 		return true, nil
 	})
 	if err == nil {