Merge pull request #62 from rackspace/rescue-servers

Expose rescue/unrescue functionality.
diff --git a/acceptance/11-rescue-unrescue-server.go b/acceptance/11-rescue-unrescue-server.go
new file mode 100644
index 0000000..4c4d481
--- /dev/null
+++ b/acceptance/11-rescue-unrescue-server.go
@@ -0,0 +1,50 @@
+package main
+
+import (
+	"fmt"
+	"flag"
+	"github.com/rackspace/gophercloud"
+)
+
+var quiet = flag.Bool("quiet", false, "Quiet mode, for acceptance testing.  $? still indicates errors though.")
+
+func main() {
+	flag.Parse()
+	withIdentity(false, func(acc gophercloud.AccessProvider) {
+		withServerApi(acc, func(servers gophercloud.CloudServersProvider) {
+			log("Creating server")
+			id, err := createServer(servers, "", "", "", "")
+			if err != nil {
+				panic(err)
+			}
+			waitForServerState(servers, id, "ACTIVE")
+			defer servers.DeleteServerById(id)
+
+			log("Rescuing server")
+			adminPass, err := servers.RescueServer(id)
+			if err != nil {
+				panic(err)
+			}
+			log("  Admin password = " + adminPass)
+			if len(adminPass) < 1 {
+				panic("Empty admin password")
+			}
+			waitForServerState(servers, id, "RESCUE")
+
+			log("Unrescuing server")
+			err = servers.UnrescueServer(id)
+			if err != nil {
+				panic(err)
+			}
+			waitForServerState(servers, id, "ACTIVE")
+
+			log("Done")
+		})
+	})
+}
+
+func log(s string) {
+	if !*quiet {
+		fmt.Println(s)
+	}
+}
diff --git a/interfaces.go b/interfaces.go
index e05bdc4..2bb8ac5 100644
--- a/interfaces.go
+++ b/interfaces.go
@@ -36,6 +36,8 @@
 	RevertResize(id string) error
 	ConfirmResize(id string) error
 	RebootServer(id string, hard bool) error
+	RescueServer(id string) (string, error)
+	UnrescueServer(id string) error
 
   // Images
 
diff --git a/servers.go b/servers.go
index c1026b4..6b97f05 100644
--- a/servers.go
+++ b/servers.go
@@ -210,6 +210,43 @@
 	})
 }
 
+// See the CloudServersProvider interface for details
+func (gsp *genericServersProvider) RescueServer(id string) (string, error) {
+	var pw *string
+
+	err := gsp.context.WithReauth(gsp.access, func() error {
+		url := fmt.Sprintf("%s/servers/%s/action", gsp.endpoint, id)
+		return perigee.Post(url, perigee.Options{
+			ReqBody: &struct{
+				Rescue string `json:"rescue"`
+			}{"none"},
+			MoreHeaders: map[string]string{
+				"X-Auth-Token": gsp.access.AuthToken(),
+			},
+			Results: &struct{
+				AdminPass **string `json:"adminPass"`
+			}{&pw},
+		})
+	})
+	return *pw, err
+}
+
+// See the CloudServersProvider interface for details
+func (gsp *genericServersProvider) UnrescueServer(id string) error {
+	return gsp.context.WithReauth(gsp.access, func() error {
+		url := fmt.Sprintf("%s/servers/%s/action", gsp.endpoint, id)
+		return perigee.Post(url, perigee.Options{
+			ReqBody: &struct{
+				Unrescue *int `json:"unrescue"`
+			}{nil},
+			MoreHeaders: map[string]string{
+				"X-Auth-Token": gsp.access.AuthToken(),
+			},
+			OkCodes: []int{202},
+		})
+	})
+}
+
 // RaxBandwidth provides measurement of server bandwidth consumed over a given audit interval.
 type RaxBandwidth struct {
 	AuditPeriodEnd    string `json:"audit_period_end"`