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")
}