unit tests; consistency with the other OpenStack services
diff --git a/openstack/blockstorage/v1/volumes/requests.go b/openstack/blockstorage/v1/volumes/requests.go
index 448ebd5..2a4ce91 100644
--- a/openstack/blockstorage/v1/volumes/requests.go
+++ b/openstack/blockstorage/v1/volumes/requests.go
@@ -17,7 +17,7 @@
VolumeType string
}
-func Create(client *gophercloud.ServiceClient, opts CreateOpts) (*Volume, error) {
+func Create(client *gophercloud.ServiceClient, opts *CreateOpts) CreateResult {
type volume struct {
Availability *string `json:"availability_zone,omitempty"`
@@ -48,42 +48,43 @@
reqBody.Volume.SourceVolID = utils.MaybeString(opts.SourceVolID)
reqBody.Volume.VolumeType = utils.MaybeString(opts.VolumeType)
- type response struct {
- Volume Volume `json:"volume"`
- }
-
- var respBody response
-
- _, err := perigee.Request("POST", volumesURL(client), perigee.Options{
+ var res CreateResult
+ _, res.Err = perigee.Request("POST", createURL(client), perigee.Options{
MoreHeaders: client.Provider.AuthenticatedHeaders(),
- OkCodes: []int{200, 201},
ReqBody: &reqBody,
- Results: &respBody,
+ Results: &res.Resp,
+ OkCodes: []int{200, 201},
})
- if err != nil {
- return nil, err
- }
-
- return &respBody.Volume, nil
+ return res
}
-func List(client *gophercloud.ServiceClient, opts ListOpts) pagination.Pager {
+// ListOpts holds options for listing volumes. It is passed to the volumes.List function.
+type ListOpts struct {
+ // AllTenants is an admin-only option. Set it to true to see a tenant volumes.
+ AllTenants bool
+ // List only volumes that contain Metadata.
+ Metadata map[string]string
+ // List only volumes that have Name as the display name.
+ Name string
+ // List only volumes that have a status of Status.
+ Status string
+}
+func List(client *gophercloud.ServiceClient, opts *ListOpts) pagination.Pager {
createPage := func(r pagination.LastHTTPResponse) pagination.Page {
return ListResult{pagination.SinglePageBase(r)}
}
-
- return pagination.NewPager(client, volumesURL(client), createPage)
+ return pagination.NewPager(client, listURL(client), createPage)
}
func Get(client *gophercloud.ServiceClient, id string) GetResult {
- var gr GetResult
- _, err := perigee.Request("GET", volumeURL(client, id), perigee.Options{
- Results: &gr.r,
+ var res GetResult
+ _, res.Err = perigee.Request("GET", getURL(client, id), perigee.Options{
+ Results: &res.Resp,
MoreHeaders: client.Provider.AuthenticatedHeaders(),
+ OkCodes: []int{200},
})
- gr.err = err
- return gr
+ return res
}
type UpdateOpts struct {
@@ -92,7 +93,7 @@
Metadata map[string]string
}
-func Update(client *gophercloud.ServiceClient, id string, opts UpdateOpts) (*Volume, error) {
+func Update(client *gophercloud.ServiceClient, id string, opts *UpdateOpts) UpdateResult {
type update struct {
Description *string `json:"display_description,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
@@ -110,29 +111,23 @@
reqBody.Volume.Description = utils.MaybeString(opts.Description)
reqBody.Volume.Name = utils.MaybeString(opts.Name)
- type response struct {
- Volume Volume `json:"volume"`
- }
+ var res UpdateResult
- var respBody response
-
- _, err := perigee.Request("PUT", volumeURL(client, id), perigee.Options{
+ _, res.Err = perigee.Request("PUT", updateURL(client, id), perigee.Options{
MoreHeaders: client.Provider.AuthenticatedHeaders(),
OkCodes: []int{200},
ReqBody: &reqBody,
- Results: &respBody,
+ Results: &res.Resp,
})
- if err != nil {
- return nil, err
- }
-
- return &respBody.Volume, nil
+ return res
}
-func Delete(client *gophercloud.ServiceClient, id string) error {
- _, err := perigee.Request("DELETE", volumeURL(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.Provider.AuthenticatedHeaders(),
+ OkCodes: []int{204},
})
- return err
+ return res
}
diff --git a/openstack/blockstorage/v1/volumes/requests_test.go b/openstack/blockstorage/v1/volumes/requests_test.go
index bea59b2..2ae8dd3 100644
--- a/openstack/blockstorage/v1/volumes/requests_test.go
+++ b/openstack/blockstorage/v1/volumes/requests_test.go
@@ -1 +1,160 @@
package volumes
+
+import (
+ "fmt"
+ "net/http"
+ "testing"
+
+ "github.com/rackspace/gophercloud"
+ "github.com/rackspace/gophercloud/pagination"
+ th "github.com/rackspace/gophercloud/testhelper"
+)
+
+const TokenID = "123"
+
+func ServiceClient() *gophercloud.ServiceClient {
+ return &gophercloud.ServiceClient{
+ Provider: &gophercloud.ProviderClient{
+ TokenID: TokenID,
+ },
+ Endpoint: th.Endpoint(),
+ }
+}
+
+func TestList(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+
+ th.Mux.HandleFunc("/volumes", func(w http.ResponseWriter, r *http.Request) {
+ th.TestMethod(t, r, "GET")
+ th.TestHeader(t, r, "X-Auth-Token", TokenID)
+
+ w.Header().Add("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+
+ fmt.Fprintf(w, `
+ {
+ "volumes": [
+ {
+ "id": "289da7f8-6440-407c-9fb4-7db01ec49164",
+ "display_name": "vol-001"
+ },
+ {
+ "id": "96c3bda7-c82a-4f50-be73-ca7621794835",
+ "display_name": "vol-002"
+ }
+ ]
+ }
+ `)
+ })
+
+ client := ServiceClient()
+ count := 0
+
+ List(client, &ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+ count++
+ actual, err := ExtractVolumes(page)
+ if err != nil {
+ t.Errorf("Failed to extract volumes: %v", err)
+ return false, err
+ }
+
+ expected := []Volume{
+ Volume{
+ ID: "289da7f8-6440-407c-9fb4-7db01ec49164",
+ Name: "vol-001",
+ },
+ Volume{
+ ID: "96c3bda7-c82a-4f50-be73-ca7621794835",
+ Name: "vol-002",
+ },
+ }
+
+ th.CheckDeepEquals(t, expected, actual)
+
+ return true, nil
+ })
+
+ if count != 1 {
+ t.Errorf("Expected 1 page, got %d", count)
+ }
+}
+
+func TestGet(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+
+ th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
+ th.TestMethod(t, r, "GET")
+ th.TestHeader(t, r, "X-Auth-Token", TokenID)
+
+ w.Header().Add("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ fmt.Fprintf(w, `
+{
+ "volume": {
+ "display_name": "vol-001",
+ "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
+ }
+}
+ `)
+ })
+
+ v, err := Get(ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
+ th.AssertNoErr(t, err)
+
+ th.AssertEquals(t, v.Name, "vol-001")
+ th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+}
+
+func TestCreate(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+
+ th.Mux.HandleFunc("/volumes", func(w http.ResponseWriter, r *http.Request) {
+ th.TestMethod(t, r, "POST")
+ th.TestHeader(t, r, "X-Auth-Token", TokenID)
+ th.TestHeader(t, r, "Content-Type", "application/json")
+ th.TestHeader(t, r, "Accept", "application/json")
+ th.TestJSONRequest(t, r, `
+{
+ "volume": {
+ "display_name": "vol-001"
+ }
+}
+ `)
+
+ w.Header().Add("Content-Type", "application/json")
+ w.WriteHeader(http.StatusCreated)
+
+ fmt.Fprintf(w, `
+{
+ "volume": {
+ "display_name": "vol-001",
+ "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
+ }
+}
+ `)
+ })
+
+ options := &CreateOpts{Name: "vol-001"}
+ n, err := Create(ServiceClient(), options).Extract()
+ th.AssertNoErr(t, err)
+
+ th.AssertEquals(t, n.Name, "vol-001")
+ th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+}
+
+func TestDelete(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+
+ th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
+ th.TestMethod(t, r, "DELETE")
+ th.TestHeader(t, r, "X-Auth-Token", TokenID)
+ w.WriteHeader(http.StatusNoContent)
+ })
+
+ res := Delete(ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+ th.AssertNoErr(t, res.Err)
+}
diff --git a/openstack/blockstorage/v1/volumes/results.go b/openstack/blockstorage/v1/volumes/results.go
index 14dc4c4..961582d 100644
--- a/openstack/blockstorage/v1/volumes/results.go
+++ b/openstack/blockstorage/v1/volumes/results.go
@@ -3,6 +3,7 @@
import (
"fmt"
+ "github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
"github.com/mitchellh/mapstructure"
@@ -24,18 +25,6 @@
Size int `mapstructure:"size"`
}
-// ListOpts holds options for listing volumes. It is passed to the volumes.List function.
-type ListOpts struct {
- // AllTenants is an admin-only option. Set it to true to see a tenant volumes.
- AllTenants bool
- // List only volumes that contain Metadata.
- Metadata map[string]string
- // List only volumes that have Name as the display name.
- Name string
- // List only volumes that have a status of Status.
- Status string
-}
-
// ListResult is a *http.Response that is returned from a call to the List function.
type ListResult struct {
pagination.SinglePageBase
@@ -60,24 +49,36 @@
return response.Volumes, err
}
-type GetResult struct {
- err error
- r map[string]interface{}
+type commonResult struct {
+ gophercloud.CommonResult
}
// ExtractVolume extracts and returns the Volume from a 'Get' request.
-func (gr GetResult) ExtractVolume() (*Volume, error) {
- if gr.err != nil {
- return nil, gr.err
+func (r commonResult) Extract() (*Volume, error) {
+ if r.Err != nil {
+ return nil, r.Err
}
- var response struct {
+ var res struct {
Volume *Volume `json:"volume"`
}
- err := mapstructure.Decode(gr.r, &response)
+ err := mapstructure.Decode(r.Resp, &res)
if err != nil {
- return nil, fmt.Errorf("volumes: Error decoding volumes.GetResult: %v", err)
+ return nil, fmt.Errorf("volumes: Error decoding volumes.commonResult: %v", err)
}
- return response.Volume, nil
+ return res.Volume, nil
}
+
+type GetResult struct {
+ commonResult
+}
+
+type CreateResult struct {
+ commonResult
+}
+type UpdateResult struct {
+ commonResult
+}
+
+type DeleteResult commonResult
diff --git a/openstack/blockstorage/v1/volumes/urls.go b/openstack/blockstorage/v1/volumes/urls.go
index d1a6499..29629a1 100644
--- a/openstack/blockstorage/v1/volumes/urls.go
+++ b/openstack/blockstorage/v1/volumes/urls.go
@@ -2,10 +2,22 @@
import "github.com/rackspace/gophercloud"
-func volumesURL(c *gophercloud.ServiceClient) string {
+func createURL(c *gophercloud.ServiceClient) string {
return c.ServiceURL("volumes")
}
-func volumeURL(c *gophercloud.ServiceClient, id string) string {
+func listURL(c *gophercloud.ServiceClient) string {
+ return createURL(c)
+}
+
+func deleteURL(c *gophercloud.ServiceClient, id string) string {
return c.ServiceURL("volumes", id)
}
+
+func getURL(c *gophercloud.ServiceClient, id string) string {
+ return deleteURL(c, id)
+}
+
+func updateURL(c *gophercloud.ServiceClient, id string) string {
+ return deleteURL(c, id)
+}
diff --git a/openstack/blockstorage/v1/volumes/urls_test.go b/openstack/blockstorage/v1/volumes/urls_test.go
index fac4033..a95270e 100644
--- a/openstack/blockstorage/v1/volumes/urls_test.go
+++ b/openstack/blockstorage/v1/volumes/urls_test.go
@@ -13,14 +13,32 @@
return &gophercloud.ServiceClient{Endpoint: endpoint}
}
-func TestVolumesURL(t *testing.T) {
- actual := volumesURL(endpointClient())
+func TestCreateURL(t *testing.T) {
+ actual := createURL(endpointClient())
expected := endpoint + "volumes"
th.AssertEquals(t, expected, actual)
}
-func TestVolumeURL(t *testing.T) {
- actual := volumeURL(endpointClient(), "foo")
+func TestListURL(t *testing.T) {
+ actual := listURL(endpointClient())
+ expected := endpoint + "volumes"
+ th.AssertEquals(t, expected, actual)
+}
+
+func TestDeleteURL(t *testing.T) {
+ actual := deleteURL(endpointClient(), "foo")
+ expected := endpoint + "volumes/foo"
+ th.AssertEquals(t, expected, actual)
+}
+
+func TestGetURL(t *testing.T) {
+ actual := getURL(endpointClient(), "foo")
+ expected := endpoint + "volumes/foo"
+ th.AssertEquals(t, expected, actual)
+}
+
+func TestUpdateURL(t *testing.T) {
+ actual := updateURL(endpointClient(), "foo")
expected := endpoint + "volumes/foo"
th.AssertEquals(t, expected, actual)
}