Compute v2: Extended Availability Zone Status (#282)
* Compute v2: Extended Availability Zone Status API
* Compute v2: Extended Availability Zone Status unit tests
* Compute v2: Extended Availability Zone Status acceptance tests
diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go
index 22b6580..f43c94d 100644
--- a/acceptance/openstack/compute/v2/servers_test.go
+++ b/acceptance/openstack/compute/v2/servers_test.go
@@ -9,6 +9,7 @@
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
+ "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
th "github.com/gophercloud/gophercloud/testhelper"
)
@@ -88,6 +89,30 @@
}
}
+func TestServersCreateDestroyWithExtensions(t *testing.T) {
+ var extendedServer struct {
+ servers.Server
+ availabilityzones.ServerExt
+ }
+
+ client, err := clients.NewComputeV2Client()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ server, err := CreateServer(t, client)
+ if err != nil {
+ t.Fatalf("Unable to create server: %v", err)
+ }
+ defer DeleteServer(t, client, server)
+
+ err = servers.Get(client, server.ID).ExtractInto(&extendedServer)
+ if err != nil {
+ t.Errorf("Unable to retrieve server: %v", err)
+ }
+ tools.PrintResource(t, extendedServer)
+}
+
func TestServersWithoutImageRef(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
diff --git a/openstack/compute/v2/extensions/availabilityzones/results.go b/openstack/compute/v2/extensions/availabilityzones/results.go
new file mode 100644
index 0000000..96a6a50
--- /dev/null
+++ b/openstack/compute/v2/extensions/availabilityzones/results.go
@@ -0,0 +1,12 @@
+package availabilityzones
+
+// ServerExt is an extension to the base Server object
+type ServerExt struct {
+ // AvailabilityZone is the availabilty zone the server is in.
+ AvailabilityZone string `json:"OS-EXT-AZ:availability_zone"`
+}
+
+// UnmarshalJSON to override default
+func (r *ServerExt) UnmarshalJSON(b []byte) error {
+ return nil
+}
diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go
index c121a6b..1ae1e91 100644
--- a/openstack/compute/v2/servers/results.go
+++ b/openstack/compute/v2/servers/results.go
@@ -19,11 +19,17 @@
// Extract interprets any serverResult as a Server, if possible.
func (r serverResult) Extract() (*Server, error) {
- var s struct {
- Server *Server `json:"server"`
- }
+ var s Server
err := r.ExtractInto(&s)
- return s.Server, err
+ return &s, err
+}
+
+func (r serverResult) ExtractInto(v interface{}) error {
+ return r.Result.ExtractIntoStructPtr(v, "server")
+}
+
+func ExtractServersInto(r pagination.Page, v interface{}) error {
+ return r.(ServerPage).Result.ExtractIntoSlicePtr(v, "servers")
}
// CreateResult temporarily contains the response from a Create call.
@@ -221,11 +227,9 @@
// ExtractServers interprets the results of a single page from a List() call, producing a slice of Server entities.
func ExtractServers(r pagination.Page) ([]Server, error) {
- var s struct {
- Servers []Server `json:"servers"`
- }
- err := (r.(ServerPage)).ExtractInto(&s)
- return s.Servers, err
+ var s []Server
+ err := ExtractServersInto(r, &s)
+ return s, err
}
// MetadataResult contains the result of a call for (potentially) multiple key-value pairs.
diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go
index 15d6eb5..05712f7 100644
--- a/openstack/compute/v2/servers/testing/requests_test.go
+++ b/openstack/compute/v2/servers/testing/requests_test.go
@@ -6,6 +6,7 @@
"net/http"
"testing"
+ "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
"github.com/gophercloud/gophercloud/pagination"
th "github.com/gophercloud/gophercloud/testhelper"
@@ -56,6 +57,26 @@
th.CheckDeepEquals(t, ServerDerp, actual[1])
}
+func TestListAllServersWithExtensions(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+ HandleServerListSuccessfully(t)
+
+ type ServerWithExt struct {
+ servers.Server
+ availabilityzones.ServerExt
+ }
+
+ allPages, err := servers.List(client.ServiceClient(), servers.ListOpts{}).AllPages()
+ th.AssertNoErr(t, err)
+
+ var actual []ServerWithExt
+ err = servers.ExtractServersInto(allPages, &actual)
+ th.AssertNoErr(t, err)
+ th.AssertEquals(t, 3, len(actual))
+ th.AssertEquals(t, "nova", actual[0].AvailabilityZone)
+}
+
func TestCreateServer(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
@@ -189,6 +210,26 @@
th.CheckDeepEquals(t, ServerDerp, *actual)
}
+func TestGetServerWithExtensions(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+ HandleServerGetSuccessfully(t)
+
+ var s struct {
+ servers.Server
+ availabilityzones.ServerExt
+ }
+
+ err := servers.Get(client.ServiceClient(), "1234asdf").ExtractInto(&s)
+ th.AssertNoErr(t, err)
+ th.AssertEquals(t, "nova", s.AvailabilityZone)
+
+ err = servers.Get(client.ServiceClient(), "1234asdf").ExtractInto(s)
+ if err == nil {
+ t.Errorf("Expected error when providing non-pointer struct")
+ }
+}
+
func TestUpdateServer(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()