Jamie Hannaford | b6927c1 | 2014-11-03 10:31:26 +0100 | [diff] [blame] | 1 | package nodes |
Jamie Hannaford | 3cfa00a | 2014-11-03 11:16:35 +0100 | [diff] [blame] | 2 | |
| 3 | import ( |
Jamie Hannaford | 0a9a6be | 2014-11-03 12:55:38 +0100 | [diff] [blame] | 4 | "errors" |
Jamie Hannaford | 3cfa00a | 2014-11-03 11:16:35 +0100 | [diff] [blame] | 5 | "fmt" |
| 6 | |
| 7 | "github.com/rackspace/gophercloud" |
| 8 | "github.com/rackspace/gophercloud/pagination" |
| 9 | ) |
| 10 | |
Jamie Hannaford | 8cdaa80 | 2014-11-03 15:11:39 +0100 | [diff] [blame] | 11 | // List is the operation responsible for returning a paginated collection of |
| 12 | // load balancer nodes. It requires the node ID, its parent load balancer ID, |
| 13 | // and optional limit integer (passed in either as a pointer or a nil poitner). |
Jamie Hannaford | 3cfa00a | 2014-11-03 11:16:35 +0100 | [diff] [blame] | 14 | func List(client *gophercloud.ServiceClient, loadBalancerID int, limit *int) pagination.Pager { |
| 15 | url := rootURL(client, loadBalancerID) |
Jamie Hannaford | 3cfa00a | 2014-11-03 11:16:35 +0100 | [diff] [blame] | 16 | if limit != nil { |
| 17 | url += fmt.Sprintf("?limit=%d", limit) |
| 18 | } |
| 19 | |
Jamie Hannaford | ed8b89a | 2014-11-03 12:24:19 +0100 | [diff] [blame] | 20 | return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { |
Jamie Hannaford | 3cfa00a | 2014-11-03 11:16:35 +0100 | [diff] [blame] | 21 | return NodePage{pagination.SinglePageBase(r)} |
Jamie Hannaford | ed8b89a | 2014-11-03 12:24:19 +0100 | [diff] [blame] | 22 | }) |
| 23 | } |
| 24 | |
Jamie Hannaford | 8cdaa80 | 2014-11-03 15:11:39 +0100 | [diff] [blame] | 25 | // CreateOptsBuilder is the interface responsible for generating the JSON |
| 26 | // for a Create operation. |
Jamie Hannaford | ed8b89a | 2014-11-03 12:24:19 +0100 | [diff] [blame] | 27 | type CreateOptsBuilder interface { |
| 28 | ToNodeCreateMap() (map[string]interface{}, error) |
| 29 | } |
| 30 | |
Jamie Hannaford | 8cdaa80 | 2014-11-03 15:11:39 +0100 | [diff] [blame] | 31 | // CreateOpts is a slice of CreateOpt structs, that allow the user to create |
| 32 | // multiple nodes in a single operation (one node per CreateOpt). |
Jamie Hannaford | ed8b89a | 2014-11-03 12:24:19 +0100 | [diff] [blame] | 33 | type CreateOpts []CreateOpt |
| 34 | |
Jamie Hannaford | 8cdaa80 | 2014-11-03 15:11:39 +0100 | [diff] [blame] | 35 | // CreateOpt represents the options to create a single node. |
Jamie Hannaford | ed8b89a | 2014-11-03 12:24:19 +0100 | [diff] [blame] | 36 | type CreateOpt struct { |
Jamie Hannaford | 8cdaa80 | 2014-11-03 15:11:39 +0100 | [diff] [blame] | 37 | // Required - the IP address or CIDR for this back-end node. It can either be |
| 38 | // a private IP (ServiceNet) or a public IP. |
| 39 | Address string |
| 40 | |
| 41 | // Optional - the port on which traffic is sent and received. |
| 42 | Port int |
| 43 | |
| 44 | // Optional - the condition of the node. See the consts in Results.go. |
Jamie Hannaford | ed8b89a | 2014-11-03 12:24:19 +0100 | [diff] [blame] | 45 | Condition Condition |
Jamie Hannaford | 8cdaa80 | 2014-11-03 15:11:39 +0100 | [diff] [blame] | 46 | |
| 47 | // Optional - the type of the node. See the consts in Results.go. |
| 48 | Type Type |
| 49 | |
| 50 | // Optional - a pointer to an integer between 0 and 100. |
| 51 | Weight *int |
Jamie Hannaford | 00222d7 | 2014-11-03 13:58:52 +0100 | [diff] [blame] | 52 | } |
| 53 | |
| 54 | func validateWeight(weight *int) error { |
| 55 | if weight != nil && (*weight > 100 || *weight < 0) { |
| 56 | return errors.New("Weight must be a valid int between 0 and 100") |
| 57 | } |
| 58 | return nil |
Jamie Hannaford | ed8b89a | 2014-11-03 12:24:19 +0100 | [diff] [blame] | 59 | } |
| 60 | |
Jamie Hannaford | 8cdaa80 | 2014-11-03 15:11:39 +0100 | [diff] [blame] | 61 | // ToNodeCreateMap converts a slice of options into a map that can be used for |
| 62 | // the JSON. |
Jamie Hannaford | ed8b89a | 2014-11-03 12:24:19 +0100 | [diff] [blame] | 63 | func (opts CreateOpts) ToNodeCreateMap() (map[string]interface{}, error) { |
| 64 | type nodeMap map[string]interface{} |
| 65 | nodes := []nodeMap{} |
| 66 | |
| 67 | for k, v := range opts { |
| 68 | if v.Address == "" { |
| 69 | return nodeMap{}, fmt.Errorf("ID is a required attribute, none provided for %d CreateOpt element", k) |
| 70 | } |
Jamie Hannaford | 00222d7 | 2014-11-03 13:58:52 +0100 | [diff] [blame] | 71 | if weightErr := validateWeight(v.Weight); weightErr != nil { |
| 72 | return nodeMap{}, weightErr |
| 73 | } |
Jamie Hannaford | ed8b89a | 2014-11-03 12:24:19 +0100 | [diff] [blame] | 74 | |
| 75 | node := make(map[string]interface{}) |
| 76 | node["address"] = v.Address |
| 77 | |
| 78 | if v.Port > 0 { |
| 79 | node["port"] = v.Port |
| 80 | } |
| 81 | if v.Condition != "" { |
| 82 | node["condition"] = v.Condition |
| 83 | } |
| 84 | if v.Type != "" { |
| 85 | node["type"] = v.Type |
| 86 | } |
Jamie Hannaford | 00222d7 | 2014-11-03 13:58:52 +0100 | [diff] [blame] | 87 | if v.Weight != nil { |
| 88 | node["weight"] = &v.Weight |
| 89 | } |
Jamie Hannaford | ed8b89a | 2014-11-03 12:24:19 +0100 | [diff] [blame] | 90 | |
| 91 | nodes = append(nodes, node) |
Jamie Hannaford | 3cfa00a | 2014-11-03 11:16:35 +0100 | [diff] [blame] | 92 | } |
Jamie Hannaford | ed8b89a | 2014-11-03 12:24:19 +0100 | [diff] [blame] | 93 | |
| 94 | return nodeMap{"nodes": nodes}, nil |
| 95 | } |
| 96 | |
Jamie Hannaford | 8cdaa80 | 2014-11-03 15:11:39 +0100 | [diff] [blame] | 97 | // Create is the operation responsible for creating a new node on a load |
| 98 | // balancer. Since every load balancer exists in both ServiceNet and the public |
| 99 | // Internet, both private and public IP addresses can be used for nodes. |
| 100 | // |
| 101 | // If nodes need time to boot up services before they become operational, you |
| 102 | // can temporarily prevent traffic from being sent to that node by setting the |
| 103 | // Condition field to DRAINING. Health checks will still be performed; but once |
| 104 | // your node is ready, you can update its condition to ENABLED and have it |
| 105 | // handle traffic. |
Jamie Hannaford | ed8b89a | 2014-11-03 12:24:19 +0100 | [diff] [blame] | 106 | func Create(client *gophercloud.ServiceClient, loadBalancerID int, opts CreateOptsBuilder) CreateResult { |
| 107 | var res CreateResult |
| 108 | |
| 109 | reqBody, err := opts.ToNodeCreateMap() |
| 110 | if err != nil { |
| 111 | res.Err = err |
| 112 | return res |
| 113 | } |
| 114 | |
Jamie Hannaford | 5497f94 | 2015-03-25 11:55:51 +0100 | [diff] [blame^] | 115 | resp, err := client.Post(rootURL(client, loadBalancerID), reqBody, &res.Body, nil) |
| 116 | |
Jamie Hannaford | ed8b89a | 2014-11-03 12:24:19 +0100 | [diff] [blame] | 117 | if err != nil { |
| 118 | res.Err = err |
| 119 | return res |
| 120 | } |
| 121 | |
Ash Wilson | 59fb6c4 | 2015-02-12 16:21:13 -0500 | [diff] [blame] | 122 | pr, err := pagination.PageResultFrom(resp) |
Jamie Hannaford | ed8b89a | 2014-11-03 12:24:19 +0100 | [diff] [blame] | 123 | if err != nil { |
| 124 | res.Err = err |
| 125 | return res |
| 126 | } |
| 127 | |
| 128 | return CreateResult{pagination.SinglePageBase(pr)} |
Jamie Hannaford | 3cfa00a | 2014-11-03 11:16:35 +0100 | [diff] [blame] | 129 | } |
Jamie Hannaford | 16bebfc | 2014-11-03 12:52:30 +0100 | [diff] [blame] | 130 | |
Jamie Hannaford | 8cdaa80 | 2014-11-03 15:11:39 +0100 | [diff] [blame] | 131 | // BulkDelete is the operation responsible for batch deleting multiple nodes in |
| 132 | // a single operation. It accepts a slice of integer IDs and will remove them |
| 133 | // from the load balancer. The maximum limit is 10 node removals at once. |
Jamie Hannaford | 16bebfc | 2014-11-03 12:52:30 +0100 | [diff] [blame] | 134 | func BulkDelete(c *gophercloud.ServiceClient, loadBalancerID int, nodeIDs []int) DeleteResult { |
| 135 | var res DeleteResult |
| 136 | |
Jamie Hannaford | 0a9a6be | 2014-11-03 12:55:38 +0100 | [diff] [blame] | 137 | if len(nodeIDs) > 10 || len(nodeIDs) == 0 { |
| 138 | res.Err = errors.New("You must provide a minimum of 1 and a maximum of 10 node IDs") |
| 139 | return res |
| 140 | } |
| 141 | |
Jamie Hannaford | 16bebfc | 2014-11-03 12:52:30 +0100 | [diff] [blame] | 142 | url := rootURL(c, loadBalancerID) |
Jamie Hannaford | 950561c | 2014-11-12 11:12:20 +0100 | [diff] [blame] | 143 | url += gophercloud.IDSliceToQueryString("id", nodeIDs) |
Jamie Hannaford | 16bebfc | 2014-11-03 12:52:30 +0100 | [diff] [blame] | 144 | |
Jamie Hannaford | 5497f94 | 2015-03-25 11:55:51 +0100 | [diff] [blame^] | 145 | _, res.Err = c.Delete(url, nil) |
Jamie Hannaford | 16bebfc | 2014-11-03 12:52:30 +0100 | [diff] [blame] | 146 | return res |
| 147 | } |
Jamie Hannaford | 51175a0 | 2014-11-03 13:29:44 +0100 | [diff] [blame] | 148 | |
Jamie Hannaford | 8cdaa80 | 2014-11-03 15:11:39 +0100 | [diff] [blame] | 149 | // Get is the operation responsible for showing details for a single node. |
Jamie Hannaford | 51175a0 | 2014-11-03 13:29:44 +0100 | [diff] [blame] | 150 | func Get(c *gophercloud.ServiceClient, lbID, nodeID int) GetResult { |
| 151 | var res GetResult |
Jamie Hannaford | 5497f94 | 2015-03-25 11:55:51 +0100 | [diff] [blame^] | 152 | _, res.Err = c.Get(resourceURL(c, lbID, nodeID), &res.Body, nil) |
Jamie Hannaford | 51175a0 | 2014-11-03 13:29:44 +0100 | [diff] [blame] | 153 | return res |
| 154 | } |
Jamie Hannaford | 00222d7 | 2014-11-03 13:58:52 +0100 | [diff] [blame] | 155 | |
Jamie Hannaford | 8cdaa80 | 2014-11-03 15:11:39 +0100 | [diff] [blame] | 156 | // UpdateOptsBuilder represents a type that can be converted into a JSON-like |
| 157 | // map structure. |
Jamie Hannaford | 00222d7 | 2014-11-03 13:58:52 +0100 | [diff] [blame] | 158 | type UpdateOptsBuilder interface { |
| 159 | ToNodeUpdateMap() (map[string]interface{}, error) |
| 160 | } |
| 161 | |
Jamie Hannaford | 8cdaa80 | 2014-11-03 15:11:39 +0100 | [diff] [blame] | 162 | // UpdateOpts represent the options for updating an existing node. |
Jamie Hannaford | 00222d7 | 2014-11-03 13:58:52 +0100 | [diff] [blame] | 163 | type UpdateOpts struct { |
Jamie Hannaford | 8cdaa80 | 2014-11-03 15:11:39 +0100 | [diff] [blame] | 164 | // Optional - the condition of the node. See the consts in Results.go. |
Jamie Hannaford | 00222d7 | 2014-11-03 13:58:52 +0100 | [diff] [blame] | 165 | Condition Condition |
Jamie Hannaford | 8cdaa80 | 2014-11-03 15:11:39 +0100 | [diff] [blame] | 166 | |
| 167 | // Optional - the type of the node. See the consts in Results.go. |
| 168 | Type Type |
| 169 | |
| 170 | // Optional - a pointer to an integer between 0 and 100. |
| 171 | Weight *int |
Jamie Hannaford | 00222d7 | 2014-11-03 13:58:52 +0100 | [diff] [blame] | 172 | } |
| 173 | |
Jamie Hannaford | 8cdaa80 | 2014-11-03 15:11:39 +0100 | [diff] [blame] | 174 | // ToNodeUpdateMap converts an options struct into a JSON-like map. |
Jamie Hannaford | 00222d7 | 2014-11-03 13:58:52 +0100 | [diff] [blame] | 175 | func (opts UpdateOpts) ToNodeUpdateMap() (map[string]interface{}, error) { |
| 176 | node := make(map[string]interface{}) |
| 177 | |
Jamie Hannaford | 00222d7 | 2014-11-03 13:58:52 +0100 | [diff] [blame] | 178 | if opts.Condition != "" { |
| 179 | node["condition"] = opts.Condition |
| 180 | } |
| 181 | if opts.Weight != nil { |
| 182 | if weightErr := validateWeight(opts.Weight); weightErr != nil { |
| 183 | return node, weightErr |
| 184 | } |
| 185 | node["weight"] = &opts.Weight |
| 186 | } |
| 187 | if opts.Type != "" { |
| 188 | node["type"] = opts.Type |
| 189 | } |
| 190 | |
| 191 | return map[string]interface{}{"node": node}, nil |
| 192 | } |
| 193 | |
Jamie Hannaford | 8cdaa80 | 2014-11-03 15:11:39 +0100 | [diff] [blame] | 194 | // Update is the operation responsible for updating an existing node. A node's |
| 195 | // IP, port, and status are immutable attributes and cannot be modified. |
Jamie Hannaford | 00222d7 | 2014-11-03 13:58:52 +0100 | [diff] [blame] | 196 | func Update(c *gophercloud.ServiceClient, lbID, nodeID int, opts UpdateOptsBuilder) UpdateResult { |
| 197 | var res UpdateResult |
| 198 | |
| 199 | reqBody, err := opts.ToNodeUpdateMap() |
| 200 | if err != nil { |
| 201 | res.Err = err |
| 202 | return res |
| 203 | } |
| 204 | |
Jamie Hannaford | 5497f94 | 2015-03-25 11:55:51 +0100 | [diff] [blame^] | 205 | _, res.Err = c.Put(resourceURL(c, lbID, nodeID), reqBody, nil, nil) |
Jamie Hannaford | 00222d7 | 2014-11-03 13:58:52 +0100 | [diff] [blame] | 206 | return res |
| 207 | } |
Jamie Hannaford | 9f4870f | 2014-11-03 14:03:16 +0100 | [diff] [blame] | 208 | |
Jamie Hannaford | 8cdaa80 | 2014-11-03 15:11:39 +0100 | [diff] [blame] | 209 | // Delete is the operation responsible for permanently deleting a node. |
Jamie Hannaford | 9f4870f | 2014-11-03 14:03:16 +0100 | [diff] [blame] | 210 | func Delete(c *gophercloud.ServiceClient, lbID, nodeID int) DeleteResult { |
| 211 | var res DeleteResult |
Jamie Hannaford | 5497f94 | 2015-03-25 11:55:51 +0100 | [diff] [blame^] | 212 | _, res.Err = c.Delete(resourceURL(c, lbID, nodeID), nil) |
Jamie Hannaford | 9f4870f | 2014-11-03 14:03:16 +0100 | [diff] [blame] | 213 | return res |
| 214 | } |
Jamie Hannaford | 1fac9dd | 2014-11-03 14:22:40 +0100 | [diff] [blame] | 215 | |
Jamie Hannaford | 8cdaa80 | 2014-11-03 15:11:39 +0100 | [diff] [blame] | 216 | // ListEventsOptsBuilder allows extensions to add additional parameters to the |
| 217 | // List request. |
| 218 | type ListEventsOptsBuilder interface { |
| 219 | ToEventsListQuery() (string, error) |
| 220 | } |
| 221 | |
| 222 | // ListEventsOpts allows the filtering and sorting of paginated collections through |
| 223 | // the API. |
| 224 | type ListEventsOpts struct { |
| 225 | Marker string `q:"marker"` |
| 226 | Limit int `q:"limit"` |
| 227 | } |
| 228 | |
| 229 | // ToEventsListQuery formats a ListOpts into a query string. |
| 230 | func (opts ListEventsOpts) ToEventsListQuery() (string, error) { |
| 231 | q, err := gophercloud.BuildQueryString(opts) |
| 232 | if err != nil { |
| 233 | return "", err |
| 234 | } |
| 235 | return q.String(), nil |
| 236 | } |
| 237 | |
| 238 | // ListEvents is the operation responsible for listing all the events |
| 239 | // associated with the activity between the node and the load balancer. The |
| 240 | // events report errors found with the node. The detailedMessage provides the |
| 241 | // detailed reason for the error. |
Jamie Hannaford | 703527e | 2014-11-05 12:38:15 +0100 | [diff] [blame] | 242 | func ListEvents(client *gophercloud.ServiceClient, loadBalancerID int, opts ListEventsOptsBuilder) pagination.Pager { |
| 243 | url := eventsURL(client, loadBalancerID) |
Jamie Hannaford | 8cdaa80 | 2014-11-03 15:11:39 +0100 | [diff] [blame] | 244 | |
| 245 | if opts != nil { |
| 246 | query, err := opts.ToEventsListQuery() |
| 247 | if err != nil { |
| 248 | return pagination.Pager{Err: err} |
| 249 | } |
| 250 | url += query |
| 251 | } |
| 252 | |
Jamie Hannaford | 1fac9dd | 2014-11-03 14:22:40 +0100 | [diff] [blame] | 253 | return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { |
| 254 | return NodeEventPage{pagination.SinglePageBase(r)} |
| 255 | }) |
| 256 | } |