Merge pull request #237 from jamiehannaford/server-action-results

Server action results
diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go
index 5193e4e..3113aeb 100644
--- a/acceptance/openstack/compute/v2/servers_test.go
+++ b/acceptance/openstack/compute/v2/servers_test.go
@@ -157,8 +157,8 @@
 	}
 
 	randomPassword := tools.MakeNewPassword(server.AdminPass)
-	err = servers.ChangeAdminPassword(client, server.ID, randomPassword)
-	if err != nil {
+	res = servers.ChangeAdminPassword(client, server.ID, randomPassword)
+	if res.Err != nil {
 		t.Fatal(err)
 	}
 
@@ -194,14 +194,14 @@
 		t.Fatal(err)
 	}
 
-	err = servers.Reboot(client, server.ID, "aldhjflaskhjf")
-	if err == nil {
+	res = servers.Reboot(client, server.ID, "aldhjflaskhjf")
+	if res.Err == nil {
 		t.Fatal("Expected the SDK to provide an ArgumentError here")
 	}
 
 	t.Logf("Attempting reboot of server %s", server.ID)
-	err = servers.Reboot(client, server.ID, servers.OSReboot)
-	if err != nil {
+	res = servers.Reboot(client, server.ID, servers.OSReboot)
+	if res.Err != nil {
 		t.Fatalf("Unable to reboot server: %v", err)
 	}
 
@@ -270,7 +270,7 @@
 
 	t.Logf("Attempting to resize server [%s]", server.ID)
 
-	if err := servers.Resize(client, server.ID, choices.FlavorIDResize); err != nil {
+	if res := servers.Resize(client, server.ID, choices.FlavorIDResize); res.Err != nil {
 		t.Fatal(err)
 	}
 
@@ -301,7 +301,7 @@
 
 	t.Logf("Attempting to confirm resize for server %s", server.ID)
 
-	if err = servers.ConfirmResize(client, server.ID); err != nil {
+	if res := servers.ConfirmResize(client, server.ID); res.Err != nil {
 		t.Fatal(err)
 	}
 
@@ -332,7 +332,7 @@
 
 	t.Logf("Attempting to revert resize for server %s", server.ID)
 
-	if err := servers.RevertResize(client, server.ID); err != nil {
+	if res := servers.RevertResize(client, server.ID); res.Err != nil {
 		t.Fatal(err)
 	}
 
diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go
index 4e20036..9207474 100644
--- a/openstack/compute/v2/servers/requests.go
+++ b/openstack/compute/v2/servers/requests.go
@@ -261,7 +261,7 @@
 }
 
 // ChangeAdminPassword alters the administrator or root password for a specified server.
-func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword string) error {
+func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword string) ActionResult {
 	var req struct {
 		ChangePassword struct {
 			AdminPass string `json:"adminPass"`
@@ -270,12 +270,16 @@
 
 	req.ChangePassword.AdminPass = newPassword
 
-	_, err := perigee.Request("POST", actionURL(client, id), perigee.Options{
+	var res ActionResult
+
+	_, res.Err = perigee.Request("POST", actionURL(client, id), perigee.Options{
 		ReqBody:     req,
+		Results:     &res.Body,
 		MoreHeaders: client.Provider.AuthenticatedHeaders(),
 		OkCodes:     []int{202},
 	})
-	return err
+
+	return res
 }
 
 // ErrArgument errors occur when an argument supplied to a package function
@@ -326,16 +330,19 @@
 //
 // SoftReboot (aka OSReboot) simply tells the OS to restart under its own procedures.
 // E.g., in Linux, asking it to enter runlevel 6, or executing "sudo shutdown -r now", or by asking Windows to restart the machine.
-func Reboot(client *gophercloud.ServiceClient, id string, how RebootMethod) error {
+func Reboot(client *gophercloud.ServiceClient, id string, how RebootMethod) ActionResult {
+	var res ActionResult
+
 	if (how != SoftReboot) && (how != HardReboot) {
-		return &ErrArgument{
+		res.Err = &ErrArgument{
 			Function: "Reboot",
 			Argument: "how",
 			Value:    how,
 		}
+		return res
 	}
 
-	_, err := perigee.Request("POST", actionURL(client, id), perigee.Options{
+	_, res.Err = perigee.Request("POST", actionURL(client, id), perigee.Options{
 		ReqBody: struct {
 			C map[string]string `json:"reboot"`
 		}{
@@ -344,7 +351,8 @@
 		MoreHeaders: client.Provider.AuthenticatedHeaders(),
 		OkCodes:     []int{202},
 	})
-	return err
+
+	return res
 }
 
 // RebuildOptsBuilder is an interface that allows extensions to override the
@@ -453,8 +461,10 @@
 // While in this state, you can explore the use of the new server's configuration.
 // If you like it, call ConfirmResize() to commit the resize permanently.
 // Otherwise, call RevertResize() to restore the old configuration.
-func Resize(client *gophercloud.ServiceClient, id, flavorRef string) error {
-	_, err := perigee.Request("POST", actionURL(client, id), perigee.Options{
+func Resize(client *gophercloud.ServiceClient, id, flavorRef string) ActionResult {
+	var res ActionResult
+
+	_, res.Err = perigee.Request("POST", actionURL(client, id), perigee.Options{
 		ReqBody: struct {
 			R map[string]interface{} `json:"resize"`
 		}{
@@ -463,27 +473,34 @@
 		MoreHeaders: client.Provider.AuthenticatedHeaders(),
 		OkCodes:     []int{202},
 	})
-	return err
+
+	return res
 }
 
 // ConfirmResize confirms a previous resize operation on a server.
 // See Resize() for more details.
-func ConfirmResize(client *gophercloud.ServiceClient, id string) error {
-	_, err := perigee.Request("POST", actionURL(client, id), perigee.Options{
+func ConfirmResize(client *gophercloud.ServiceClient, id string) ActionResult {
+	var res ActionResult
+
+	_, res.Err = perigee.Request("POST", actionURL(client, id), perigee.Options{
 		ReqBody:     map[string]interface{}{"confirmResize": nil},
 		MoreHeaders: client.Provider.AuthenticatedHeaders(),
 		OkCodes:     []int{204},
 	})
-	return err
+
+	return res
 }
 
 // RevertResize cancels a previous resize operation on a server.
 // See Resize() for more details.
-func RevertResize(client *gophercloud.ServiceClient, id string) error {
-	_, err := perigee.Request("POST", actionURL(client, id), perigee.Options{
+func RevertResize(client *gophercloud.ServiceClient, id string) ActionResult {
+	var res ActionResult
+
+	_, res.Err = perigee.Request("POST", actionURL(client, id), perigee.Options{
 		ReqBody:     map[string]interface{}{"revertResize": nil},
 		MoreHeaders: client.Provider.AuthenticatedHeaders(),
 		OkCodes:     []int{202},
 	})
-	return err
+
+	return res
 }
diff --git a/openstack/compute/v2/servers/requests_test.go b/openstack/compute/v2/servers/requests_test.go
index d7ce2a0..0db92f9 100644
--- a/openstack/compute/v2/servers/requests_test.go
+++ b/openstack/compute/v2/servers/requests_test.go
@@ -163,11 +163,8 @@
 		w.WriteHeader(http.StatusAccepted)
 	})
 
-	client := fake.ServiceClient()
-	err := ChangeAdminPassword(client, "1234asdf", "new-password")
-	if err != nil {
-		t.Errorf("Unexpected ChangeAdminPassword error: %v", err)
-	}
+	res := ChangeAdminPassword(fake.ServiceClient(), "1234asdf", "new-password")
+	testhelper.AssertNoErr(t, res.Err)
 }
 
 func TestRebootServer(t *testing.T) {
@@ -182,11 +179,8 @@
 		w.WriteHeader(http.StatusAccepted)
 	})
 
-	client := fake.ServiceClient()
-	err := Reboot(client, "1234asdf", SoftReboot)
-	if err != nil {
-		t.Errorf("Unexpected Reboot error: %v", err)
-	}
+	res := Reboot(fake.ServiceClient(), "1234asdf", SoftReboot)
+	testhelper.AssertNoErr(t, res.Err)
 }
 
 func TestRebuildServer(t *testing.T) {
@@ -237,11 +231,8 @@
 		w.WriteHeader(http.StatusAccepted)
 	})
 
-	client := fake.ServiceClient()
-	err := Resize(client, "1234asdf", "2")
-	if err != nil {
-		t.Errorf("Unexpected Reboot error: %v", err)
-	}
+	res := Resize(fake.ServiceClient(), "1234asdf", "2")
+	testhelper.AssertNoErr(t, res.Err)
 }
 
 func TestConfirmResize(t *testing.T) {
@@ -256,11 +247,8 @@
 		w.WriteHeader(http.StatusNoContent)
 	})
 
-	client := fake.ServiceClient()
-	err := ConfirmResize(client, "1234asdf")
-	if err != nil {
-		t.Errorf("Unexpected ConfirmResize error: %v", err)
-	}
+	res := ConfirmResize(fake.ServiceClient(), "1234asdf")
+	testhelper.AssertNoErr(t, res.Err)
 }
 
 func TestRevertResize(t *testing.T) {
@@ -275,9 +263,6 @@
 		w.WriteHeader(http.StatusAccepted)
 	})
 
-	client := fake.ServiceClient()
-	err := RevertResize(client, "1234asdf")
-	if err != nil {
-		t.Errorf("Unexpected RevertResize error: %v", err)
-	}
+	res := RevertResize(fake.ServiceClient(), "1234asdf")
+	testhelper.AssertNoErr(t, res.Err)
 }
diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go
index fff6203..c3e41d7 100644
--- a/openstack/compute/v2/servers/results.go
+++ b/openstack/compute/v2/servers/results.go
@@ -44,6 +44,16 @@
 	serverResult
 }
 
+// ActionResult represents the result of server action operations, like reboot
+type ActionResult struct {
+	gophercloud.Result
+}
+
+// Extract is a function that extracts error information from a result
+func (r ActionResult) Extract() error {
+	return r.Err
+}
+
 // Server exposes only the standard OpenStack fields corresponding to a given server on the user's account.
 type Server struct {
 	// ID uniquely identifies this server amongst all other servers, including those not accessible to the current tenant.