blob: 2dd15958d6c85acf4870cba871c46c066a6b44b4 [file] [log] [blame]
Jamie Hannafordfba65af2014-11-03 10:32:37 +01001package lbs
Jamie Hannaford186d4e22014-10-31 12:26:11 +01002
3import (
Jamie Hannaforde09b6822014-10-31 15:33:57 +01004 "errors"
5
Jamie Hannafordd78bb352014-11-07 16:36:09 +01006 "github.com/mitchellh/mapstructure"
Jamie Hannaforde09b6822014-10-31 15:33:57 +01007 "github.com/racker/perigee"
8
Jamie Hannaford186d4e22014-10-31 12:26:11 +01009 "github.com/rackspace/gophercloud"
10 "github.com/rackspace/gophercloud/pagination"
Jamie Hannaford940159d2014-11-03 13:04:08 +010011 "github.com/rackspace/gophercloud/rackspace/lb/v1"
Jamie Hannafordcfe2f282014-11-07 15:11:21 +010012 "github.com/rackspace/gophercloud/rackspace/lb/v1/acl"
13 "github.com/rackspace/gophercloud/rackspace/lb/v1/monitors"
Jamie Hannaford0a9a6be2014-11-03 12:55:38 +010014 "github.com/rackspace/gophercloud/rackspace/lb/v1/nodes"
Jamie Hannaford6bc93aa2014-11-06 12:37:52 +010015 "github.com/rackspace/gophercloud/rackspace/lb/v1/sessions"
Jamie Hannafordcfe2f282014-11-07 15:11:21 +010016 "github.com/rackspace/gophercloud/rackspace/lb/v1/throttle"
Jamie Hannaford1c817312014-11-04 10:56:58 +010017 "github.com/rackspace/gophercloud/rackspace/lb/v1/vips"
Jamie Hannaford186d4e22014-10-31 12:26:11 +010018)
19
Jamie Hannafordcfe2f282014-11-07 15:11:21 +010020var (
21 errNameRequired = errors.New("Name is a required attribute")
22 errTimeoutExceeded = errors.New("Timeout must be less than 120")
23)
24
Jamie Hannaford186d4e22014-10-31 12:26:11 +010025// ListOptsBuilder allows extensions to add additional parameters to the
26// List request.
27type ListOptsBuilder interface {
28 ToLBListQuery() (string, error)
29}
30
31// ListOpts allows the filtering and sorting of paginated collections through
32// the API.
33type ListOpts struct {
34 ChangesSince string `q:"changes-since"`
35 Status Status `q:"status"`
36 NodeAddr string `q:"nodeaddress"`
37 Marker string `q:"marker"`
38 Limit int `q:"limit"`
39}
40
41// ToLBListQuery formats a ListOpts into a query string.
42func (opts ListOpts) ToLBListQuery() (string, error) {
43 q, err := gophercloud.BuildQueryString(opts)
44 if err != nil {
45 return "", err
46 }
47 return q.String(), nil
48}
49
Jamie Hannafordb2007ee2014-11-03 16:24:43 +010050// List is the operation responsible for returning a paginated collection of
51// load balancers. You may pass in a ListOpts struct to filter results.
Jamie Hannaford186d4e22014-10-31 12:26:11 +010052func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
53 url := rootURL(client)
54 if opts != nil {
55 query, err := opts.ToLBListQuery()
56 if err != nil {
57 return pagination.Pager{Err: err}
58 }
59 url += query
60 }
61
62 return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
63 return LBPage{pagination.LinkedPageBase{PageResult: r}}
64 })
65}
Jamie Hannaforde09b6822014-10-31 15:33:57 +010066
Jamie Hannaforde09b6822014-10-31 15:33:57 +010067// CreateOptsBuilder is the interface options structs have to satisfy in order
68// to be used in the main Create operation in this package. Since many
69// extensions decorate or modify the common logic, it is useful for them to
70// satisfy a basic interface in order for them to be used.
71type CreateOptsBuilder interface {
72 ToLBCreateMap() (map[string]interface{}, error)
73}
74
75// CreateOpts is the common options struct used in this package's Create
76// operation.
77type CreateOpts struct {
78 // Required - name of the load balancer to create. The name must be 128
79 // characters or fewer in length, and all UTF-8 characters are valid.
80 Name string
81
82 // Optional - nodes to be added.
Jamie Hannaford0a9a6be2014-11-03 12:55:38 +010083 Nodes []nodes.Node
Jamie Hannaforde09b6822014-10-31 15:33:57 +010084
85 // Required - protocol of the service that is being load balanced.
Jamie Hannaford4ab9aea2014-11-04 14:38:06 +010086 // See http://docs.rackspace.com/loadbalancers/api/v1.0/clb-devguide/content/protocols.html
87 // for a full list of supported protocols.
88 Protocol string
Jamie Hannaforde09b6822014-10-31 15:33:57 +010089
90 // Optional - enables or disables Half-Closed support for the load balancer.
91 // Half-Closed support provides the ability for one end of the connection to
92 // terminate its output, while still receiving data from the other end. Only
93 // available for TCP/TCP_CLIENT_FIRST protocols.
Jamie Hannafordcfe2f282014-11-07 15:11:21 +010094 HalfClosed gophercloud.EnabledState
Jamie Hannaforde09b6822014-10-31 15:33:57 +010095
96 // Optional - the type of virtual IPs you want associated with the load
97 // balancer.
Jamie Hannaford1c817312014-11-04 10:56:58 +010098 VIPs []vips.VIP
Jamie Hannaforde09b6822014-10-31 15:33:57 +010099
100 // Optional - the access list management feature allows fine-grained network
101 // access controls to be applied to the load balancer virtual IP address.
Jamie Hannafordcfe2f282014-11-07 15:11:21 +0100102 AccessList *acl.AccessList
Jamie Hannaforde09b6822014-10-31 15:33:57 +0100103
104 // Optional - algorithm that defines how traffic should be directed between
105 // back-end nodes.
Jamie Hannaford46336282014-11-04 14:48:20 +0100106 Algorithm string
Jamie Hannaforde09b6822014-10-31 15:33:57 +0100107
108 // Optional - current connection logging configuration.
109 ConnectionLogging *ConnectionLogging
110
111 // Optional - specifies a limit on the number of connections per IP address
112 // to help mitigate malicious or abusive traffic to your applications.
Jamie Hannafordcfe2f282014-11-07 15:11:21 +0100113 ConnThrottle *throttle.ConnectionThrottle
Jamie Hannaforde09b6822014-10-31 15:33:57 +0100114
Jamie Hannafordcfe2f282014-11-07 15:11:21 +0100115 // Optional
116 HealthMonitor *monitors.Monitor
Jamie Hannaforde09b6822014-10-31 15:33:57 +0100117
118 // Optional - arbitrary information that can be associated with each LB.
119 Metadata map[string]interface{}
120
121 // Optional - port number for the service you are load balancing.
122 Port int
123
124 // Optional - the timeout value for the load balancer and communications with
125 // its nodes. Defaults to 30 seconds with a maximum of 120 seconds.
126 Timeout int
127
128 // Optional - specifies whether multiple requests from clients are directed
129 // to the same node.
Jamie Hannaford6bc93aa2014-11-06 12:37:52 +0100130 SessionPersistence *sessions.SessionPersistence
Jamie Hannaforde09b6822014-10-31 15:33:57 +0100131
132 // Optional - enables or disables HTTP to HTTPS redirection for the load
133 // balancer. When enabled, any HTTP request returns status code 301 (Moved
134 // Permanently), and the requester is redirected to the requested URL via the
135 // HTTPS protocol on port 443. For example, http://example.com/page.html
136 // would be redirected to https://example.com/page.html. Only available for
137 // HTTPS protocol (port=443), or HTTP protocol with a properly configured SSL
138 // termination (secureTrafficOnly=true, securePort=443).
Jamie Hannafordcfe2f282014-11-07 15:11:21 +0100139 HTTPSRedirect gophercloud.EnabledState
Jamie Hannaforde09b6822014-10-31 15:33:57 +0100140}
141
Jamie Hannaforde09b6822014-10-31 15:33:57 +0100142// ToLBCreateMap casts a CreateOpts struct to a map.
143func (opts CreateOpts) ToLBCreateMap() (map[string]interface{}, error) {
144 lb := make(map[string]interface{})
145
146 if opts.Name == "" {
147 return lb, errNameRequired
148 }
149 if opts.Timeout > 120 {
150 return lb, errTimeoutExceeded
151 }
152
153 lb["name"] = opts.Name
154
155 if len(opts.Nodes) > 0 {
156 nodes := []map[string]interface{}{}
157 for _, n := range opts.Nodes {
158 nodes = append(nodes, map[string]interface{}{
159 "address": n.Address,
160 "port": n.Port,
161 "condition": n.Condition,
162 })
163 }
164 lb["nodes"] = nodes
165 }
166
167 if opts.Protocol != "" {
168 lb["protocol"] = opts.Protocol
169 }
170 if opts.HalfClosed != nil {
171 lb["halfClosed"] = opts.HalfClosed
172 }
Jamie Hannaforde09b6822014-10-31 15:33:57 +0100173 if len(opts.VIPs) > 0 {
Jamie Hannaforde09b6822014-10-31 15:33:57 +0100174 lb["virtualIps"] = opts.VIPs
175 }
Jamie Hannafordcfe2f282014-11-07 15:11:21 +0100176 if opts.AccessList != nil {
177 lb["accessList"] = &opts.AccessList
178 }
Jamie Hannaforde09b6822014-10-31 15:33:57 +0100179 if opts.Algorithm != "" {
180 lb["algorithm"] = opts.Algorithm
181 }
182 if opts.ConnectionLogging != nil {
183 lb["connectionLogging"] = &opts.ConnectionLogging
184 }
Jamie Hannafordb2007ee2014-11-03 16:24:43 +0100185 if opts.ConnThrottle != nil {
186 lb["connectionThrottle"] = &opts.ConnThrottle
187 }
Jamie Hannafordcfe2f282014-11-07 15:11:21 +0100188 if opts.HealthMonitor != nil {
189 lb["healthMonitor"] = &opts.HealthMonitor
190 }
Jamie Hannaforde09b6822014-10-31 15:33:57 +0100191 if len(opts.Metadata) != 0 {
192 lb["metadata"] = opts.Metadata
193 }
194 if opts.Port > 0 {
195 lb["port"] = opts.Port
196 }
197 if opts.Timeout > 0 {
198 lb["timeout"] = opts.Timeout
199 }
Jamie Hannafordb2007ee2014-11-03 16:24:43 +0100200 if opts.SessionPersistence != nil {
201 lb["sessionPersistence"] = &opts.SessionPersistence
202 }
Jamie Hannaforde09b6822014-10-31 15:33:57 +0100203 if opts.HTTPSRedirect != nil {
204 lb["httpsRedirect"] = &opts.HTTPSRedirect
205 }
206
207 return map[string]interface{}{"loadBalancer": lb}, nil
208}
209
Jamie Hannafordb2007ee2014-11-03 16:24:43 +0100210// Create is the operation responsible for asynchronously provisioning a new
211// load balancer based on the configuration defined in CreateOpts. Once the
212// request is validated and progress has started on the provisioning process, a
213// response struct is returned. When extracted (with Extract()), you have
214// to the load balancer's unique ID and status.
215//
216// Once an ID is attained, you can check on the progress of the operation by
217// calling Get and passing in the ID. If the corresponding request cannot be
218// fulfilled due to insufficient or invalid data, a HTTP 400 (Bad Request)
219// error response is returned with information regarding the nature of the
220// failure in the body of the response. Failures in the validation process are
221// non-recoverable and require the caller to correct the cause of the failure.
Jamie Hannaforde09b6822014-10-31 15:33:57 +0100222func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
223 var res CreateResult
224
225 reqBody, err := opts.ToLBCreateMap()
226 if err != nil {
227 res.Err = err
228 return res
229 }
230
231 _, res.Err = perigee.Request("POST", rootURL(c), perigee.Options{
232 MoreHeaders: c.AuthenticatedHeaders(),
233 ReqBody: &reqBody,
234 Results: &res.Body,
Jamie Hannafordd56375d2014-11-05 12:38:04 +0100235 OkCodes: []int{202},
Jamie Hannaforde09b6822014-10-31 15:33:57 +0100236 })
237
238 return res
239}
Jamie Hannaford1c260332014-10-31 15:57:22 +0100240
Jamie Hannafordb2007ee2014-11-03 16:24:43 +0100241// Get is the operation responsible for providing detailed information
242// regarding a specific load balancer which is configured and associated with
243// your account. This operation is not capable of returning details for a load
244// balancer which has been deleted.
Jamie Hannaford07c06962014-10-31 16:42:03 +0100245func Get(c *gophercloud.ServiceClient, id int) GetResult {
246 var res GetResult
247
248 _, res.Err = perigee.Request("GET", resourceURL(c, id), perigee.Options{
249 MoreHeaders: c.AuthenticatedHeaders(),
250 Results: &res.Body,
251 OkCodes: []int{200},
252 })
253
254 return res
255}
256
Jamie Hannafordb2007ee2014-11-03 16:24:43 +0100257// BulkDelete removes all the load balancers referenced in the slice of IDs.
258// Any and all configuration data associated with these load balancers are
259// immediately purged and is not recoverable.
260//
261// If one of the items in the list cannot be removed due to its current status,
262// a 400 Bad Request error is returned along with the IDs of the ones the
263// system identified as potential failures for this request.
Jamie Hannaford1c260332014-10-31 15:57:22 +0100264func BulkDelete(c *gophercloud.ServiceClient, ids []int) DeleteResult {
265 var res DeleteResult
266
Jamie Hannaford0a9a6be2014-11-03 12:55:38 +0100267 if len(ids) > 10 || len(ids) == 0 {
268 res.Err = errors.New("You must provide a minimum of 1 and a maximum of 10 LB IDs")
269 return res
270 }
271
Jamie Hannaford1c260332014-10-31 15:57:22 +0100272 url := rootURL(c)
Jamie Hannaford940159d2014-11-03 13:04:08 +0100273 url += v1.IDSliceToQueryString("id", ids)
Jamie Hannaford1c260332014-10-31 15:57:22 +0100274
275 _, res.Err = perigee.Request("DELETE", url, perigee.Options{
276 MoreHeaders: c.AuthenticatedHeaders(),
277 OkCodes: []int{202},
278 })
279
280 return res
281}
Jamie Hannaford5f95e6a2014-10-31 16:13:44 +0100282
Jamie Hannafordb2007ee2014-11-03 16:24:43 +0100283// Delete removes a single load balancer.
Jamie Hannaford5f95e6a2014-10-31 16:13:44 +0100284func Delete(c *gophercloud.ServiceClient, id int) DeleteResult {
285 var res DeleteResult
286
287 _, res.Err = perigee.Request("DELETE", resourceURL(c, id), perigee.Options{
288 MoreHeaders: c.AuthenticatedHeaders(),
289 OkCodes: []int{202},
290 })
291
292 return res
293}
Jamie Hannaford76fcc832014-10-31 16:56:50 +0100294
Jamie Hannafordb2007ee2014-11-03 16:24:43 +0100295// UpdateOptsBuilder represents a type that can be converted into a JSON-like
296// map structure.
Jamie Hannaford76fcc832014-10-31 16:56:50 +0100297type UpdateOptsBuilder interface {
298 ToLBUpdateMap() (map[string]interface{}, error)
299}
300
Jamie Hannafordb2007ee2014-11-03 16:24:43 +0100301// UpdateOpts represent the options for updating an existing load balancer.
Jamie Hannaford76fcc832014-10-31 16:56:50 +0100302type UpdateOpts struct {
Jamie Hannafordb2007ee2014-11-03 16:24:43 +0100303 // Optional - new name of the load balancer.
Jamie Hannaford76fcc832014-10-31 16:56:50 +0100304 Name string
305
Jamie Hannafordb2007ee2014-11-03 16:24:43 +0100306 // Optional - the new protocol you want your load balancer to have.
Jamie Hannaford4ab9aea2014-11-04 14:38:06 +0100307 // See http://docs.rackspace.com/loadbalancers/api/v1.0/clb-devguide/content/protocols.html
308 // for a full list of supported protocols.
309 Protocol string
Jamie Hannaford76fcc832014-10-31 16:56:50 +0100310
Jamie Hannafordb2007ee2014-11-03 16:24:43 +0100311 // Optional - see the HalfClosed field in CreateOpts for more information.
Jamie Hannafordcfe2f282014-11-07 15:11:21 +0100312 HalfClosed gophercloud.EnabledState
Jamie Hannaford76fcc832014-10-31 16:56:50 +0100313
Jamie Hannafordb2007ee2014-11-03 16:24:43 +0100314 // Optional - see the Algorithm field in CreateOpts for more information.
Jamie Hannaford46336282014-11-04 14:48:20 +0100315 Algorithm string
Jamie Hannaford76fcc832014-10-31 16:56:50 +0100316
Jamie Hannafordb2007ee2014-11-03 16:24:43 +0100317 // Optional - see the Port field in CreateOpts for more information.
Jamie Hannaford76fcc832014-10-31 16:56:50 +0100318 Port int
319
Jamie Hannafordb2007ee2014-11-03 16:24:43 +0100320 // Optional - see the Timeout field in CreateOpts for more information.
Jamie Hannaford76fcc832014-10-31 16:56:50 +0100321 Timeout int
322
Jamie Hannafordb2007ee2014-11-03 16:24:43 +0100323 // Optional - see the HTTPSRedirect field in CreateOpts for more information.
Jamie Hannafordcfe2f282014-11-07 15:11:21 +0100324 HTTPSRedirect gophercloud.EnabledState
Jamie Hannaford76fcc832014-10-31 16:56:50 +0100325}
326
Jamie Hannafordb2007ee2014-11-03 16:24:43 +0100327// ToLBUpdateMap casts an UpdateOpts struct to a map.
Jamie Hannaford76fcc832014-10-31 16:56:50 +0100328func (opts UpdateOpts) ToLBUpdateMap() (map[string]interface{}, error) {
329 lb := make(map[string]interface{})
330
331 if opts.Name != "" {
332 lb["name"] = opts.Name
333 }
334 if opts.Protocol != "" {
335 lb["protocol"] = opts.Protocol
336 }
337 if opts.HalfClosed != nil {
338 lb["halfClosed"] = opts.HalfClosed
339 }
340 if opts.Algorithm != "" {
341 lb["algorithm"] = opts.Algorithm
342 }
343 if opts.Port > 0 {
344 lb["port"] = opts.Port
345 }
346 if opts.Timeout > 0 {
347 lb["timeout"] = opts.Timeout
348 }
349 if opts.HTTPSRedirect != nil {
350 lb["httpsRedirect"] = &opts.HTTPSRedirect
351 }
352
353 return map[string]interface{}{"loadBalancer": lb}, nil
354}
355
Jamie Hannafordb2007ee2014-11-03 16:24:43 +0100356// Update is the operation responsible for asynchronously updating the
357// attributes of a specific load balancer. Upon successful validation of the
358// request, the service returns a 202 Accepted response and the load balancer
359// enters a PENDING_UPDATE state. A user can poll the load balancer with Get to
360// wait for the changes to be applied. When this happens, the load balancer will
361// return to an ACTIVE state.
Jamie Hannaford76fcc832014-10-31 16:56:50 +0100362func Update(c *gophercloud.ServiceClient, id int, opts UpdateOptsBuilder) UpdateResult {
363 var res UpdateResult
364
365 reqBody, err := opts.ToLBUpdateMap()
366 if err != nil {
367 res.Err = err
368 return res
369 }
370
371 _, res.Err = perigee.Request("PUT", resourceURL(c, id), perigee.Options{
372 MoreHeaders: c.AuthenticatedHeaders(),
373 ReqBody: &reqBody,
Jamie Hannafordd56375d2014-11-05 12:38:04 +0100374 OkCodes: []int{202},
Jamie Hannaford76fcc832014-10-31 16:56:50 +0100375 })
376
377 return res
378}
Jamie Hannaford4ab9aea2014-11-04 14:38:06 +0100379
380// ListProtocols is the operation responsible for returning a paginated
381// collection of load balancer protocols.
382func ListProtocols(client *gophercloud.ServiceClient) pagination.Pager {
383 url := protocolsURL(client)
384 return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
385 return ProtocolPage{pagination.SinglePageBase(r)}
386 })
387}
Jamie Hannaford46336282014-11-04 14:48:20 +0100388
389// ListAlgorithms is the operation responsible for returning a paginated
390// collection of load balancer algorithms.
391func ListAlgorithms(client *gophercloud.ServiceClient) pagination.Pager {
392 url := algorithmsURL(client)
393 return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
394 return AlgorithmPage{pagination.SinglePageBase(r)}
395 })
396}
Jamie Hannafordd78bb352014-11-07 16:36:09 +0100397
398// IsLoggingEnabled returns true if the load balancer has connection logging
399// enabled and false if not.
400func IsLoggingEnabled(client *gophercloud.ServiceClient, id int) (bool, error) {
401 var body interface{}
402
403 _, err := perigee.Request("GET", loggingURL(client, id), perigee.Options{
404 MoreHeaders: client.AuthenticatedHeaders(),
405 Results: &body,
406 OkCodes: []int{200},
407 })
408 if err != nil {
409 return false, err
410 }
411
412 var resp struct {
413 CL struct {
414 Enabled bool `mapstructure:"enabled"`
415 } `mapstructure:"connectionLogging"`
416 }
417
418 err = mapstructure.Decode(body, &resp)
419 return resp.CL.Enabled, err
420}
421
422func toConnLoggingMap(state bool) map[string]map[string]bool {
423 return map[string]map[string]bool{
424 "connectionLogging": map[string]bool{"enabled": false},
425 }
426}
427
428// EnableLogging will enable connection logging for a specified load balancer.
429func EnableLogging(client *gophercloud.ServiceClient, id int) gophercloud.ErrResult {
430 reqBody := toConnLoggingMap(true)
431 var res gophercloud.ErrResult
432
433 _, res.Err = perigee.Request("GET", loggingURL(client, id), perigee.Options{
434 MoreHeaders: client.AuthenticatedHeaders(),
435 ReqBody: &reqBody,
436 OkCodes: []int{200},
437 })
438
439 return res
440}
441
442// DisableLogging will disable connection logging for a specified load balancer.
443func DisableLogging(client *gophercloud.ServiceClient, id int) gophercloud.ErrResult {
444 reqBody := toConnLoggingMap(false)
445 var res gophercloud.ErrResult
446
447 _, res.Err = perigee.Request("GET", loggingURL(client, id), perigee.Options{
448 MoreHeaders: client.AuthenticatedHeaders(),
449 ReqBody: &reqBody,
450 OkCodes: []int{200},
451 })
452
453 return res
454}
Jamie Hannafordda45b422014-11-10 11:00:38 +0100455
456func GetErrorPage(client *gophercloud.ServiceClient, id int) ErrorPageResult {
457 var res ErrorPageResult
458
459 _, res.Err = perigee.Request("GET", errorPageURL(client, id), perigee.Options{
460 MoreHeaders: client.AuthenticatedHeaders(),
461 Results: &res.Body,
462 OkCodes: []int{200},
463 })
464
465 return res
466}
467
468func SetErrorPage(client *gophercloud.ServiceClient, id int, html string) ErrorPageResult {
469 var res ErrorPageResult
470
471 type stringMap map[string]string
472 reqBody := map[string]stringMap{"errorpage": stringMap{"content": html}}
473
474 _, res.Err = perigee.Request("PUT", errorPageURL(client, id), perigee.Options{
475 MoreHeaders: client.AuthenticatedHeaders(),
476 Results: &res.Body,
477 ReqBody: &reqBody,
478 OkCodes: []int{200},
479 })
480
481 return res
482}
483
484func DeleteErrorPage(client *gophercloud.ServiceClient, id int) gophercloud.ErrResult {
485 var res gophercloud.ErrResult
486
487 _, res.Err = perigee.Request("DELETE", errorPageURL(client, id), perigee.Options{
488 MoreHeaders: client.AuthenticatedHeaders(),
489 OkCodes: []int{200},
490 })
491
492 return res
493}