images service v2 port from rackpsace/gophercloud (#171)
* CheckByteArrayEquals funcs
* direct port from rackspace/gophercloud with minor additions to get unit tests passing
* new package for uploading and downloading image data
* updates to make imageservice v2 consistent with the rest of gophercloud/gophercloud
* add image service v2 client
diff --git a/openstack/imageservice/v2/imagedata/testing/fixtures.go b/openstack/imageservice/v2/imagedata/testing/fixtures.go
new file mode 100644
index 0000000..fe93fc9
--- /dev/null
+++ b/openstack/imageservice/v2/imagedata/testing/fixtures.go
@@ -0,0 +1,40 @@
+package testing
+
+import (
+ "io/ioutil"
+ "net/http"
+ "testing"
+
+ th "github.com/gophercloud/gophercloud/testhelper"
+ fakeclient "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// HandlePutImageDataSuccessfully setup
+func HandlePutImageDataSuccessfully(t *testing.T) {
+ th.Mux.HandleFunc("/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/file", func(w http.ResponseWriter, r *http.Request) {
+ th.TestMethod(t, r, "PUT")
+ th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID)
+
+ b, err := ioutil.ReadAll(r.Body)
+ if err != nil {
+ t.Errorf("Unable to read request body: %v", err)
+ }
+
+ th.AssertByteArrayEquals(t, []byte{5, 3, 7, 24}, b)
+
+ w.WriteHeader(http.StatusNoContent)
+ })
+}
+
+// HandleGetImageDataSuccessfully setup
+func HandleGetImageDataSuccessfully(t *testing.T) {
+ th.Mux.HandleFunc("/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/file", func(w http.ResponseWriter, r *http.Request) {
+ th.TestMethod(t, r, "GET")
+ th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID)
+
+ w.WriteHeader(http.StatusOK)
+
+ _, err := w.Write([]byte{34, 87, 0, 23, 23, 23, 56, 255, 254, 0})
+ th.AssertNoErr(t, err)
+ })
+}
diff --git a/openstack/imageservice/v2/imagedata/testing/requests_test.go b/openstack/imageservice/v2/imagedata/testing/requests_test.go
new file mode 100644
index 0000000..4ac42d0
--- /dev/null
+++ b/openstack/imageservice/v2/imagedata/testing/requests_test.go
@@ -0,0 +1,87 @@
+package testing
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "testing"
+
+ "github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata"
+ th "github.com/gophercloud/gophercloud/testhelper"
+ fakeclient "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func TestUpload(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+
+ HandlePutImageDataSuccessfully(t)
+
+ err := imagedata.Upload(
+ fakeclient.ServiceClient(),
+ "da3b75d9-3f4a-40e7-8a2c-bfab23927dea",
+ readSeekerOfBytes([]byte{5, 3, 7, 24})).ExtractErr()
+
+ th.AssertNoErr(t, err)
+}
+
+func readSeekerOfBytes(bs []byte) io.ReadSeeker {
+ return &RS{bs: bs}
+}
+
+// implements io.ReadSeeker
+type RS struct {
+ bs []byte
+ offset int
+}
+
+func (rs *RS) Read(p []byte) (int, error) {
+ leftToRead := len(rs.bs) - rs.offset
+
+ if 0 < leftToRead {
+ bytesToWrite := min(leftToRead, len(p))
+ for i := 0; i < bytesToWrite; i++ {
+ p[i] = rs.bs[rs.offset]
+ rs.offset++
+ }
+ return bytesToWrite, nil
+ }
+ return 0, io.EOF
+}
+
+func min(a int, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+func (rs *RS) Seek(offset int64, whence int) (int64, error) {
+ var offsetInt = int(offset)
+ if whence == 0 {
+ rs.offset = offsetInt
+ } else if whence == 1 {
+ rs.offset = rs.offset + offsetInt
+ } else if whence == 2 {
+ rs.offset = len(rs.bs) - offsetInt
+ } else {
+ return 0, fmt.Errorf("For parameter `whence`, expected value in {0,1,2} but got: %#v", whence)
+ }
+
+ return int64(rs.offset), nil
+}
+
+func TestDownload(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+
+ HandleGetImageDataSuccessfully(t)
+
+ rdr, err := imagedata.Download(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea").Extract()
+ th.AssertNoErr(t, err)
+
+ bs, err := ioutil.ReadAll(rdr)
+ th.AssertNoErr(t, err)
+
+ th.AssertByteArrayEquals(t, []byte{34, 87, 0, 23, 23, 23, 56, 255, 254, 0}, bs)
+}