Use result structs for server requests.
diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go
index da93ff1..3b23be5 100644
--- a/openstack/compute/v2/servers/requests.go
+++ b/openstack/compute/v2/servers/requests.go
@@ -36,11 +36,6 @@
return servers[len(servers)-1].ID, nil
}
-// ServerResult abstracts a single server description, as returned by the OpenStack provider.
-// As OpenStack extensions may freely alter the response bodies of the structures returned to the client,
-// you may only safely access the data provided through separate, type-safe accessors or methods.
-type ServerResult map[string]interface{}
-
// List makes a request against the API to list servers accessible to you.
func List(client *gophercloud.ServiceClient) pagination.Pager {
createPage := func(r pagination.LastHTTPResponse) pagination.Page {
@@ -53,17 +48,17 @@
}
// Create requests a server to be provisioned to the user in the current tenant.
-func Create(client *gophercloud.ServiceClient, opts map[string]interface{}) (ServerResult, error) {
- var sr ServerResult
- _, err := perigee.Request("POST", getListURL(client), perigee.Options{
- Results: &sr,
+func Create(client *gophercloud.ServiceClient, opts map[string]interface{}) CreateResult {
+ var result CreateResult
+ _, result.Err = perigee.Request("POST", getListURL(client), perigee.Options{
+ Results: &result.Resp,
ReqBody: map[string]interface{}{
"server": opts,
},
MoreHeaders: client.Provider.AuthenticatedHeaders(),
OkCodes: []int{202},
})
- return sr, err
+ return result
}
// Delete requests that a server previously provisioned be removed from your account.
@@ -76,26 +71,26 @@
}
// Get requests details on a single server, by ID.
-func Get(client *gophercloud.ServiceClient, id string) (ServerResult, error) {
- var sr ServerResult
- _, err := perigee.Request("GET", getServerURL(client, id), perigee.Options{
- Results: &sr,
+func Get(client *gophercloud.ServiceClient, id string) GetResult {
+ var result GetResult
+ _, result.Err = perigee.Request("GET", getServerURL(client, id), perigee.Options{
+ Results: &result.Resp,
MoreHeaders: client.Provider.AuthenticatedHeaders(),
})
- return sr, err
+ return result
}
// Update requests that various attributes of the indicated server be changed.
-func Update(client *gophercloud.ServiceClient, id string, opts map[string]interface{}) (ServerResult, error) {
- var sr ServerResult
- _, err := perigee.Request("PUT", getServerURL(client, id), perigee.Options{
- Results: &sr,
+func Update(client *gophercloud.ServiceClient, id string, opts map[string]interface{}) UpdateResult {
+ var result UpdateResult
+ _, result.Err = perigee.Request("PUT", getServerURL(client, id), perigee.Options{
+ Results: &result.Resp,
ReqBody: map[string]interface{}{
"server": opts,
},
MoreHeaders: client.Provider.AuthenticatedHeaders(),
})
- return sr, err
+ return result
}
// ChangeAdminPassword alters the administrator or root password for a specified server.
@@ -194,39 +189,43 @@
//
// Rebuild returns a server result as though you had called GetDetail() on the server's ID.
// The information, however, refers to the new server, not the old.
-func Rebuild(client *gophercloud.ServiceClient, id, name, password, imageRef string, additional map[string]interface{}) (ServerResult, error) {
- var sr ServerResult
+func Rebuild(client *gophercloud.ServiceClient, id, name, password, imageRef string, additional map[string]interface{}) RebuildResult {
+ var result RebuildResult
if id == "" {
- return sr, &ErrArgument{
+ result.Err = &ErrArgument{
Function: "Rebuild",
Argument: "id",
Value: "",
}
+ return result
}
if name == "" {
- return sr, &ErrArgument{
+ result.Err = &ErrArgument{
Function: "Rebuild",
Argument: "name",
Value: "",
}
+ return result
}
if password == "" {
- return sr, &ErrArgument{
+ result.Err = &ErrArgument{
Function: "Rebuild",
Argument: "password",
Value: "",
}
+ return result
}
if imageRef == "" {
- return sr, &ErrArgument{
+ result.Err = &ErrArgument{
Function: "Rebuild",
Argument: "imageRef",
Value: "",
}
+ return result
}
if additional == nil {
@@ -237,17 +236,17 @@
additional["imageRef"] = imageRef
additional["adminPass"] = password
- _, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
+ _, result.Err = perigee.Request("POST", getActionURL(client, id), perigee.Options{
ReqBody: struct {
R map[string]interface{} `json:"rebuild"`
}{
additional,
},
- Results: &sr,
+ Results: &result.Resp,
MoreHeaders: client.Provider.AuthenticatedHeaders(),
OkCodes: []int{202},
})
- return sr, err
+ return result
}
// Resize instructs the provider to change the flavor of the server.
diff --git a/openstack/compute/v2/servers/requests_test.go b/openstack/compute/v2/servers/requests_test.go
index 3f5f2b4..fa1a2f5 100644
--- a/openstack/compute/v2/servers/requests_test.go
+++ b/openstack/compute/v2/servers/requests_test.go
@@ -88,20 +88,15 @@
})
client := serviceClient()
- result, err := Create(client, map[string]interface{}{
+ actual, err := Create(client, map[string]interface{}{
"name": "derp",
"imageRef": "f90f6034-2570-4974-8351-6b49732ef2eb",
"flavorRef": "1",
- })
+ }).Extract()
if err != nil {
t.Fatalf("Unexpected Create error: %v", err)
}
- actual, err := ExtractServer(result)
- if err != nil {
- t.Fatalf("Unexpected ExtractServer error: %v", err)
- }
-
equalServers(t, serverDerp, *actual)
}
@@ -136,16 +131,11 @@
})
client := serviceClient()
- response, err := Get(client, "1234asdf")
+ actual, err := Get(client, "1234asdf").Extract()
if err != nil {
t.Fatalf("Unexpected Get error: %v", err)
}
- actual, err := ExtractServer(response)
- if err != nil {
- t.Fatalf("Unexpected ExtractServer error: %v", err)
- }
-
equalServers(t, serverDerp, *actual)
}
@@ -164,18 +154,13 @@
})
client := serviceClient()
- response, err := Update(client, "1234asdf", map[string]interface{}{
+ actual, err := Update(client, "1234asdf", map[string]interface{}{
"name": "new-name",
- })
+ }).Extract()
if err != nil {
t.Fatalf("Unexpected Update error: %v", err)
}
- actual, err := ExtractServer(response)
- if err != nil {
- t.Fatalf("Unexpected ExtractServer error: %v", err)
- }
-
equalServers(t, serverDerp, *actual)
}
@@ -241,19 +226,15 @@
})
client := serviceClient()
- response, err := Rebuild(client,
+ actual, err := Rebuild(client,
"1234asdf", "new-name", "swordfish",
"http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
- map[string]interface{}{"accessIPv4": "1.2.3.4"})
+ map[string]interface{}{"accessIPv4": "1.2.3.4"},
+ ).Extract()
if err != nil {
t.Fatalf("Unexpected Rebuild error: %v", err)
}
- actual, err := ExtractServer(response)
- if err != nil {
- t.Fatalf("Unexpected ExtractServer error: %v", err)
- }
-
equalServers(t, serverDerp, *actual)
}
diff --git a/openstack/compute/v2/servers/servers.go b/openstack/compute/v2/servers/results.go
similarity index 66%
rename from openstack/compute/v2/servers/servers.go
rename to openstack/compute/v2/servers/results.go
index fbf50c3..8e967f0 100644
--- a/openstack/compute/v2/servers/servers.go
+++ b/openstack/compute/v2/servers/results.go
@@ -4,6 +4,7 @@
"errors"
"github.com/mitchellh/mapstructure"
+ "github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@@ -64,6 +65,60 @@
AdminPass string `mapstructure:"adminPass"`
}
+// extractServer interprets the result of a call expected to return data on a single server.
+func extractServer(r gophercloud.CommonResult) (*Server, error) {
+ if r.Err != nil {
+ return nil, r.Err
+ }
+
+ var response struct {
+ Server Server `mapstructure:"server"`
+ }
+
+ err := mapstructure.Decode(r.Resp, &response)
+ return &response.Server, err
+}
+
+// CreateResult temporarily contains the response from a Create call.
+type CreateResult struct {
+ gophercloud.CommonResult
+}
+
+// Extract interprets a CreateResult as a Server, if possible.
+func (r CreateResult) Extract() (*Server, error) {
+ return extractServer(r.CommonResult)
+}
+
+// GetResult temporarily contains the response from a Get call.
+type GetResult struct {
+ gophercloud.CommonResult
+}
+
+// Extract interprets a GetResult as a Server, if possible.
+func (r GetResult) Extract() (*Server, error) {
+ return extractServer(r.CommonResult)
+}
+
+// UpdateResult temporarily contains the response from an Update call.
+type UpdateResult struct {
+ gophercloud.CommonResult
+}
+
+// Extract interprets an UpdateResult as a Server, if possible.
+func (r UpdateResult) Extract() (*Server, error) {
+ return extractServer(r.CommonResult)
+}
+
+// RebuildResult temporarily contains the response from a Rebuild call.
+type RebuildResult struct {
+ gophercloud.CommonResult
+}
+
+// Extract interprets a RebuildResult as a Server, if possible.
+func (r RebuildResult) Extract() (*Server, error) {
+ return extractServer(r.CommonResult)
+}
+
// ExtractServers interprets the results of a single page from a List() call, producing a slice of Server entities.
func ExtractServers(page pagination.Page) ([]Server, error) {
casted := page.(ListPage).Body
@@ -74,15 +129,3 @@
err := mapstructure.Decode(casted, &response)
return response.Servers, err
}
-
-// ExtractServer interprets the result of a call expected to return data on a single server.
-func ExtractServer(sr ServerResult) (*Server, error) {
- so, ok := sr["server"]
- if !ok {
- return nil, ErrCannotInterpet
- }
- serverObj := so.(map[string]interface{})
- s := new(Server)
- err := mapstructure.Decode(serverObj, s)
- return s, err
-}
diff --git a/openstack/compute/v2/servers/servers_test.go b/openstack/compute/v2/servers/servers_test.go
index 9127991..590fc8b 100644
--- a/openstack/compute/v2/servers/servers_test.go
+++ b/openstack/compute/v2/servers/servers_test.go
@@ -1,192 +1,10 @@
package servers
import (
- "encoding/json"
"reflect"
"testing"
)
-// Taken from: http://docs.openstack.org/api/openstack-compute/2/content/List_Servers-d1e2078.html
-const goodListServersResult = `
-{
- "servers": [
- {
- "id": "52415800-8b69-11e0-9b19-734f6af67565",
- "tenant_id": "1234",
- "user_id": "5678",
- "name": "sample-server",
- "updated": "2010-10-10T12:00:00Z",
- "created": "2010-08-10T12:00:00Z",
- "hostId": "e4d909c290d0fb1ca068ffaddf22cbd0",
- "status": "BUILD",
- "progress": 60,
- "accessIPv4": "67.23.10.132",
- "accessIPv6": "::babe:67.23.10.132",
- "image": {
- "id": "52415800-8b69-11e0-9b19-734f6f006e54",
- "links": [
- {
- "rel": "self",
- "href": "http://servers.api.openstack.org/v2/1234/images/52415800-8b69-11e0-9b19-734f6f006e54"
- },
- {
- "rel": "bookmark",
- "href": "http://servers.api.openstack.org/1234/images/52415800-8b69-11e0-9b19-734f6f006e54"
- }
- ]
- },
- "flavor": {
- "id": "52415800-8b69-11e0-9b19-734f216543fd",
- "links": [
- {
- "rel": "self",
- "href": "http://servers.api.openstack.org/v2/1234/flavors/52415800-8b69-11e0-9b19-734f216543fd"
- },
- {
- "rel": "bookmark",
- "href": "http://servers.api.openstack.org/1234/flavors/52415800-8b69-11e0-9b19-734f216543fd"
- }
- ]
- },
- "addresses": {
- "public": [
- {
- "version": 4,
- "addr": "67.23.10.132"
- },
- {
- "version": 6,
- "addr": "::babe:67.23.10.132"
- },
- {
- "version": 4,
- "addr": "67.23.10.131"
- },
- {
- "version": 6,
- "addr": "::babe:4317:0A83"
- }
- ],
- "private": [
- {
- "version": 4,
- "addr": "10.176.42.16"
- },
- {
- "version": 6,
- "addr": "::babe:10.176.42.16"
- }
- ]
- },
- "metadata": {
- "Server Label": "Web Head 1",
- "Image Version": "2.1"
- },
- "links": [
- {
- "rel": "self",
- "href": "http://servers.api.openstack.org/v2/1234/servers/52415800-8b69-11e0-9b19-734f6af67565"
- },
- {
- "rel": "bookmark",
- "href": "http://servers.api.openstack.org/1234/servers/52415800-8b69-11e0-9b19-734f6af67565"
- }
- ]
- },
- {
- "id": "52415800-8b69-11e0-9b19-734f1f1350e5",
- "user_id": "5678",
- "name": "sample-server2",
- "tenant_id": "1234",
- "updated": "2010-10-10T12:00:00Z",
- "created": "2010-08-10T12:00:00Z",
- "hostId": "9e107d9d372bb6826bd81d3542a419d6",
- "status": "ACTIVE",
- "accessIPv4": "67.23.10.133",
- "accessIPv6": "::babe:67.23.10.133",
- "image": {
- "id": "52415800-8b69-11e0-9b19-734f5736d2a2",
- "links": [
- {
- "rel": "self",
- "href": "http://servers.api.openstack.org/v2/1234/images/52415800-8b69-11e0-9b19-734f5736d2a2"
- },
- {
- "rel": "bookmark",
- "href": "http://servers.api.openstack.org/1234/images/52415800-8b69-11e0-9b19-734f5736d2a2"
- }
- ]
- },
- "flavor": {
- "id": "52415800-8b69-11e0-9b19-734f216543fd",
- "links": [
- {
- "rel": "self",
- "href": "http://servers.api.openstack.org/v2/1234/flavors/52415800-8b69-11e0-9b19-734f216543fd"
- },
- {
- "rel": "bookmark",
- "href": "http://servers.api.openstack.org/1234/flavors/52415800-8b69-11e0-9b19-734f216543fd"
- }
- ]
- },
- "addresses": {
- "public": [
- {
- "version": 4,
- "addr": "67.23.10.133"
- },
- {
- "version": 6,
- "addr": "::babe:67.23.10.133"
- }
- ],
- "private": [
- {
- "version": 4,
- "addr": "10.176.42.17"
- },
- {
- "version": 6,
- "addr": "::babe:10.176.42.17"
- }
- ]
- },
- "metadata": {
- "Server Label": "DB 1"
- },
- "links": [
- {
- "rel": "self",
- "href": "http://servers.api.openstack.org/v2/1234/servers/52415800-8b69-11e0-9b19-734f1f1350e5"
- },
- {
- "rel": "bookmark",
- "href": "http://servers.api.openstack.org/1234/servers/52415800-8b69-11e0-9b19-734f1f1350e5"
- }
- ]
- }
- ]
-}
-`
-
-func TestExtractServers(t *testing.T) {
- var listPage ListPage
- err := json.Unmarshal([]byte(goodListServersResult), &listPage.MarkerPageBase.LastHTTPResponse.Body)
- if err != nil {
- t.Fatalf("Error decoding JSON fixture: %v", err)
- }
-
- svrs, err := ExtractServers(listPage)
- if err != nil {
- t.Fatalf("Error extracting servers: %v", err)
- }
-
- if len(svrs) != 2 {
- t.Fatalf("Expected 2 servers; got %d", len(svrs))
- }
-}
-
// This provides more fine-grained failures when Servers differ, because Server structs are too damn big to compare by eye.
// FIXME I should absolutely refactor this into a general-purpose thing in testhelper.
func equalServers(t *testing.T, expected Server, actual Server) {