From f12f8fa5402b9cb3263f4dbe6b856480760698bc Mon Sep 17 00:00:00 2001 From: Brad Ison Date: Thu, 24 Mar 2016 16:44:39 -0400 Subject: [PATCH 1/3] Rackspace Auto Scale: Add groups List() --- rackspace/autoscale/v1/groups/fixtures.go | 236 ++++++++++++++++++ rackspace/autoscale/v1/groups/requests.go | 54 ++++ .../autoscale/v1/groups/requests_test.go | 40 +++ rackspace/autoscale/v1/groups/results.go | 127 ++++++++++ rackspace/autoscale/v1/groups/urls.go | 7 + 5 files changed, 464 insertions(+) create mode 100644 rackspace/autoscale/v1/groups/fixtures.go create mode 100644 rackspace/autoscale/v1/groups/requests.go create mode 100644 rackspace/autoscale/v1/groups/requests_test.go create mode 100644 rackspace/autoscale/v1/groups/results.go create mode 100644 rackspace/autoscale/v1/groups/urls.go diff --git a/rackspace/autoscale/v1/groups/fixtures.go b/rackspace/autoscale/v1/groups/fixtures.go new file mode 100644 index 00000000..c9b199c5 --- /dev/null +++ b/rackspace/autoscale/v1/groups/fixtures.go @@ -0,0 +1,236 @@ +// +build fixtures + +package groups + +import ( + "fmt" + "net/http" + "testing" + + "github.com/rackspace/gophercloud" + th "github.com/rackspace/gophercloud/testhelper" + "github.com/rackspace/gophercloud/testhelper/client" +) + +// GroupListBody contains the canned body of a groups.List response. +const GroupListBody = ` +{ + "groups_links": [], + "groups": [ + { + "state": { + "status": "ACTIVE", + "desiredCapacity": 2, + "paused": false, + "active": [ + { + "id": "449cead0-48b2-44fe-9107-dea7cdb6d925", + "links": [ + { + "href": "https://dfw.servers.api.rackspacecloud.com/v2/123456/servers/449cead0-48b2-44fe-9107-dea7cdb6d925", + "rel": "self" + }, + { + "href": "https://dfw.servers.api.rackspacecloud.com/123456/servers/449cead0-48b2-44fe-9107-dea7cdb6d925", + "rel": "bookmark" + } + ] + }, + { + "id": "d8c2696f-1936-45c7-892d-f5f741ef0f60", + "links": [ + { + "href": "https://dfw.servers.api.rackspacecloud.com/v2/123456/servers/d8c2696f-1936-45c7-892d-f5f741ef0f60", + "rel": "self" + }, + { + "href": "https://dfw.servers.api.rackspacecloud.com/123456/servers/d8c2696f-1936-45c7-892d-f5f741ef0f60", + "rel": "bookmark" + } + ] + } + ], + "pendingCapacity": 0, + "activeCapacity": 2, + "name": "first-group" + }, + "id": "10eb3219-1b12-4b34-b1e4-e10ee4f24c65", + "links": [ + { + "href": "https://dfw.autoscale.api.rackspacecloud.com/v1.0/123456/groups/10eb3219-1b12-4b34-b1e4-e10ee4f24c65/", + "rel": "self" + } + ] + }, + { + "state": { + "status": "ACTIVE", + "desiredCapacity": 3, + "paused": false, + "active": [ + { + "id": "6cca7222-8ab5-4361-ac2c-d35eb0b78ab4", + "links": [ + { + "href": "https://dfw.servers.api.rackspacecloud.com/v2/123456/servers/6cca7222-8ab5-4361-ac2c-d35eb0b78ab4", + "rel": "self" + }, + { + "href": "https://dfw.servers.api.rackspacecloud.com/123456/servers/6cca7222-8ab5-4361-ac2c-d35eb0b78ab4", + "rel": "bookmark" + } + ] + }, + { + "id": "44764e46-9ab2-48ce-8512-f7691e0cd9d2", + "links": [ + { + "href": "https://dfw.servers.api.rackspacecloud.com/v2/123456/servers/44764e46-9ab2-48ce-8512-f7691e0cd9d2", + "rel": "self" + }, + { + "href": "https://dfw.servers.api.rackspacecloud.com/123456/servers/44764e46-9ab2-48ce-8512-f7691e0cd9d2", + "rel": "bookmark" + } + ] + }, + { + "id": "11a31131-9233-4dac-bcab-15ef06f6b939", + "links": [ + { + "href": "https://dfw.servers.api.rackspacecloud.com/v2/123456/servers/11a31131-9233-4dac-bcab-15ef06f6b939", + "rel": "self" + }, + { + "href": "https://dfw.servers.api.rackspacecloud.com/123456/servers/11a31131-9233-4dac-bcab-15ef06f6b939", + "rel": "bookmark" + } + ] + } + ], + "pendingCapacity": 0, + "activeCapacity": 3, + "name": "second-group" + }, + "id": "e21c7d72-2faa-475a-a35c-8c51d9c66e01", + "links": [ + { + "href": "https://dfw.autoscale.api.rackspacecloud.com/v1.0/123456/groups/e21c7d72-2faa-475a-a35c-8c51d9c66e01/", + "rel": "self" + } + ] + }, + { + "state": { + "status": "ACTIVE", + "desiredCapacity": 2, + "paused": false, + "active": [ + { + "id": "f4ff054b-b78c-4123-98f4-7f0e343c64cd", + "links": [ + { + "href": "https://dfw.servers.api.rackspacecloud.com/v2/123456/servers/f4ff054b-b78c-4123-98f4-7f0e343c64cd", + "rel": "self" + }, + { + "href": "https://dfw.servers.api.rackspacecloud.com/123456/servers/f4ff054b-b78c-4123-98f4-7f0e343c64cd", + "rel": "bookmark" + } + ] + }, + { + "id": "c89cfdbf-e3fa-419b-844c-70c3e8016268", + "links": [ + { + "href": "https://dfw.servers.api.rackspacecloud.com/v2/123456/servers/c89cfdbf-e3fa-419b-844c-70c3e8016268", + "rel": "self" + }, + { + "href": "https://dfw.servers.api.rackspacecloud.com/123456/servers/c89cfdbf-e3fa-419b-844c-70c3e8016268", + "rel": "bookmark" + } + ] + } + ], + "pendingCapacity": 0, + "activeCapacity": 2, + "name": "third-group" + }, + "id": "e34fa1e9-d0f4-47c1-9a01-e531204e1f25", + "links": [ + { + "href": "https://dfw.autoscale.api.rackspacecloud.com/v1.0/123456/groups/e34fa1e9-d0f4-47c1-9a01-e531204e1f25/", + "rel": "self" + } + ] + } + ] +} +` + +var ( + // FirstGroup is a Group struct corresponding to the first result in GroupListBody. + FirstGroup = Group{ + ID: "10eb3219-1b12-4b34-b1e4-e10ee4f24c65", + State: State{ + Name: "first-group", + Status: ACTIVE, + DesiredCapacity: 2, + PendingCapacity: 0, + ActiveCapacity: 2, + Paused: false, + Errors: nil, + Active: []ActiveServer{ + ActiveServer{ + ID: "449cead0-48b2-44fe-9107-dea7cdb6d925", + Links: []gophercloud.Link{ + gophercloud.Link{ + Href: "https://dfw.servers.api.rackspacecloud.com/v2/123456/servers/449cead0-48b2-44fe-9107-dea7cdb6d925", + Rel: "self", + }, + gophercloud.Link{ + Href: "https://dfw.servers.api.rackspacecloud.com/123456/servers/449cead0-48b2-44fe-9107-dea7cdb6d925", + Rel: "bookmark", + }, + }, + }, + ActiveServer{ + ID: "d8c2696f-1936-45c7-892d-f5f741ef0f60", + Links: []gophercloud.Link{ + gophercloud.Link{ + Href: "https://dfw.servers.api.rackspacecloud.com/v2/123456/servers/d8c2696f-1936-45c7-892d-f5f741ef0f60", + Rel: "self", + }, + gophercloud.Link{ + Href: "https://dfw.servers.api.rackspacecloud.com/123456/servers/d8c2696f-1936-45c7-892d-f5f741ef0f60", + Rel: "bookmark", + }, + }, + }, + }, + }, + } +) + +// HandleGroupListSuccessfully sets up the test server to respond to a group List request. +func HandleGroupListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/groups", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + + r.ParseForm() + marker := r.Form.Get("marker") + + switch marker { + case "": + fmt.Fprintf(w, GroupListBody) + case "e34fa1e9-d0f4-47c1-9a01-e531204e1f25": + fmt.Fprintf(w, `{ "servers": [] }`) + default: + t.Fatalf("/groups invoked with unexpected marker=[%s]", marker) + } + }) +} diff --git a/rackspace/autoscale/v1/groups/requests.go b/rackspace/autoscale/v1/groups/requests.go new file mode 100644 index 00000000..a40045d0 --- /dev/null +++ b/rackspace/autoscale/v1/groups/requests.go @@ -0,0 +1,54 @@ +package groups + +import ( + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToGroupListQuery() (string, error) +} + +// ListOpts allows the filtering and sorting of paginated collections through +// the API. +type ListOpts struct { + // UUID of the group at which you want to set a marker. + Marker string `q:"marker"` + + // Integer value for the limit of values to return. + Limit int `q:"limit"` +} + +// ToGroupListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToGroupListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + + if err != nil { + return "", err + } + + return q.String(), nil +} + +// List returns all scaling groups. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + + if opts != nil { + query, err := opts.ToGroupListQuery() + + if err != nil { + return pagination.Pager{Err: err} + } + + url += query + } + + createPageFn := func(r pagination.PageResult) pagination.Page { + return GroupPage{pagination.LinkedPageBase{PageResult: r}} + } + + return pagination.NewPager(client, url, createPageFn) +} diff --git a/rackspace/autoscale/v1/groups/requests_test.go b/rackspace/autoscale/v1/groups/requests_test.go new file mode 100644 index 00000000..f6ecdb42 --- /dev/null +++ b/rackspace/autoscale/v1/groups/requests_test.go @@ -0,0 +1,40 @@ +package groups + +import ( + "testing" + + "github.com/rackspace/gophercloud/pagination" + th "github.com/rackspace/gophercloud/testhelper" + "github.com/rackspace/gophercloud/testhelper/client" +) + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGroupListSuccessfully(t) + + pages := 0 + err := List(client.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + groups, err := ExtractGroups(page) + + if err != nil { + return false, err + } + + if len(groups) != 3 { + t.Fatalf("Expected 3 groups, got %d", len(groups)) + } + + th.CheckDeepEquals(t, FirstGroup, groups[0]) + + return true, nil + }) + + th.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} diff --git a/rackspace/autoscale/v1/groups/results.go b/rackspace/autoscale/v1/groups/results.go new file mode 100644 index 00000000..fdc56355 --- /dev/null +++ b/rackspace/autoscale/v1/groups/results.go @@ -0,0 +1,127 @@ +package groups + +import ( + "github.com/mitchellh/mapstructure" + + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/pagination" +) + +type groupResult struct { + gophercloud.Result +} + +// Group represents an Auto Scale group. +type Group struct { + // UUID for the group + ID string `mapstructure:"id" json:"id"` + + // State information for the group + State State `mapstructure:"state" json:"state"` +} + +// State represents the state information belonging to an Auto Scale group. +type State struct { + // The name of the scaling group. + Name string `mapstructure:"name" json:"name"` + + // Status of the scaling group. + Status Status `mapstructure:"status" json:"status"` + + // Number of servers desired. + DesiredCapacity int `mapstructure:"desiredCapacity" json:"desiredCapacity"` + + // Number of servers in a "BUILDING" state. + PendingCapacity int `mapstructure:"pendingCapacity" json:"pendingCapacity"` + + // Number of active servers. + ActiveCapacity int `mapstructure:"activeCapacity" json:"activeCapacity"` + + // Whether a group is paused. All scaling operations are suspended while a + // group is pasued. + Paused bool `mapstructure:"paused" json:"paused"` + + // List of active servers. Includes server ID and links. + Active []ActiveServer `mapstructure:"active" json:"active"` + + // List of errors with human readable messages when a group is in the + // "ERROR" state. + Errors []Error `mapstructure:"errors" json:"errors"` +} + +// ActiveServer represents an active member server of a scaling group. +type ActiveServer struct { + // The UUID of the server. + ID string `mapstructure:"id" json:"id"` + + // Links associated with the server. + Links []gophercloud.Link `mapstructure:"links" json:"links"` +} + +// Error represents a human readable error for groups in an ERROR state. +type Error struct { + Message string `mapstructure:"message" json:"message"` +} + +func (e *Error) Error() string { return e.Message } + +// Status indicates the status of an Auto Scale group. +type Status string + +// Possible group states. +const ( + ACTIVE Status = "ACTIVE" + ERROR Status = "ERROR" + DELETING Status = "DELETING" +) + +// GroupPage is the page returned by a pager when traversing over a collection +// of Auto Scale groups. +type GroupPage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if a page contains no Group results. +func (page GroupPage) IsEmpty() (bool, error) { + groups, err := ExtractGroups(page) + + if err != nil { + return true, err + } + + return len(groups) == 0, nil +} + +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. +func (page GroupPage) NextPageURL() (string, error) { + var response struct { + Links []gophercloud.Link `mapstructure:"groups_links"` + } + + err := mapstructure.Decode(page.Body, &response) + + if err != nil { + return "", err + } + + return gophercloud.ExtractNextURL(response.Links) +} + +// ExtractGroups interprets the results of a single page from a List() call, +// producing a slice of Groups. +func ExtractGroups(page pagination.Page) ([]Group, error) { + casted := page.(GroupPage).Body + + var response struct { + Groups []Group `mapstructure:"groups"` + } + + err := mapstructure.Decode(casted, &response) + + if err != nil { + return nil, err + } + + return response.Groups, err +} diff --git a/rackspace/autoscale/v1/groups/urls.go b/rackspace/autoscale/v1/groups/urls.go new file mode 100644 index 00000000..c4e7283b --- /dev/null +++ b/rackspace/autoscale/v1/groups/urls.go @@ -0,0 +1,7 @@ +package groups + +import "github.com/rackspace/gophercloud" + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("groups") +} From a14c8f389e919be55389e89e9a8616c65baab60a Mon Sep 17 00:00:00 2001 From: Brad Ison Date: Thu, 5 May 2016 15:27:23 -0500 Subject: [PATCH 2/3] Rackspace Auto Scale: Add groups GetState() --- rackspace/autoscale/v1/groups/fixtures.go | 129 +++++++++++++----- rackspace/autoscale/v1/groups/requests.go | 9 ++ .../autoscale/v1/groups/requests_test.go | 17 +++ rackspace/autoscale/v1/groups/results.go | 27 ++++ rackspace/autoscale/v1/groups/urls.go | 4 + 5 files changed, 152 insertions(+), 34 deletions(-) diff --git a/rackspace/autoscale/v1/groups/fixtures.go b/rackspace/autoscale/v1/groups/fixtures.go index c9b199c5..149190fe 100644 --- a/rackspace/autoscale/v1/groups/fixtures.go +++ b/rackspace/autoscale/v1/groups/fixtures.go @@ -169,48 +169,95 @@ const GroupListBody = ` } ` +// FirstGroupStateBody contains the canned body of a groups.GetState response. +// The response corresponds to the state of first result in GroupListBody. +const FirstGroupStateBody = ` +{ + "group": { + "status": "ACTIVE", + "desiredCapacity": 2, + "paused": false, + "active": [ + { + "id": "449cead0-48b2-44fe-9107-dea7cdb6d925", + "links": [ + { + "href": "https://dfw.servers.api.rackspacecloud.com/v2/123456/servers/449cead0-48b2-44fe-9107-dea7cdb6d925", + "rel": "self" + }, + { + "href": "https://dfw.servers.api.rackspacecloud.com/123456/servers/449cead0-48b2-44fe-9107-dea7cdb6d925", + "rel": "bookmark" + } + ] + }, + { + "id": "d8c2696f-1936-45c7-892d-f5f741ef0f60", + "links": [ + { + "href": "https://dfw.servers.api.rackspacecloud.com/v2/123456/servers/d8c2696f-1936-45c7-892d-f5f741ef0f60", + "rel": "self" + }, + { + "href": "https://dfw.servers.api.rackspacecloud.com/123456/servers/d8c2696f-1936-45c7-892d-f5f741ef0f60", + "rel": "bookmark" + } + ] + } + ], + "pendingCapacity": 0, + "activeCapacity": 2, + "name": "first-group" + } +} +` + var ( - // FirstGroup is a Group struct corresponding to the first result in GroupListBody. - FirstGroup = Group{ - ID: "10eb3219-1b12-4b34-b1e4-e10ee4f24c65", - State: State{ - Name: "first-group", - Status: ACTIVE, - DesiredCapacity: 2, - PendingCapacity: 0, - ActiveCapacity: 2, - Paused: false, - Errors: nil, - Active: []ActiveServer{ - ActiveServer{ - ID: "449cead0-48b2-44fe-9107-dea7cdb6d925", - Links: []gophercloud.Link{ - gophercloud.Link{ - Href: "https://dfw.servers.api.rackspacecloud.com/v2/123456/servers/449cead0-48b2-44fe-9107-dea7cdb6d925", - Rel: "self", - }, - gophercloud.Link{ - Href: "https://dfw.servers.api.rackspacecloud.com/123456/servers/449cead0-48b2-44fe-9107-dea7cdb6d925", - Rel: "bookmark", - }, + // FirstGroupState is a State struct corresponding to the state of + // the first result in GroupListBody. + FirstGroupState = State{ + Name: "first-group", + Status: ACTIVE, + DesiredCapacity: 2, + PendingCapacity: 0, + ActiveCapacity: 2, + Paused: false, + Errors: nil, + Active: []ActiveServer{ + ActiveServer{ + ID: "449cead0-48b2-44fe-9107-dea7cdb6d925", + Links: []gophercloud.Link{ + gophercloud.Link{ + Href: "https://dfw.servers.api.rackspacecloud.com/v2/123456/servers/449cead0-48b2-44fe-9107-dea7cdb6d925", + Rel: "self", + }, + gophercloud.Link{ + Href: "https://dfw.servers.api.rackspacecloud.com/123456/servers/449cead0-48b2-44fe-9107-dea7cdb6d925", + Rel: "bookmark", }, }, - ActiveServer{ - ID: "d8c2696f-1936-45c7-892d-f5f741ef0f60", - Links: []gophercloud.Link{ - gophercloud.Link{ - Href: "https://dfw.servers.api.rackspacecloud.com/v2/123456/servers/d8c2696f-1936-45c7-892d-f5f741ef0f60", - Rel: "self", - }, - gophercloud.Link{ - Href: "https://dfw.servers.api.rackspacecloud.com/123456/servers/d8c2696f-1936-45c7-892d-f5f741ef0f60", - Rel: "bookmark", - }, + }, + ActiveServer{ + ID: "d8c2696f-1936-45c7-892d-f5f741ef0f60", + Links: []gophercloud.Link{ + gophercloud.Link{ + Href: "https://dfw.servers.api.rackspacecloud.com/v2/123456/servers/d8c2696f-1936-45c7-892d-f5f741ef0f60", + Rel: "self", + }, + gophercloud.Link{ + Href: "https://dfw.servers.api.rackspacecloud.com/123456/servers/d8c2696f-1936-45c7-892d-f5f741ef0f60", + Rel: "bookmark", }, }, }, }, } + + // FirstGroup is a Group struct corresponding to the first result in GroupListBody. + FirstGroup = Group{ + ID: "10eb3219-1b12-4b34-b1e4-e10ee4f24c65", + State: FirstGroupState, + } ) // HandleGroupListSuccessfully sets up the test server to respond to a group List request. @@ -234,3 +281,17 @@ func HandleGroupListSuccessfully(t *testing.T) { } }) } + +// HandleGroupGetStateSuccessfully sets up the test server to respond to a group GetState request. +func HandleGroupGetStateSuccessfully(t *testing.T) { + path := "/groups/10eb3219-1b12-4b34-b1e4-e10ee4f24c65/state" + + th.Mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + + fmt.Fprintf(w, FirstGroupStateBody) + }) +} diff --git a/rackspace/autoscale/v1/groups/requests.go b/rackspace/autoscale/v1/groups/requests.go index a40045d0..8ec21edf 100644 --- a/rackspace/autoscale/v1/groups/requests.go +++ b/rackspace/autoscale/v1/groups/requests.go @@ -52,3 +52,12 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa return pagination.NewPager(client, url, createPageFn) } + +// GetState requests the details of a given auto scale group's current state. +func GetState(client *gophercloud.ServiceClient, groupID string) StateResult { + var result StateResult + + _, result.Err = client.Get(stateURL(client, groupID), &result.Body, nil) + + return result +} diff --git a/rackspace/autoscale/v1/groups/requests_test.go b/rackspace/autoscale/v1/groups/requests_test.go index f6ecdb42..4a43494f 100644 --- a/rackspace/autoscale/v1/groups/requests_test.go +++ b/rackspace/autoscale/v1/groups/requests_test.go @@ -38,3 +38,20 @@ func TestList(t *testing.T) { t.Errorf("Expected 1 page, saw %d", pages) } } + +func TestGetState(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGroupGetStateSuccessfully(t) + + client := client.ServiceClient() + groupID := "10eb3219-1b12-4b34-b1e4-e10ee4f24c65" + + state, err := GetState(client, groupID).Extract() + + if err != nil { + t.Fatalf("Unexpected GetState error: %v", err) + } + + th.CheckDeepEquals(t, FirstGroupState, *state) +} diff --git a/rackspace/autoscale/v1/groups/results.go b/rackspace/autoscale/v1/groups/results.go index fdc56355..33e72710 100644 --- a/rackspace/autoscale/v1/groups/results.go +++ b/rackspace/autoscale/v1/groups/results.go @@ -11,6 +11,11 @@ type groupResult struct { gophercloud.Result } +// StateResult represents the result of a GetState operation. +type StateResult struct { + groupResult +} + // Group represents an Auto Scale group. type Group struct { // UUID for the group @@ -125,3 +130,25 @@ func ExtractGroups(page pagination.Page) ([]Group, error) { return response.Groups, err } + +// Extract attempts to interpret any StateResult as a State struct. +func (res StateResult) Extract() (*State, error) { + if res.Err != nil { + return nil, res.Err + } + + // When listing groups or requesting group details, the state is an object + // under the "state" key. For some reason, it's under "group" when + // explicitly requesting state information. + var response struct { + State State `mapstructure:"group"` + } + + err := mapstructure.Decode(res.Body, &response) + + if err != nil { + return nil, err + } + + return &response.State, nil +} diff --git a/rackspace/autoscale/v1/groups/urls.go b/rackspace/autoscale/v1/groups/urls.go index c4e7283b..035143e2 100644 --- a/rackspace/autoscale/v1/groups/urls.go +++ b/rackspace/autoscale/v1/groups/urls.go @@ -5,3 +5,7 @@ import "github.com/rackspace/gophercloud" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("groups") } + +func stateURL(c *gophercloud.ServiceClient, groupID string) string { + return c.ServiceURL("groups", groupID, "state") +} From 86ba0bdd7cef26ea8b2676018cfd7bd9df02ca4f Mon Sep 17 00:00:00 2001 From: Brad Ison Date: Thu, 5 May 2016 16:34:35 -0500 Subject: [PATCH 3/3] Rackspace Auto Scale: Add groups GetConfig() --- rackspace/autoscale/v1/groups/fixtures.go | 41 +++++++++++++++++ rackspace/autoscale/v1/groups/requests.go | 9 ++++ .../autoscale/v1/groups/requests_test.go | 17 +++++++ rackspace/autoscale/v1/groups/results.go | 44 ++++++++++++++++++- rackspace/autoscale/v1/groups/urls.go | 4 ++ 5 files changed, 114 insertions(+), 1 deletion(-) diff --git a/rackspace/autoscale/v1/groups/fixtures.go b/rackspace/autoscale/v1/groups/fixtures.go index 149190fe..109cc159 100644 --- a/rackspace/autoscale/v1/groups/fixtures.go +++ b/rackspace/autoscale/v1/groups/fixtures.go @@ -212,7 +212,34 @@ const FirstGroupStateBody = ` } ` +// GroupConfigurationBody contains the canned body of a groups.GetConfig response. +const GroupConfigurationBody = ` +{ + "groupConfiguration": { + "maxEntities": 3, + "name": "test-group", + "cooldown": 60, + "minEntities": 3, + "metadata": { + "foo": "bar" + } + } +} +` + var ( + // GroupConfiguration is a Configuration struct corresponding to the result + // in GroupConfigurationBody. + GroupConfiguration = Configuration{ + Name: "test-group", + Cooldown: 60, + MinEntities: 3, + MaxEntities: 3, + Metadata: map[string]string{ + "foo": "bar", + }, + } + // FirstGroupState is a State struct corresponding to the state of // the first result in GroupListBody. FirstGroupState = State{ @@ -295,3 +322,17 @@ func HandleGroupGetStateSuccessfully(t *testing.T) { fmt.Fprintf(w, FirstGroupStateBody) }) } + +// HandleGroupGetConfigSuccessfully sets up the test server to respond to a group GetConfig request. +func HandleGroupGetConfigSuccessfully(t *testing.T) { + path := "/groups/10eb3219-1b12-4b34-b1e4-e10ee4f24c65/config" + + th.Mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + + fmt.Fprintf(w, GroupConfigurationBody) + }) +} diff --git a/rackspace/autoscale/v1/groups/requests.go b/rackspace/autoscale/v1/groups/requests.go index 8ec21edf..8a8de3f8 100644 --- a/rackspace/autoscale/v1/groups/requests.go +++ b/rackspace/autoscale/v1/groups/requests.go @@ -61,3 +61,12 @@ func GetState(client *gophercloud.ServiceClient, groupID string) StateResult { return result } + +// GetConfig requests the details of a given auto scale group's basic configuration. +func GetConfig(client *gophercloud.ServiceClient, groupID string) GetConfigResult { + var result GetConfigResult + + _, result.Err = client.Get(configURL(client, groupID), &result.Body, nil) + + return result +} diff --git a/rackspace/autoscale/v1/groups/requests_test.go b/rackspace/autoscale/v1/groups/requests_test.go index 4a43494f..751a3cf7 100644 --- a/rackspace/autoscale/v1/groups/requests_test.go +++ b/rackspace/autoscale/v1/groups/requests_test.go @@ -55,3 +55,20 @@ func TestGetState(t *testing.T) { th.CheckDeepEquals(t, FirstGroupState, *state) } + +func TestGetConfig(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGroupGetConfigSuccessfully(t) + + client := client.ServiceClient() + groupID := "10eb3219-1b12-4b34-b1e4-e10ee4f24c65" + + state, err := GetConfig(client, groupID).Extract() + + if err != nil { + t.Fatalf("Unexpected GetState error: %v", err) + } + + th.CheckDeepEquals(t, GroupConfiguration, *state) +} diff --git a/rackspace/autoscale/v1/groups/results.go b/rackspace/autoscale/v1/groups/results.go index 33e72710..8f97791d 100644 --- a/rackspace/autoscale/v1/groups/results.go +++ b/rackspace/autoscale/v1/groups/results.go @@ -13,7 +13,12 @@ type groupResult struct { // StateResult represents the result of a GetState operation. type StateResult struct { - groupResult + gophercloud.Result +} + +// GetConfigResult represents the result of a GetConfig operation. +type GetConfigResult struct { + gophercloud.Result } // Group represents an Auto Scale group. @@ -80,6 +85,24 @@ const ( DELETING Status = "DELETING" ) +// Configuration represents the basic configuration of a scaling group. +type Configuration struct { + // The name of the scaling group. + Name string `mapstructure:"name" json:"name"` + + // The cooldown period, in seconds, before any additional changes can happen. + Cooldown int `mapstructure:"cooldown" json:"cooldown"` + + // The minimum number of entities in the scaling group. + MinEntities int `mapstructure:"minEntities" json:"minEntities"` + + // The maximum number of entities that are allowed in the scaling group. + MaxEntities int `mapstructure:"maxEntities" json:"maxEntities"` + + // Additional metadata for the group configuration. + Metadata map[string]string `mapstructure:"metadata" json:"metadata"` +} + // GroupPage is the page returned by a pager when traversing over a collection // of Auto Scale groups. type GroupPage struct { @@ -152,3 +175,22 @@ func (res StateResult) Extract() (*State, error) { return &response.State, nil } + +// Extract attempts to interpret any GetConfigResult as a Configuration struct. +func (res GetConfigResult) Extract() (*Configuration, error) { + if res.Err != nil { + return nil, res.Err + } + + var response struct { + Configuration Configuration `mapstructure:"groupConfiguration"` + } + + err := mapstructure.Decode(res.Body, &response) + + if err != nil { + return nil, err + } + + return &response.Configuration, nil +} diff --git a/rackspace/autoscale/v1/groups/urls.go b/rackspace/autoscale/v1/groups/urls.go index 035143e2..79c27503 100644 --- a/rackspace/autoscale/v1/groups/urls.go +++ b/rackspace/autoscale/v1/groups/urls.go @@ -9,3 +9,7 @@ func listURL(c *gophercloud.ServiceClient) string { func stateURL(c *gophercloud.ServiceClient, groupID string) string { return c.ServiceURL("groups", groupID, "state") } + +func configURL(c *gophercloud.ServiceClient, groupID string) string { + return c.ServiceURL("groups", groupID, "config") +}