struct opts -> interface opts (block storage)
diff --git a/openstack/blockstorage/v1/volumes/requests.go b/openstack/blockstorage/v1/volumes/requests.go
index bca27db..dc04732 100644
--- a/openstack/blockstorage/v1/volumes/requests.go
+++ b/openstack/blockstorage/v1/volumes/requests.go
@@ -1,62 +1,93 @@
package volumes
import (
+ "fmt"
+
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
"github.com/racker/perigee"
)
+// CreateOptsBuilder allows extensions to add additional parameters to the
+// Create request.
+type CreateOptsBuilder interface {
+ ToVolumeCreateMap() (map[string]interface{}, error)
+}
+
// CreateOpts contains options for creating a Volume. This object is passed to
// the volumes.Create function. For more information about these parameters,
// see the Volume object.
type CreateOpts struct {
- Availability string // OPTIONAL
- Description string // OPTIONAL
- Metadata map[string]string // OPTIONAL
- Name string // OPTIONAL
- Size int // REQUIRED
- SnapshotID, SourceVolID, ImageID string // REQUIRED (one of them)
- VolumeType string // OPTIONAL
+ // OPTIONAL
+ Availability string
+ // OPTIONAL
+ Description string
+ // OPTIONAL
+ Metadata map[string]string
+ // OPTIONAL
+ Name string
+ // REQUIRED
+ Size int
+ // OPTIONAL
+ SnapshotID, SourceVolID, ImageID string
+ // OPTIONAL
+ VolumeType string
+}
+
+// ToVolumeCreateMap assembles a request body based on the contents of a
+// CreateOpts.
+func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) {
+ v := make(map[string]interface{})
+
+ if opts.Size == 0 {
+ return nil, fmt.Errorf("Required CreateOpts field 'Size' not set.")
+ }
+ v["size"] = opts.Size
+
+ if opts.Availability != "" {
+ v["availability_zone"] = opts.Availability
+ }
+ if opts.Description != "" {
+ v["display_description"] = opts.Description
+ }
+ if opts.ImageID != "" {
+ v["imageRef"] = opts.ImageID
+ }
+ if opts.Metadata != nil {
+ v["metadata"] = opts.Metadata
+ }
+ if opts.Name != "" {
+ v["display_name"] = opts.Name
+ }
+ if opts.SourceVolID != "" {
+ v["source_volid"] = opts.SourceVolID
+ }
+ if opts.SnapshotID != "" {
+ v["snapshot_id"] = opts.SnapshotID
+ }
+ if opts.VolumeType != "" {
+ v["volume_type"] = opts.VolumeType
+ }
+
+ return map[string]interface{}{"volume": v}, nil
}
// Create will create a new Volume based on the values in CreateOpts. To extract
-// the Volume object from the response, call the Extract method on the CreateResult.
-func Create(client *gophercloud.ServiceClient, opts *CreateOpts) CreateResult {
-
- type volume struct {
- Availability *string `json:"availability_zone,omitempty"`
- Description *string `json:"display_description,omitempty"`
- ImageID *string `json:"imageRef,omitempty"`
- Metadata map[string]string `json:"metadata,omitempty"`
- Name *string `json:"display_name,omitempty"`
- Size *int `json:"size,omitempty"`
- SnapshotID *string `json:"snapshot_id,omitempty"`
- SourceVolID *string `json:"source_volid,omitempty"`
- VolumeType *string `json:"volume_type,omitempty"`
- }
-
- type request struct {
- Volume volume `json:"volume"`
- }
-
- reqBody := request{
- Volume: volume{},
- }
-
- reqBody.Volume.Availability = gophercloud.MaybeString(opts.Availability)
- reqBody.Volume.Description = gophercloud.MaybeString(opts.Description)
- reqBody.Volume.ImageID = gophercloud.MaybeString(opts.ImageID)
- reqBody.Volume.Name = gophercloud.MaybeString(opts.Name)
- reqBody.Volume.Size = gophercloud.MaybeInt(opts.Size)
- reqBody.Volume.SnapshotID = gophercloud.MaybeString(opts.SnapshotID)
- reqBody.Volume.SourceVolID = gophercloud.MaybeString(opts.SourceVolID)
- reqBody.Volume.VolumeType = gophercloud.MaybeString(opts.VolumeType)
-
+// the Volume object from the response, call the Extract method on the
+// CreateResult.
+func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
var res CreateResult
+
+ reqBody, err := opts.ToVolumeCreateMap()
+ if err != nil {
+ res.Err = err
+ return res
+ }
+
_, res.Err = perigee.Request("POST", createURL(client), perigee.Options{
MoreHeaders: client.Provider.AuthenticatedHeaders(),
- ReqBody: &reqBody,
+ ReqBody: reqBody,
Results: &res.Resp,
OkCodes: []int{200, 201},
})
@@ -72,8 +103,8 @@
return err
}
-// Get retrieves the Volume with the provided ID. To extract the Volume object from
-// the response, call the Extract method on the GetResult.
+// Get retrieves the Volume with the provided ID. To extract the Volume object
+// from the response, call the Extract method on the GetResult.
func Get(client *gophercloud.ServiceClient, id string) GetResult {
var res GetResult
_, res.Err = perigee.Request("GET", getURL(client, id), perigee.Options{
@@ -84,66 +115,101 @@
return res
}
+// ListOptsBuilder allows extensions to add additional parameters to the List
+// request.
+type ListOptsBuilder interface {
+ ToVolumeListParams() (string, error)
+}
+
// ListOpts holds options for listing Volumes. It is passed to the volumes.List
// function.
type ListOpts struct {
- AllTenants bool `q:"all_tenants"` // admin-only option. Set it to true to see all tenant volumes.
- Metadata map[string]string `q:"metadata"` // List only volumes that contain Metadata.
- Name string `q:"name"` // List only volumes that have Name as the display name.
- Status string `q:"status"` // List only volumes that have a status of Status.
+ // admin-only option. Set it to true to see all tenant volumes.
+ AllTenants bool `q:"all_tenants"`
+ // List only volumes that contain Metadata.
+ Metadata map[string]string `q:"metadata"`
+ // List only volumes that have Name as the display name.
+ Name string `q:"name"`
+ // List only volumes that have a status of Status.
+ Status string `q:"status"`
+}
+
+// ToVolumeListParams formats a ListOpts into a query string.
+func (opts ListOpts) ToVolumeListParams() (string, error) {
+ q, err := gophercloud.BuildQueryString(opts)
+ if err != nil {
+ return "", err
+ }
+ return q.String(), nil
}
// List returns Volumes optionally limited by the conditions provided in ListOpts.
-func List(client *gophercloud.ServiceClient, opts *ListOpts) pagination.Pager {
+func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
url := listURL(client)
if opts != nil {
- query, err := gophercloud.BuildQueryString(opts)
+ query, err := opts.ToVolumeListParams()
if err != nil {
return pagination.Pager{Err: err}
}
- url += query.String()
+ url += query
}
createPage := func(r pagination.LastHTTPResponse) pagination.Page {
return ListResult{pagination.SinglePageBase(r)}
}
- return pagination.NewPager(client, listURL(client), createPage)
+ return pagination.NewPager(client, url, createPage)
+}
+
+// UpdateOptsBuilder allows extensions to add additional parameters to the
+// Update request.
+type UpdateOptsBuilder interface {
+ ToVolumeUpdateMap() (map[string]interface{}, error)
}
// UpdateOpts contain options for updating an existing Volume. This object is passed
// to the volumes.Update function. For more information about the parameters, see
// the Volume object.
type UpdateOpts struct {
- Name string // OPTIONAL
- Description string // OPTIONAL
- Metadata map[string]string // OPTIONAL
+ // OPTIONAL
+ Name string
+ // OPTIONAL
+ Description string
+ // OPTIONAL
+ Metadata map[string]string
+}
+
+// ToVolumeUpdateMap assembles a request body based on the contents of an
+// UpdateOpts.
+func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) {
+ v := make(map[string]interface{})
+
+ if opts.Description != "" {
+ v["display_description"] = opts.Description
+ }
+ if opts.Metadata != nil {
+ v["metadata"] = opts.Metadata
+ }
+ if opts.Name != "" {
+ v["display_name"] = opts.Name
+ }
+
+ return map[string]interface{}{"volume": v}, nil
}
// Update will update the Volume with provided information. To extract the updated
// Volume from the response, call the Extract method on the UpdateResult.
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"`
- Name *string `json:"display_name,omitempty"`
- }
-
- type request struct {
- Volume update `json:"volume"`
- }
-
- reqBody := request{
- Volume: update{},
- }
-
- reqBody.Volume.Description = gophercloud.MaybeString(opts.Description)
- reqBody.Volume.Name = gophercloud.MaybeString(opts.Name)
-
var res UpdateResult
+ reqBody, err := opts.ToVolumeUpdateMap()
+ if err != nil {
+ res.Err = err
+ return res
+ }
+
_, res.Err = perigee.Request("PUT", updateURL(client, id), perigee.Options{
MoreHeaders: client.Provider.AuthenticatedHeaders(),
OkCodes: []int{200},
- ReqBody: &reqBody,
+ ReqBody: reqBody,
Results: &res.Resp,
})
return res
diff --git a/openstack/blockstorage/v1/volumes/requests_test.go b/openstack/blockstorage/v1/volumes/requests_test.go
index d1632d1..7cd37d5 100644
--- a/openstack/blockstorage/v1/volumes/requests_test.go
+++ b/openstack/blockstorage/v1/volumes/requests_test.go
@@ -119,7 +119,7 @@
th.TestJSONRequest(t, r, `
{
"volume": {
- "display_name": "vol-001"
+ "size": 4
}
}
`)
@@ -130,18 +130,18 @@
fmt.Fprintf(w, `
{
"volume": {
- "display_name": "vol-001",
+ "size": 4,
"id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
}
}
`)
})
- options := &CreateOpts{Name: "vol-001"}
+ options := &CreateOpts{Size: 4}
n, err := Create(ServiceClient(), options).Extract()
th.AssertNoErr(t, err)
- th.AssertEquals(t, n.Name, "vol-001")
+ th.AssertEquals(t, n.Size, 4)
th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
}