rax bulk operations; results update [not working]
diff --git a/rackspace/objectstorage/v1/accounts/delegate_test.go b/rackspace/objectstorage/v1/accounts/delegate_test.go
index bfe3f95..c568bd6 100644
--- a/rackspace/objectstorage/v1/accounts/delegate_test.go
+++ b/rackspace/objectstorage/v1/accounts/delegate_test.go
@@ -14,8 +14,8 @@
 	os.HandleGetAccountSuccessfully(t)
 
 	options := &UpdateOpts{Metadata: map[string]string{"gophercloud-test": "accounts"}}
-	_, err := Update(fake.ServiceClient(), options).ExtractHeaders()
-	th.CheckNoErr(t, err)
+	res := Update(fake.ServiceClient(), options)
+	th.CheckNoErr(t, res.Err)
 }
 
 func TestUpdateAccounts(t *testing.T) {
diff --git a/rackspace/objectstorage/v1/bulk/requests.go b/rackspace/objectstorage/v1/bulk/requests.go
new file mode 100644
index 0000000..6585a14
--- /dev/null
+++ b/rackspace/objectstorage/v1/bulk/requests.go
@@ -0,0 +1,107 @@
+package bulk
+
+import (
+  "archive/tar"
+  "compress/gzip"
+  "compress/bzip2"
+  "errors"
+  "io"
+	"net/url"
+  "os"
+  "path/filepath"
+	"strings"
+
+	"github.com/racker/perigee"
+	"github.com/rackspace/gophercloud"
+)
+
+// DeleteOptsBuilder allows extensions to add additional parameters to the
+// Delete request.
+type DeleteOptsBuilder interface {
+	ToBulkDeleteBody() (string, error)
+}
+
+// DeleteOpts is a structure that holds parameters for deleting an object.
+type DeleteOpts []string
+
+// ToBulkDeleteBody formats a DeleteOpts into a request body.
+func (opts DeleteOpts) ToBulkDeleteBody() (string, error) {
+	return url.QueryEscape(strings.Join(opts, "\n")), nil
+}
+
+// Delete will delete objects or containers in bulk.
+func Delete(c *gophercloud.ServiceClient, opts DeleteOptsBuilder) DeleteResult {
+	var res DeleteResult
+
+	if opts == nil {
+		return res
+	}
+
+	reqString, err := opts.ToBulkDeleteBody()
+	if err != nil {
+		res.Err = err
+		return res
+	}
+
+  reqBody := strings.NewReader(reqString)
+
+	resp, err := perigee.Request("DELETE", deleteURL(c), perigee.Options{
+    ContentType: "text/plain",
+		MoreHeaders: c.Provider.AuthenticatedHeaders(),
+		OkCodes:     []int{200},
+		ReqBody:     reqBody,
+		Results:     &res.Body,
+	})
+	res.Header = resp.HttpResponse.Header
+	res.Err = err
+	return res
+}
+
+// Extract will extract the files in `file` and create objects in object storage
+// from them.
+func Extract(c *gophercloud.ServiceClient, file string) ExtractResult {
+  var res ExtractResult
+
+  if file == ""{
+    res.Err = errors.New("Missing required field 'f'.")
+    return res
+  }
+
+  var ext string
+  var reqBody io.Reader
+  f, err := os.Open(file)
+  if err != nil {
+    res.Err = errors.New("Error opening file.")
+    return res
+  }
+  defer f.Close()
+
+  switch filepath.Ext(file) {
+    case "tar":
+      ext = "tar"
+      reqBody = tar.NewReader(f)
+    case "gz":
+      ext = "tar.gz"
+      reqBody, err = gzip.NewReader(f)
+      if err != nil {
+        res.Err = err
+        return res
+      }
+    case "bz2":
+      ext = "tar.bz2"
+      reqBody = bzip2.NewReader(f)
+    default:
+      res.Err = errors.New("Unsupported extension type.")
+      return res
+  }
+
+  resp, err := perigee.Request("PUT", extractURL(c, ext), perigee.Options{
+    MoreHeaders: c.Provider.AuthenticatedHeaders(),
+    OkCodes:     []int{200},
+    ReqBody:     reqBody,
+    Results:     &res.Body,
+  })
+  res.Header = resp.HttpResponse.Header
+  res.Err = err
+  return res
+}
diff --git a/rackspace/objectstorage/v1/bulk/requests_test.go b/rackspace/objectstorage/v1/bulk/requests_test.go
new file mode 100644
index 0000000..8598f30
--- /dev/null
+++ b/rackspace/objectstorage/v1/bulk/requests_test.go
@@ -0,0 +1,36 @@
+package bulk
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	th "github.com/rackspace/gophercloud/testhelper"
+	fake "github.com/rackspace/gophercloud/testhelper/client"
+)
+
+func TestBulkDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+    th.AssertEquals(t, r.URL.RawQuery, "bulk-delete")
+
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, `
+      {
+        "Number Not Found": 1,
+        "Response Status": "200 OK",
+        "Errors": [],
+        "Number Deleted": 1,
+        "Response Body": ""
+      }
+    `)
+	})
+
+	options := &DeleteOpts{"gophercloud-testcontainer1", "gophercloud-testcontainer2"}
+	actual, err := Delete(fake.ServiceClient(), options).ExtractBody()
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, actual.NumberDeleted, 1)
+}
diff --git a/rackspace/objectstorage/v1/bulk/results.go b/rackspace/objectstorage/v1/bulk/results.go
new file mode 100644
index 0000000..9c7a9a4
--- /dev/null
+++ b/rackspace/objectstorage/v1/bulk/results.go
@@ -0,0 +1,31 @@
+package bulk
+
+import (
+  "github.com/rackspace/gophercloud"
+
+  "github.com/mitchellh/mapstructure"
+
+  )
+
+// DeleteResult represents the result of a delete operation.
+type DeleteResult struct {
+	gophercloud.Result
+}
+
+type DeleteBody struct {
+  NumberNotFound int `mapstructure:"Number Not Found"`
+  ResponseStatus string `mapstructure:"Response Status"`
+  Errors []string `mapstructure:"Errors"`
+  NumberDeleted int `mapstructure:"Number Deleted"`
+  ResponseBody string `mapstructure:"Response Body"`
+}
+
+func (dr DeleteResult) ExtractBody() (DeleteBody, error) {
+  var resp DeleteBody
+  err := mapstructure.Decode(dr.Body, &resp)
+  return resp, err
+}
+
+type ExtractResult struct {
+  gophercloud.Result
+}
diff --git a/rackspace/objectstorage/v1/bulk/urls.go b/rackspace/objectstorage/v1/bulk/urls.go
new file mode 100644
index 0000000..2e11203
--- /dev/null
+++ b/rackspace/objectstorage/v1/bulk/urls.go
@@ -0,0 +1,11 @@
+package bulk
+
+import "github.com/rackspace/gophercloud"
+
+func deleteURL(c *gophercloud.ServiceClient) string {
+	return c.Endpoint + "?bulk-delete"
+}
+
+func extractURL(c *gophercloud.ServiceClient, ext string) string {
+	return c.Endpoint + "?extract-archive=" + ext
+}
diff --git a/rackspace/objectstorage/v1/bulk/urls_test.go b/rackspace/objectstorage/v1/bulk/urls_test.go
new file mode 100644
index 0000000..9169e52
--- /dev/null
+++ b/rackspace/objectstorage/v1/bulk/urls_test.go
@@ -0,0 +1,26 @@
+package bulk
+
+import (
+	"testing"
+
+	"github.com/rackspace/gophercloud"
+	th "github.com/rackspace/gophercloud/testhelper"
+)
+
+const endpoint = "http://localhost:57909/"
+
+func endpointClient() *gophercloud.ServiceClient {
+	return &gophercloud.ServiceClient{Endpoint: endpoint}
+}
+
+func TestDeleteURL(t *testing.T) {
+	actual := deleteURL(endpointClient())
+	expected := endpoint + "?bulk-delete"
+	th.CheckEquals(t, expected, actual)
+}
+
+func TestExtractURL(t *testing.T) {
+	actual := extractURL(endpointClient(), "tar")
+	expected := endpoint + "?extract-archive=tar"
+	th.CheckEquals(t, expected, actual)
+}
diff --git a/rackspace/objectstorage/v1/cdncontainers/requests_test.go b/rackspace/objectstorage/v1/cdncontainers/requests_test.go
index 04ee814..28b963d 100644
--- a/rackspace/objectstorage/v1/cdncontainers/requests_test.go
+++ b/rackspace/objectstorage/v1/cdncontainers/requests_test.go
@@ -22,8 +22,8 @@
 	})
 
 	options := &EnableOpts{CDNEnabled: true, TTL: 259200}
-	actual, err := Enable(fake.ServiceClient(), "testContainer", options).ExtractHeaders()
-	th.AssertNoErr(t, err)
-	th.CheckEquals(t, actual["X-Ttl"][0], "259200")
-	th.CheckEquals(t, actual["X-Cdn-Enabled"][0], "True")
+	actual := Enable(fake.ServiceClient(), "testContainer", options)
+	th.AssertNoErr(t, actual.Err)
+	th.CheckEquals(t, actual.Header["X-Ttl"][0], "259200")
+	th.CheckEquals(t, actual.Header["X-Cdn-Enabled"][0], "True")
 }