Rackspace Auto Scale: Add webhooks Create()
diff --git a/rackspace/autoscale/v1/webhooks/fixtures.go b/rackspace/autoscale/v1/webhooks/fixtures.go
index 18f6b63..f243289 100644
--- a/rackspace/autoscale/v1/webhooks/fixtures.go
+++ b/rackspace/autoscale/v1/webhooks/fixtures.go
@@ -53,6 +53,24 @@
 }
 `
 
+// WebhookCreateBody contains the canned body of a webhooks.Create response.
+const WebhookCreateBody = WebhookListBody
+
+// WebhookCreateRequest contains the canned body of a webhooks.Create request.
+const WebhookCreateRequest = `
+[
+  {
+    "name": "first hook"
+  },
+  {
+    "name": "second hook",
+    "metadata": {
+      "notes": "a note about this webhook"
+    }
+  }
+]
+`
+
 var (
 	// FirstWebhook is a Webhook corresponding to the first result in WebhookListBody.
 	FirstWebhook = Webhook{
@@ -104,3 +122,22 @@
 		fmt.Fprintf(w, WebhookListBody)
 	})
 }
+
+// HandleWebhookCreateSuccessfully sets up the test server to respond to a webhooks Create request.
+func HandleWebhookCreateSuccessfully(t *testing.T) {
+	path := "/groups/10eb3219-1b12-4b34-b1e4-e10ee4f24c65/policies/2b48d247-0282-4b9d-8775-5c4b67e8e649/webhooks"
+
+	th.Mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		th.TestJSONRequest(t, r, WebhookCreateRequest)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, WebhookCreateBody)
+	})
+}
diff --git a/rackspace/autoscale/v1/webhooks/requests.go b/rackspace/autoscale/v1/webhooks/requests.go
index 1d56cef..5bcc3da 100644
--- a/rackspace/autoscale/v1/webhooks/requests.go
+++ b/rackspace/autoscale/v1/webhooks/requests.go
@@ -1,6 +1,8 @@
 package webhooks
 
 import (
+	"errors"
+
 	"github.com/rackspace/gophercloud"
 	"github.com/rackspace/gophercloud/pagination"
 )
@@ -15,3 +17,70 @@
 
 	return pagination.NewPager(client, url, createPageFn)
 }
+
+// CreateOptsBuilder is the interface responsible for generating the JSON
+// for a Create operation.
+type CreateOptsBuilder interface {
+	ToWebhookCreateMap() ([]map[string]interface{}, error)
+}
+
+// CreateOpts is a slice of CreateOpt structs, that allow the user to create
+// multiple webhooks in a single operation.
+type CreateOpts []CreateOpt
+
+// CreateOpt represents the options to create a webhook.
+type CreateOpt struct {
+	// Name [required] is a name for the webhook.
+	Name string
+
+	// Metadata [optional] is user-provided key-value metadata.
+	// Maximum length for keys and values is 256 characters.
+	Metadata map[string]string
+}
+
+// ToWebhookCreateMap converts a slice of CreateOpt structs into a map for use
+// in the request body of a Create operation.
+func (opts CreateOpts) ToWebhookCreateMap() ([]map[string]interface{}, error) {
+	var webhooks []map[string]interface{}
+
+	for _, o := range opts {
+		if o.Name == "" {
+			return nil, errors.New("Cannot create a Webhook without a name.")
+		}
+
+		hook := make(map[string]interface{})
+
+		hook["name"] = o.Name
+
+		if o.Metadata != nil {
+			hook["metadata"] = o.Metadata
+		}
+
+		webhooks = append(webhooks, hook)
+	}
+
+	return webhooks, nil
+}
+
+// Create requests a new webhook be created and associated with the given group
+// and scaling policy.
+func Create(client *gophercloud.ServiceClient, groupID, policyID string, opts CreateOptsBuilder) CreateResult {
+	var res CreateResult
+
+	reqBody, err := opts.ToWebhookCreateMap()
+
+	if err != nil {
+		res.Err = err
+		return res
+	}
+
+	resp, err := client.Post(createURL(client, groupID, policyID), reqBody, &res.Body, nil)
+
+	if err != nil {
+		res.Err = err
+		return res
+	}
+
+	pr := pagination.PageResultFromParsed(resp, res.Body)
+	return CreateResult{pagination.SinglePageBase(pr)}
+}
diff --git a/rackspace/autoscale/v1/webhooks/requests_test.go b/rackspace/autoscale/v1/webhooks/requests_test.go
index c0c1258..8c17aa2 100644
--- a/rackspace/autoscale/v1/webhooks/requests_test.go
+++ b/rackspace/autoscale/v1/webhooks/requests_test.go
@@ -8,14 +8,16 @@
 	"github.com/rackspace/gophercloud/testhelper/client"
 )
 
+const (
+	groupID  = "10eb3219-1b12-4b34-b1e4-e10ee4f24c65"
+	policyID = "2b48d247-0282-4b9d-8775-5c4b67e8e649"
+)
+
 func TestList(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()
 	HandleWebhookListSuccessfully(t)
 
-	groupID := "10eb3219-1b12-4b34-b1e4-e10ee4f24c65"
-	policyID := "2b48d247-0282-4b9d-8775-5c4b67e8e649"
-
 	pages := 0
 	pager := List(client.ServiceClient(), groupID, policyID)
 
@@ -44,3 +46,28 @@
 		t.Errorf("Expected 1 page, saw %d", pages)
 	}
 }
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleWebhookCreateSuccessfully(t)
+
+	client := client.ServiceClient()
+	opts := CreateOpts{
+		{
+			Name: "first hook",
+		},
+		{
+			Name: "second hook",
+			Metadata: map[string]string{
+				"notes": "a note about this webhook",
+			},
+		},
+	}
+
+	webhooks, err := Create(client, groupID, policyID, opts).ExtractWebhooks()
+
+	th.AssertNoErr(t, err)
+	th.CheckDeepEquals(t, FirstWebhook, webhooks[0])
+	th.CheckDeepEquals(t, SecondWebhook, webhooks[1])
+}
diff --git a/rackspace/autoscale/v1/webhooks/results.go b/rackspace/autoscale/v1/webhooks/results.go
index a15e20e..410f5cb 100644
--- a/rackspace/autoscale/v1/webhooks/results.go
+++ b/rackspace/autoscale/v1/webhooks/results.go
@@ -11,6 +11,23 @@
 	gophercloud.Result
 }
 
+// CreateResult represents the result of a create operation. Multiple webhooks
+// can be created in a single call, so the result should be treated as a typical
+// pagination Page.  ExtractWebhooks() can be used to extract a slice of
+// Webhooks from a single page.
+type CreateResult struct {
+	pagination.SinglePageBase
+}
+
+// ExtractWebhooks extracts a slice of Webhooks from a CreateResult.
+func (res CreateResult) ExtractWebhooks() ([]Webhook, error) {
+	if res.Err != nil {
+		return nil, res.Err
+	}
+
+	return commonExtractWebhooks(res.Body)
+}
+
 // Webhook represents a webhook associted with a scaling policy.
 type Webhook struct {
 	// UUID for the webhook.
@@ -46,13 +63,15 @@
 // ExtractWebhooks interprets the results of a single page from a List() call,
 // producing a slice of Webhooks.
 func ExtractWebhooks(page pagination.Page) ([]Webhook, error) {
-	casted := page.(WebhookPage).Body
+	return commonExtractWebhooks(page.(WebhookPage).Body)
+}
 
+func commonExtractWebhooks(body interface{}) ([]Webhook, error) {
 	var response struct {
 		Webhooks []Webhook `mapstructure:"webhooks"`
 	}
 
-	err := mapstructure.Decode(casted, &response)
+	err := mapstructure.Decode(body, &response)
 
 	if err != nil {
 		return nil, err
diff --git a/rackspace/autoscale/v1/webhooks/urls.go b/rackspace/autoscale/v1/webhooks/urls.go
index 802db89..90825a0 100644
--- a/rackspace/autoscale/v1/webhooks/urls.go
+++ b/rackspace/autoscale/v1/webhooks/urls.go
@@ -5,3 +5,7 @@
 func listURL(c *gophercloud.ServiceClient, groupID, policyID string) string {
 	return c.ServiceURL("groups", groupID, "policies", policyID, "webhooks")
 }
+
+func createURL(c *gophercloud.ServiceClient, groupID, policyID string) string {
+	return c.ServiceURL("groups", groupID, "policies", policyID, "webhooks")
+}