Add nodes :joy:
diff --git a/rackspace/lb/v1/nodes/fixtures.go b/rackspace/lb/v1/nodes/fixtures.go
index 77be3b6..65eefe0 100644
--- a/rackspace/lb/v1/nodes/fixtures.go
+++ b/rackspace/lb/v1/nodes/fixtures.go
@@ -49,8 +49,8 @@
})
}
-func mockCreateResponse(t *testing.T) {
- th.Mux.HandleFunc("/loadbalancers", func(w http.ResponseWriter, r *http.Request) {
+func mockCreateResponse(t *testing.T, lbID int) {
+ th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "POST")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
diff --git a/rackspace/lb/v1/nodes/requests.go b/rackspace/lb/v1/nodes/requests.go
index 6e723d2..338c250 100644
--- a/rackspace/lb/v1/nodes/requests.go
+++ b/rackspace/lb/v1/nodes/requests.go
@@ -3,19 +3,89 @@
import (
"fmt"
+ "github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
func List(client *gophercloud.ServiceClient, loadBalancerID int, limit *int) pagination.Pager {
url := rootURL(client, loadBalancerID)
-
if limit != nil {
url += fmt.Sprintf("?limit=%d", limit)
}
- createPageFn := func(r pagination.PageResult) pagination.Page {
+ return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
return NodePage{pagination.SinglePageBase(r)}
+ })
+}
+
+type CreateOptsBuilder interface {
+ ToNodeCreateMap() (map[string]interface{}, error)
+}
+
+type CreateOpts []CreateOpt
+
+type CreateOpt struct {
+ // Required
+ Address string
+ Port int
+ Condition Condition
+ Type Type
+}
+
+func (opts CreateOpts) ToNodeCreateMap() (map[string]interface{}, error) {
+ type nodeMap map[string]interface{}
+ nodes := []nodeMap{}
+
+ for k, v := range opts {
+ if v.Address == "" {
+ return nodeMap{}, fmt.Errorf("ID is a required attribute, none provided for %d CreateOpt element", k)
+ }
+
+ node := make(map[string]interface{})
+ node["address"] = v.Address
+
+ if v.Port > 0 {
+ node["port"] = v.Port
+ }
+ if v.Condition != "" {
+ node["condition"] = v.Condition
+ }
+ if v.Type != "" {
+ node["type"] = v.Type
+ }
+
+ nodes = append(nodes, node)
}
- return pagination.NewPager(client, url, createPageFn)
+
+ return nodeMap{"nodes": nodes}, nil
+}
+
+func Create(client *gophercloud.ServiceClient, loadBalancerID int, opts CreateOptsBuilder) CreateResult {
+ var res CreateResult
+
+ reqBody, err := opts.ToNodeCreateMap()
+ if err != nil {
+ res.Err = err
+ return res
+ }
+
+ resp, err := perigee.Request("POST", rootURL(client, loadBalancerID), perigee.Options{
+ MoreHeaders: client.AuthenticatedHeaders(),
+ ReqBody: &reqBody,
+ Results: &res.Body,
+ OkCodes: []int{200},
+ })
+ if err != nil {
+ res.Err = err
+ return res
+ }
+
+ pr, err := pagination.PageResultFrom(resp.HttpResponse)
+ if err != nil {
+ res.Err = err
+ return res
+ }
+
+ return CreateResult{pagination.SinglePageBase(pr)}
}
diff --git a/rackspace/lb/v1/nodes/requests_test.go b/rackspace/lb/v1/nodes/requests_test.go
index 0509670..8e4813f 100644
--- a/rackspace/lb/v1/nodes/requests_test.go
+++ b/rackspace/lb/v1/nodes/requests_test.go
@@ -55,3 +55,53 @@
th.AssertNoErr(t, err)
th.AssertEquals(t, 1, count)
}
+
+func TestCreate(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+
+ mockCreateResponse(t, lbID)
+
+ opts := CreateOpts{
+ CreateOpt{
+ Address: "10.2.2.3",
+ Port: 80,
+ Condition: ENABLED,
+ Type: PRIMARY,
+ },
+ CreateOpt{
+ Address: "10.2.2.4",
+ Port: 81,
+ Condition: ENABLED,
+ Type: SECONDARY,
+ },
+ }
+
+ page := Create(client.ServiceClient(), lbID, opts)
+
+ actual, err := page.ExtractNodes()
+ th.AssertNoErr(t, err)
+
+ expected := []Node{
+ Node{
+ ID: 185,
+ Address: "10.2.2.3",
+ Port: 80,
+ Condition: ENABLED,
+ Status: ONLINE,
+ Weight: 1,
+ Type: PRIMARY,
+ },
+ Node{
+ ID: 186,
+ Address: "10.2.2.4",
+ Port: 81,
+ Condition: ENABLED,
+ Status: ONLINE,
+ Weight: 1,
+ Type: SECONDARY,
+ },
+ }
+
+ th.CheckDeepEquals(t, expected, actual)
+}
diff --git a/rackspace/lb/v1/nodes/results.go b/rackspace/lb/v1/nodes/results.go
index 98cb741..399f5c9 100644
--- a/rackspace/lb/v1/nodes/results.go
+++ b/rackspace/lb/v1/nodes/results.go
@@ -98,15 +98,31 @@
return len(is) == 0, nil
}
-// ExtractNodes accepts a Page struct, specifically a NodePage struct, and
-// extracts the elements into a slice of Node structs. In other words, a
-// generic collection is mapped into a relevant slice.
-func ExtractNodes(page pagination.Page) ([]Node, error) {
+func commonExtractNodes(body interface{}) ([]Node, error) {
var resp struct {
Nodes []Node `mapstructure:"nodes" json:"nodes"`
}
- err := mapstructure.Decode(page.(NodePage).Body, &resp)
+ err := mapstructure.Decode(body, &resp)
return resp.Nodes, err
}
+
+// ExtractNodes accepts a Page struct, specifically a NodePage struct, and
+// extracts the elements into a slice of Node structs. In other words, a
+// generic collection is mapped into a relevant slice.
+func ExtractNodes(page pagination.Page) ([]Node, error) {
+ return commonExtractNodes(page.(NodePage).Body)
+}
+
+// CreateResult represents the result of a create operation. Since multiple
+// nodes can be added in one operation, this result represents multiple nodes -
+// and should be treated as a typical pagination Page. Use ExtractNodes to get
+// out a slice of Node structs.
+type CreateResult struct {
+ pagination.SinglePageBase
+}
+
+func (res CreateResult) ExtractNodes() ([]Node, error) {
+ return commonExtractNodes(res.Body)
+}