struct opts -> interface opts (block storage)
diff --git a/openstack/blockstorage/v1/snapshots/requests.go b/openstack/blockstorage/v1/snapshots/requests.go
index f3180d7..f54432f 100644
--- a/openstack/blockstorage/v1/snapshots/requests.go
+++ b/openstack/blockstorage/v1/snapshots/requests.go
@@ -1,54 +1,78 @@
package snapshots
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 {
+ ToSnapshotCreateMap() (map[string]interface{}, error)
+}
+
// CreateOpts contains options for creating a Snapshot. This object is passed to
// the snapshots.Create function. For more information about these parameters,
// see the Snapshot object.
type CreateOpts struct {
- Description string // OPTIONAL
- Force bool // OPTIONAL
- Metadata map[string]interface{} // OPTIONAL
- Name string // OPTIONAL
- VolumeID string // REQUIRED
+ // OPTIONAL
+ Description string
+ // OPTIONAL
+ Force bool
+ // OPTIONAL
+ Metadata map[string]interface{}
+ // OPTIONAL
+ Name string
+ // REQUIRED
+ VolumeID string
}
-// Create will create a new Snapshot based on the values in CreateOpts. To extract
-// the Snapshot object from the response, call the Extract method on the
+// ToSnapshotCreateMap assembles a request body based on the contents of a
+// CreateOpts.
+func (opts CreateOpts) ToSnapshotCreateMap() (map[string]interface{}, error) {
+ s := make(map[string]interface{})
+
+ if opts.VolumeID == "" {
+ return nil, fmt.Errorf("Required CreateOpts field 'VolumeID' not set.")
+ }
+ s["volume_id"] = opts.VolumeID
+
+ if opts.Description != "" {
+ s["display_description"] = opts.Description
+ }
+ if opts.Force == true {
+ s["force"] = opts.Force
+ }
+ if opts.Metadata != nil {
+ s["metadata"] = opts.Metadata
+ }
+ if opts.Name != "" {
+ s["display_name"] = opts.Name
+ }
+
+ return map[string]interface{}{"snapshot": s}, nil
+}
+
+// Create will create a new Snapshot based on the values in CreateOpts. To
+// extract the Snapshot object from the response, call the Extract method on the
// CreateResult.
-func Create(client *gophercloud.ServiceClient, opts *CreateOpts) CreateResult {
- type snapshot struct {
- Description *string `json:"display_description,omitempty"`
- Force bool `json:"force,omitempty"`
- Metadata map[string]interface{} `json:"metadata,omitempty"`
- Name *string `json:"display_name,omitempty"`
- VolumeID *string `json:"volume_id,omitempty"`
- }
-
- type request struct {
- Snapshot snapshot `json:"snapshot"`
- }
-
- reqBody := request{
- Snapshot: snapshot{},
- }
-
- reqBody.Snapshot.Description = gophercloud.MaybeString(opts.Description)
- reqBody.Snapshot.Name = gophercloud.MaybeString(opts.Name)
- reqBody.Snapshot.VolumeID = gophercloud.MaybeString(opts.VolumeID)
-
- reqBody.Snapshot.Force = opts.Force
-
+func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
var res CreateResult
+
+ reqBody, err := opts.ToSnapshotCreateMap()
+ if err != nil {
+ res.Err = err
+ return res
+ }
+
_, res.Err = perigee.Request("POST", createURL(client), perigee.Options{
MoreHeaders: client.Provider.AuthenticatedHeaders(),
OkCodes: []int{200, 201},
- ReqBody: &reqBody,
+ ReqBody: reqBody,
Results: &res.Resp,
})
return res
@@ -63,8 +87,8 @@
return err
}
-// Get retrieves the Snapshot with the provided ID. To extract the Snapshot object
-// from the response, call the Extract method on the GetResult.
+// Get retrieves the Snapshot with the provided ID. To extract the Snapshot
+// 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{
@@ -75,6 +99,12 @@
return res
}
+// ListOptsBuilder allows extensions to add additional parameters to the List
+// request.
+type ListOptsBuilder interface {
+ ToVolumeListParams() (string, error)
+}
+
// ListOpts hold options for listing Snapshots. It is passed to the
// snapshots.List function.
type ListOpts struct {
@@ -83,15 +113,25 @@
VolumeID string `q:"volume_id"`
}
-// List returns Snapshots optionally limited by the conditions provided in ListOpts.
-func List(client *gophercloud.ServiceClient, opts *ListOpts) pagination.Pager {
+// 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 Snapshots optionally limited by the conditions provided in
+// ListOpts.
+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 {
@@ -100,6 +140,12 @@
return pagination.NewPager(client, url, createPage)
}
+// UpdateMetadataOptsBuilder allows extensions to add additional parameters to
+// the Update request.
+type UpdateMetadataOptsBuilder interface {
+ ToSnapshotUpdateMetadataMap() (map[string]interface{}, error)
+}
+
// UpdateMetadataOpts contain options for updating an existing Snapshot. This
// object is passed to the snapshots.Update function. For more information
// about the parameters, see the Snapshot object.
@@ -107,24 +153,34 @@
Metadata map[string]interface{}
}
+// ToSnapshotUpdateMetadataMap assembles a request body based on the contents of
+// an UpdateMetadataOpts.
+func (opts UpdateMetadataOpts) ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) {
+ v := make(map[string]interface{})
+
+ if opts.Metadata != nil {
+ v["metadata"] = opts.Metadata
+ }
+
+ return v, nil
+}
+
// UpdateMetadata will update the Snapshot with provided information. To
// extract the updated Snapshot from the response, call the ExtractMetadata
// method on the UpdateMetadataResult.
-func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts *UpdateMetadataOpts) UpdateMetadataResult {
- type request struct {
- Metadata map[string]interface{} `json:"metadata,omitempty"`
- }
-
- reqBody := request{}
-
- reqBody.Metadata = opts.Metadata
-
+func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) UpdateMetadataResult {
var res UpdateMetadataResult
+ reqBody, err := opts.ToSnapshotUpdateMetadataMap()
+ if err != nil {
+ res.Err = err
+ return res
+ }
+
_, res.Err = perigee.Request("PUT", updateMetadataURL(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/snapshots/requests_test.go b/openstack/blockstorage/v1/snapshots/requests_test.go
index d29cc0d..ddfa81b 100644
--- a/openstack/blockstorage/v1/snapshots/requests_test.go
+++ b/openstack/blockstorage/v1/snapshots/requests_test.go
@@ -119,6 +119,7 @@
th.TestJSONRequest(t, r, `
{
"snapshot": {
+ "volume_id": "1234",
"display_name": "snapshot-001"
}
}
@@ -130,6 +131,7 @@
fmt.Fprintf(w, `
{
"snapshot": {
+ "volume_id": "1234",
"display_name": "snapshot-001",
"id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
}
@@ -137,10 +139,11 @@
`)
})
- options := &CreateOpts{Name: "snapshot-001"}
+ options := &CreateOpts{VolumeID: "1234", Name: "snapshot-001"}
n, err := Create(ServiceClient(), options).Extract()
th.AssertNoErr(t, err)
+ th.AssertEquals(t, n.VolumeID, "1234")
th.AssertEquals(t, n.Name, "snapshot-001")
th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
}
diff --git a/openstack/blockstorage/v1/snapshots/results.go b/openstack/blockstorage/v1/snapshots/results.go
index d23090d..dc94a32 100644
--- a/openstack/blockstorage/v1/snapshots/results.go
+++ b/openstack/blockstorage/v1/snapshots/results.go
@@ -9,19 +9,32 @@
// Snapshot contains all the information associated with an OpenStack Snapshot.
type Snapshot struct {
- Status string `mapstructure:"status"` // currect status of the Snapshot
- Name string `mapstructure:"display_name"` // display name
- Attachments []string `mapstructure:"attachments"` // instances onto which the Snapshot is attached
- AvailabilityZone string `mapstructure:"availability_zone"` // logical group
- Bootable string `mapstructure:"bootable"` // is the Snapshot bootable
- CreatedAt string `mapstructure:"created_at"` // date created
- Description string `mapstructure:"display_discription"` // display description
- VolumeType string `mapstructure:"volume_type"` // see VolumeType object for more information
- SnapshotID string `mapstructure:"snapshot_id"` // ID of the Snapshot from which this Snapshot was created
- SourceVolID string `mapstructure:"source_volid"` // ID of the Volume from which this Snapshot was created
- Metadata map[string]string `mapstructure:"metadata"` // user-defined key-value pairs
- ID string `mapstructure:"id"` // unique identifier
- Size int `mapstructure:"size"` // size of the Snapshot, in GB
+ // Currect status of the Snapshot.
+ Status string `mapstructure:"status"`
+ // Display name.
+ Name string `mapstructure:"display_name"`
+ // Instances onto which the Snapshot is attached.
+ Attachments []string `mapstructure:"attachments"`
+ // Logical group.
+ AvailabilityZone string `mapstructure:"availability_zone"`
+ // Is the Snapshot bootable?
+ Bootable string `mapstructure:"bootable"`
+ // Date created.
+ CreatedAt string `mapstructure:"created_at"`
+ // Display description.
+ Description string `mapstructure:"display_discription"`
+ // See VolumeType object for more information.
+ VolumeType string `mapstructure:"volume_type"`
+ // ID of the Snapshot from which this Snapshot was created.
+ SnapshotID string `mapstructure:"snapshot_id"`
+ // ID of the Volume from which this Snapshot was created.
+ VolumeID string `mapstructure:"volume_id"`
+ // User-defined key-value pairs.
+ Metadata map[string]string `mapstructure:"metadata"`
+ // Unique identifier.
+ ID string `mapstructure:"id"`
+ // Size of the Snapshot, in GB.
+ Size int `mapstructure:"size"`
}
// CreateResult contains the response body and error from a Create request.
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")
}
diff --git a/openstack/blockstorage/v1/volumetypes/requests.go b/openstack/blockstorage/v1/volumetypes/requests.go
index afe650d..ff71de1 100644
--- a/openstack/blockstorage/v1/volumetypes/requests.go
+++ b/openstack/blockstorage/v1/volumetypes/requests.go
@@ -6,6 +6,12 @@
"github.com/rackspace/gophercloud/pagination"
)
+// CreateOptsBuilder allows extensions to add additional parameters to the
+// Create request.
+type CreateOptsBuilder interface {
+ ToVolumeTypeCreateMap() map[string]interface{}
+}
+
// CreateOpts are options for creating a volume type.
type CreateOpts struct {
// OPTIONAL. See VolumeType.
@@ -14,30 +20,28 @@
Name string
}
-// Create will create a new volume, optionally wih CreateOpts. To extract the
-// created volume type object, call the Extract method on the CreateResult.
-func Create(client *gophercloud.ServiceClient, opts *CreateOpts) CreateResult {
- type volumeType struct {
- ExtraSpecs map[string]interface{} `json:"extra_specs,omitempty"`
- Name *string `json:"name,omitempty"`
+// ToVolumeTypeCreateMap casts a CreateOpts struct to a map.
+func (opts CreateOpts) ToVolumeTypeCreateMap() map[string]interface{} {
+ vt := make(map[string]interface{})
+
+ if opts.ExtraSpecs != nil {
+ vt["extra_specs"] = opts.ExtraSpecs
+ }
+ if opts.Name != "" {
+ vt["name"] = opts.Name
}
- type request struct {
- VolumeType volumeType `json:"volume_type"`
- }
+ return map[string]interface{}{"volume_type": vt}
+}
- reqBody := request{
- VolumeType: volumeType{},
- }
-
- reqBody.VolumeType.Name = gophercloud.MaybeString(opts.Name)
- reqBody.VolumeType.ExtraSpecs = opts.ExtraSpecs
-
+// Create will create a new volume. To extract the created volume type object,
+// call the Extract method on the CreateResult.
+func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
var res CreateResult
_, res.Err = perigee.Request("POST", createURL(client), perigee.Options{
MoreHeaders: client.Provider.AuthenticatedHeaders(),
OkCodes: []int{200, 201},
- ReqBody: &reqBody,
+ ReqBody: opts.ToVolumeTypeCreateMap(),
Results: &res.Resp,
})
return res