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