Use ServiceClient and pagination in Flavor operations.
diff --git a/openstack/compute/v2/flavors/client.go b/openstack/compute/v2/flavors/client.go
deleted file mode 100644
index edeec66..0000000
--- a/openstack/compute/v2/flavors/client.go
+++ /dev/null
@@ -1,63 +0,0 @@
-package flavors
-
-import (
- "fmt"
- "net/url"
- "strconv"
-
- "github.com/rackspace/gophercloud"
- identity "github.com/rackspace/gophercloud/openstack/identity/v2"
-)
-
-type Client struct {
- endpoint string
- authority identity.AuthResults
- options gophercloud.AuthOptions
-}
-
-func NewClient(e string, a identity.AuthResults, ao gophercloud.AuthOptions) *Client {
- return &Client{
- endpoint: e,
- authority: a,
- options: ao,
- }
-}
-
-func (c *Client) getListUrl(lfo ListFilterOptions) string {
- v := url.Values{}
- if lfo.ChangesSince != "" {
- v.Set("changes-since", lfo.ChangesSince)
- }
- if lfo.MinDisk != 0 {
- v.Set("minDisk", strconv.Itoa(lfo.MinDisk))
- }
- if lfo.MinRam != 0 {
- v.Set("minRam", strconv.Itoa(lfo.MinRam))
- }
- if lfo.Marker != "" {
- v.Set("marker", lfo.Marker)
- }
- if lfo.Limit != 0 {
- v.Set("limit", strconv.Itoa(lfo.Limit))
- }
- tail := ""
- if len(v) > 0 {
- tail = fmt.Sprintf("?%s", v.Encode())
- }
- return fmt.Sprintf("%s/flavors/detail%s", c.endpoint, tail)
-}
-
-func (c *Client) getGetUrl(id string) string {
- return fmt.Sprintf("%s/flavors/%s", c.endpoint, id)
-}
-
-func (c *Client) getListHeaders() (map[string]string, error) {
- t, err := identity.GetToken(c.authority)
- if err != nil {
- return map[string]string{}, err
- }
-
- return map[string]string{
- "X-Auth-Token": t.ID,
- }, nil
-}
diff --git a/openstack/compute/v2/flavors/flavors.go b/openstack/compute/v2/flavors/flavors.go
index 146bcc4..4318eb6 100644
--- a/openstack/compute/v2/flavors/flavors.go
+++ b/openstack/compute/v2/flavors/flavors.go
@@ -1,31 +1,37 @@
package flavors
import (
- "github.com/mitchellh/mapstructure"
+ "errors"
"reflect"
+
+ "github.com/mitchellh/mapstructure"
+ "github.com/rackspace/gophercloud/pagination"
)
+// ErrCannotInterpret is returned by an Extract call if the response body doesn't have the expected structure.
+var ErrCannotInterpet = errors.New("Unable to interpret a response body.")
+
// Flavor records represent (virtual) hardware configurations for server resources in a region.
-//
-// The Id field contains the flavor's unique identifier.
-// For example, this identifier will be useful when specifying which hardware configuration to use for a new server instance.
-//
-// The Disk and Ram fields provide a measure of storage space offered by the flavor, in GB and MB, respectively.
-//
-// The Name field provides a human-readable moniker for the flavor.
-//
-// Swap indicates how much space is reserved for swap.
-// If not provided, this field will be set to 0.
-//
-// VCpus indicates how many (virtual) CPUs are available for this flavor.
type Flavor struct {
- Disk int
- Id string
- Name string
- Ram int
+ // The Id field contains the flavor's unique identifier.
+ // For example, this identifier will be useful when specifying which hardware configuration to use for a new server instance.
+ ID string `mapstructure:"id"`
+
+ // The Disk and RA< fields provide a measure of storage space offered by the flavor, in GB and MB, respectively.
+ Disk int `mapstructure:"disk"`
+ RAM int `mapstructure:"ram"`
+
+ // The Name field provides a human-readable moniker for the flavor.
+ Name string `mapstructure:"name"`
+
RxTxFactor float64 `mapstructure:"rxtx_factor"`
- Swap int
- VCpus int
+
+ // Swap indicates how much space is reserved for swap.
+ // If not provided, this field will be set to 0.
+ Swap int `mapstructure:"swap"`
+
+ // VCPUs indicates how many (virtual) CPUs are available for this flavor.
+ VCPUs int `mapstructure:"vcpus"`
}
func defaulter(from, to reflect.Kind, v interface{}) (interface{}, error) {
@@ -35,44 +41,38 @@
return v, nil
}
-// GetFlavors provides access to the list of flavors returned by the List function.
-func GetFlavors(lr ListResults) ([]Flavor, error) {
- fa, ok := lr["flavors"]
- if !ok {
- return nil, ErrNotImplemented
- }
- fms := fa.([]interface{})
+// ExtractFlavors provides access to the list of flavors in a page acquired from the List operation.
+func ExtractFlavors(page pagination.Page) ([]Flavor, error) {
+ casted := page.(ListResult).Body
+ var flavors []Flavor
- flavors := make([]Flavor, len(fms))
- for i, fm := range fms {
- flavorObj := fm.(map[string]interface{})
- cfg := &mapstructure.DecoderConfig{
- DecodeHook: defaulter,
- Result: &flavors[i],
- }
- decoder, err := mapstructure.NewDecoder(cfg)
- if err != nil {
- return flavors, err
- }
- err = decoder.Decode(flavorObj)
- if err != nil {
- return flavors, err
- }
+ cfg := &mapstructure.DecoderConfig{
+ DecodeHook: defaulter,
+ Result: &flavors,
}
+ decoder, err := mapstructure.NewDecoder(cfg)
+ if err != nil {
+ return flavors, err
+ }
+ err = decoder.Decode(casted)
+ if err != nil {
+ return flavors, err
+ }
+
return flavors, nil
}
-// GetFlavor provides access to the individual flavor returned by the Get function.
-func GetFlavor(gr GetResults) (*Flavor, error) {
+// ExtractFlavor provides access to the individual flavor returned by the Get function.
+func ExtractFlavor(gr GetResults) (*Flavor, error) {
f, ok := gr["flavor"]
if !ok {
- return nil, ErrNotImplemented
+ return nil, ErrCannotInterpet
}
flav := new(Flavor)
cfg := &mapstructure.DecoderConfig{
DecodeHook: defaulter,
- Result: flav,
+ Result: flav,
}
decoder, err := mapstructure.NewDecoder(cfg)
if err != nil {
diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go
index 3758206..7563d04 100644
--- a/openstack/compute/v2/flavors/requests.go
+++ b/openstack/compute/v2/flavors/requests.go
@@ -1,58 +1,79 @@
package flavors
import (
- "fmt"
"github.com/racker/perigee"
+ "github.com/rackspace/gophercloud"
+ "github.com/rackspace/gophercloud/pagination"
)
-var ErrNotImplemented = fmt.Errorf("Flavors functionality not implemented.")
+// ListResult contains a single page of the response from a List call.
+type ListResult struct {
+ pagination.MarkerPageBase
+}
-type ListResults map[string]interface{}
+// IsEmpty determines if a page contains any results.
+func (p ListResult) IsEmpty() (bool, error) {
+ flavors, err := ExtractFlavors(p)
+ if err != nil {
+ return true, err
+ }
+ return len(flavors) == 0, nil
+}
+
+// LastMarker returns the ID field of the final result from this page, to be used as the marker for the next.
+func (p ListResult) LastMarker() (string, error) {
+ flavors, err := ExtractFlavors(p)
+ if err != nil {
+ return "", err
+ }
+ if len(flavors) == 0 {
+ return "", nil
+ }
+ return flavors[len(flavors)-1].ID, nil
+}
+
+// GetResults temporarily encodes the result of a Get operation.
type GetResults map[string]interface{}
// ListFilterOptions helps control the results returned by the List() function.
-// ChangesSince, if provided, instructs List to return only those things which have changed since the timestamp provided.
-// MinDisk and MinRam, if provided, elides flavors which do not meet your criteria.
// For example, a flavor with a minDisk field of 10 will not be returned if you specify MinDisk set to 20.
-// Marker and Limit control paging.
-// Limit instructs List to refrain from sending excessively large lists of flavors.
-// Marker instructs List where to start listing from.
// Typically, software will use the last ID of the previous call to List to set the Marker for the current call.
type ListFilterOptions struct {
+
+ // ChangesSince, if provided, instructs List to return only those things which have changed since the timestamp provided.
ChangesSince string
- MinDisk, MinRam int
+
+ // MinDisk and MinRAM, if provided, elides flavors which do not meet your criteria.
+ MinDisk, MinRAM int
+
+ // Marker and Limit control paging.
+ // Marker instructs List where to start listing from.
Marker string
+
+ // Limit instructs List to refrain from sending excessively large lists of flavors.
Limit int
}
// List instructs OpenStack to provide a list of flavors.
// You may provide criteria by which List curtails its results for easier processing.
// See ListFilterOptions for more details.
-func List(c *Client, lfo ListFilterOptions) (ListResults, error) {
- var lr ListResults
-
- h, err := c.getListHeaders()
- if err != nil {
- return nil, err
+func List(client *gophercloud.ServiceClient, lfo ListFilterOptions) pagination.Pager {
+ createPage := func(r pagination.LastHTTPResponse) pagination.Page {
+ p := ListResult{pagination.MarkerPageBase{LastHTTPResponse: r}}
+ p.MarkerPageBase.Owner = p
+ return p
}
- err = perigee.Get(c.getListUrl(lfo), perigee.Options{
- Results: &lr,
- MoreHeaders: h,
- })
- return lr, err
+ return pagination.NewPager(client, getListURL(client, lfo), createPage)
}
// Get instructs OpenStack to provide details on a single flavor, identified by its ID.
-func Get(c *Client, id string) (GetResults, error) {
+// Use ExtractFlavor to convert its result into a Flavor.
+func Get(client *gophercloud.ServiceClient, id string) (GetResults, error) {
var gr GetResults
- h, err := c.getListHeaders() // same for Get Flavor API
- if err != nil {
- return gr, err
- }
- err = perigee.Get(c.getGetUrl(id), perigee.Options{
- Results: &gr,
- MoreHeaders: h,
+ err := perigee.Get(getFlavorURL(client, id), perigee.Options{
+ Results: &gr,
+ MoreHeaders: client.Provider.AuthenticatedHeaders(),
})
return gr, err
}
diff --git a/openstack/compute/v2/flavors/urls.go b/openstack/compute/v2/flavors/urls.go
new file mode 100644
index 0000000..0e2d7c2
--- /dev/null
+++ b/openstack/compute/v2/flavors/urls.go
@@ -0,0 +1,37 @@
+package flavors
+
+import (
+ "fmt"
+ "net/url"
+ "strconv"
+
+ "github.com/rackspace/gophercloud"
+)
+
+func getListURL(client *gophercloud.ServiceClient, lfo ListFilterOptions) string {
+ v := url.Values{}
+ if lfo.ChangesSince != "" {
+ v.Set("changes-since", lfo.ChangesSince)
+ }
+ if lfo.MinDisk != 0 {
+ v.Set("minDisk", strconv.Itoa(lfo.MinDisk))
+ }
+ if lfo.MinRAM != 0 {
+ v.Set("minRam", strconv.Itoa(lfo.MinRAM))
+ }
+ if lfo.Marker != "" {
+ v.Set("marker", lfo.Marker)
+ }
+ if lfo.Limit != 0 {
+ v.Set("limit", strconv.Itoa(lfo.Limit))
+ }
+ tail := ""
+ if len(v) > 0 {
+ tail = fmt.Sprintf("?%s", v.Encode())
+ }
+ return client.ServiceURL("flavors", "detail") + tail
+}
+
+func getFlavorURL(client *gophercloud.ServiceClient, id string) string {
+ return client.ServiceURL("flavors", id)
+}