mirror of https://github.com/hashicorp/consul
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
314 lines
11 KiB
314 lines
11 KiB
package godo |
|
|
|
import ( |
|
"context" |
|
"fmt" |
|
"net/http" |
|
) |
|
|
|
const loadBalancersBasePath = "/v2/load_balancers" |
|
const forwardingRulesPath = "forwarding_rules" |
|
|
|
const dropletsPath = "droplets" |
|
|
|
// LoadBalancersService is an interface for managing load balancers with the DigitalOcean API. |
|
// See: https://developers.digitalocean.com/documentation/v2#load-balancers |
|
type LoadBalancersService interface { |
|
Get(context.Context, string) (*LoadBalancer, *Response, error) |
|
List(context.Context, *ListOptions) ([]LoadBalancer, *Response, error) |
|
Create(context.Context, *LoadBalancerRequest) (*LoadBalancer, *Response, error) |
|
Update(ctx context.Context, lbID string, lbr *LoadBalancerRequest) (*LoadBalancer, *Response, error) |
|
Delete(ctx context.Context, lbID string) (*Response, error) |
|
AddDroplets(ctx context.Context, lbID string, dropletIDs ...int) (*Response, error) |
|
RemoveDroplets(ctx context.Context, lbID string, dropletIDs ...int) (*Response, error) |
|
AddForwardingRules(ctx context.Context, lbID string, rules ...ForwardingRule) (*Response, error) |
|
RemoveForwardingRules(ctx context.Context, lbID string, rules ...ForwardingRule) (*Response, error) |
|
} |
|
|
|
// LoadBalancer represents a DigitalOcean load balancer configuration. |
|
// Tags can only be provided upon the creation of a Load Balancer. |
|
type LoadBalancer struct { |
|
ID string `json:"id,omitempty"` |
|
Name string `json:"name,omitempty"` |
|
IP string `json:"ip,omitempty"` |
|
Algorithm string `json:"algorithm,omitempty"` |
|
Status string `json:"status,omitempty"` |
|
Created string `json:"created_at,omitempty"` |
|
ForwardingRules []ForwardingRule `json:"forwarding_rules,omitempty"` |
|
HealthCheck *HealthCheck `json:"health_check,omitempty"` |
|
StickySessions *StickySessions `json:"sticky_sessions,omitempty"` |
|
Region *Region `json:"region,omitempty"` |
|
DropletIDs []int `json:"droplet_ids,omitempty"` |
|
Tag string `json:"tag,omitempty"` |
|
Tags []string `json:"tags,omitempty"` |
|
RedirectHttpToHttps bool `json:"redirect_http_to_https,omitempty"` |
|
EnableProxyProtocol bool `json:"enable_proxy_protocol,omitempty"` |
|
} |
|
|
|
// String creates a human-readable description of a LoadBalancer. |
|
func (l LoadBalancer) String() string { |
|
return Stringify(l) |
|
} |
|
|
|
func (l LoadBalancer) URN() string { |
|
return ToURN("LoadBalancer", l.ID) |
|
} |
|
|
|
// AsRequest creates a LoadBalancerRequest that can be submitted to Update with the current values of the LoadBalancer. |
|
// Modifying the returned LoadBalancerRequest will not modify the original LoadBalancer. |
|
func (l LoadBalancer) AsRequest() *LoadBalancerRequest { |
|
r := LoadBalancerRequest{ |
|
Name: l.Name, |
|
Algorithm: l.Algorithm, |
|
ForwardingRules: append([]ForwardingRule(nil), l.ForwardingRules...), |
|
DropletIDs: append([]int(nil), l.DropletIDs...), |
|
Tag: l.Tag, |
|
RedirectHttpToHttps: l.RedirectHttpToHttps, |
|
EnableProxyProtocol: l.EnableProxyProtocol, |
|
HealthCheck: l.HealthCheck, |
|
} |
|
|
|
if l.HealthCheck != nil { |
|
r.HealthCheck = &HealthCheck{} |
|
*r.HealthCheck = *l.HealthCheck |
|
} |
|
if l.StickySessions != nil { |
|
r.StickySessions = &StickySessions{} |
|
*r.StickySessions = *l.StickySessions |
|
} |
|
if l.Region != nil { |
|
r.Region = l.Region.Slug |
|
} |
|
return &r |
|
} |
|
|
|
// ForwardingRule represents load balancer forwarding rules. |
|
type ForwardingRule struct { |
|
EntryProtocol string `json:"entry_protocol,omitempty"` |
|
EntryPort int `json:"entry_port,omitempty"` |
|
TargetProtocol string `json:"target_protocol,omitempty"` |
|
TargetPort int `json:"target_port,omitempty"` |
|
CertificateID string `json:"certificate_id,omitempty"` |
|
TlsPassthrough bool `json:"tls_passthrough,omitempty"` |
|
} |
|
|
|
// String creates a human-readable description of a ForwardingRule. |
|
func (f ForwardingRule) String() string { |
|
return Stringify(f) |
|
} |
|
|
|
// HealthCheck represents optional load balancer health check rules. |
|
type HealthCheck struct { |
|
Protocol string `json:"protocol,omitempty"` |
|
Port int `json:"port,omitempty"` |
|
Path string `json:"path,omitempty"` |
|
CheckIntervalSeconds int `json:"check_interval_seconds,omitempty"` |
|
ResponseTimeoutSeconds int `json:"response_timeout_seconds,omitempty"` |
|
HealthyThreshold int `json:"healthy_threshold,omitempty"` |
|
UnhealthyThreshold int `json:"unhealthy_threshold,omitempty"` |
|
} |
|
|
|
// String creates a human-readable description of a HealthCheck. |
|
func (h HealthCheck) String() string { |
|
return Stringify(h) |
|
} |
|
|
|
// StickySessions represents optional load balancer session affinity rules. |
|
type StickySessions struct { |
|
Type string `json:"type,omitempty"` |
|
CookieName string `json:"cookie_name,omitempty"` |
|
CookieTtlSeconds int `json:"cookie_ttl_seconds,omitempty"` |
|
} |
|
|
|
// String creates a human-readable description of a StickySessions instance. |
|
func (s StickySessions) String() string { |
|
return Stringify(s) |
|
} |
|
|
|
// LoadBalancerRequest represents the configuration to be applied to an existing or a new load balancer. |
|
type LoadBalancerRequest struct { |
|
Name string `json:"name,omitempty"` |
|
Algorithm string `json:"algorithm,omitempty"` |
|
Region string `json:"region,omitempty"` |
|
ForwardingRules []ForwardingRule `json:"forwarding_rules,omitempty"` |
|
HealthCheck *HealthCheck `json:"health_check,omitempty"` |
|
StickySessions *StickySessions `json:"sticky_sessions,omitempty"` |
|
DropletIDs []int `json:"droplet_ids,omitempty"` |
|
Tag string `json:"tag,omitempty"` |
|
Tags []string `json:"tags,omitempty"` |
|
RedirectHttpToHttps bool `json:"redirect_http_to_https,omitempty"` |
|
EnableProxyProtocol bool `json:"enable_proxy_protocol,omitempty"` |
|
} |
|
|
|
// String creates a human-readable description of a LoadBalancerRequest. |
|
func (l LoadBalancerRequest) String() string { |
|
return Stringify(l) |
|
} |
|
|
|
type forwardingRulesRequest struct { |
|
Rules []ForwardingRule `json:"forwarding_rules,omitempty"` |
|
} |
|
|
|
func (l forwardingRulesRequest) String() string { |
|
return Stringify(l) |
|
} |
|
|
|
type dropletIDsRequest struct { |
|
IDs []int `json:"droplet_ids,omitempty"` |
|
} |
|
|
|
func (l dropletIDsRequest) String() string { |
|
return Stringify(l) |
|
} |
|
|
|
type loadBalancersRoot struct { |
|
LoadBalancers []LoadBalancer `json:"load_balancers"` |
|
Links *Links `json:"links"` |
|
} |
|
|
|
type loadBalancerRoot struct { |
|
LoadBalancer *LoadBalancer `json:"load_balancer"` |
|
} |
|
|
|
// LoadBalancersServiceOp handles communication with load balancer-related methods of the DigitalOcean API. |
|
type LoadBalancersServiceOp struct { |
|
client *Client |
|
} |
|
|
|
var _ LoadBalancersService = &LoadBalancersServiceOp{} |
|
|
|
// Get an existing load balancer by its identifier. |
|
func (l *LoadBalancersServiceOp) Get(ctx context.Context, lbID string) (*LoadBalancer, *Response, error) { |
|
path := fmt.Sprintf("%s/%s", loadBalancersBasePath, lbID) |
|
|
|
req, err := l.client.NewRequest(ctx, http.MethodGet, path, nil) |
|
if err != nil { |
|
return nil, nil, err |
|
} |
|
|
|
root := new(loadBalancerRoot) |
|
resp, err := l.client.Do(ctx, req, root) |
|
if err != nil { |
|
return nil, resp, err |
|
} |
|
|
|
return root.LoadBalancer, resp, err |
|
} |
|
|
|
// List load balancers, with optional pagination. |
|
func (l *LoadBalancersServiceOp) List(ctx context.Context, opt *ListOptions) ([]LoadBalancer, *Response, error) { |
|
path, err := addOptions(loadBalancersBasePath, opt) |
|
if err != nil { |
|
return nil, nil, err |
|
} |
|
|
|
req, err := l.client.NewRequest(ctx, http.MethodGet, path, nil) |
|
if err != nil { |
|
return nil, nil, err |
|
} |
|
|
|
root := new(loadBalancersRoot) |
|
resp, err := l.client.Do(ctx, req, root) |
|
if err != nil { |
|
return nil, resp, err |
|
} |
|
if l := root.Links; l != nil { |
|
resp.Links = l |
|
} |
|
|
|
return root.LoadBalancers, resp, err |
|
} |
|
|
|
// Create a new load balancer with a given configuration. |
|
func (l *LoadBalancersServiceOp) Create(ctx context.Context, lbr *LoadBalancerRequest) (*LoadBalancer, *Response, error) { |
|
req, err := l.client.NewRequest(ctx, http.MethodPost, loadBalancersBasePath, lbr) |
|
if err != nil { |
|
return nil, nil, err |
|
} |
|
|
|
root := new(loadBalancerRoot) |
|
resp, err := l.client.Do(ctx, req, root) |
|
if err != nil { |
|
return nil, resp, err |
|
} |
|
|
|
return root.LoadBalancer, resp, err |
|
} |
|
|
|
// Update an existing load balancer with new configuration. |
|
func (l *LoadBalancersServiceOp) Update(ctx context.Context, lbID string, lbr *LoadBalancerRequest) (*LoadBalancer, *Response, error) { |
|
path := fmt.Sprintf("%s/%s", loadBalancersBasePath, lbID) |
|
|
|
req, err := l.client.NewRequest(ctx, "PUT", path, lbr) |
|
if err != nil { |
|
return nil, nil, err |
|
} |
|
|
|
root := new(loadBalancerRoot) |
|
resp, err := l.client.Do(ctx, req, root) |
|
if err != nil { |
|
return nil, resp, err |
|
} |
|
|
|
return root.LoadBalancer, resp, err |
|
} |
|
|
|
// Delete a load balancer by its identifier. |
|
func (l *LoadBalancersServiceOp) Delete(ctx context.Context, ldID string) (*Response, error) { |
|
path := fmt.Sprintf("%s/%s", loadBalancersBasePath, ldID) |
|
|
|
req, err := l.client.NewRequest(ctx, http.MethodDelete, path, nil) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
return l.client.Do(ctx, req, nil) |
|
} |
|
|
|
// AddDroplets adds droplets to a load balancer. |
|
func (l *LoadBalancersServiceOp) AddDroplets(ctx context.Context, lbID string, dropletIDs ...int) (*Response, error) { |
|
path := fmt.Sprintf("%s/%s/%s", loadBalancersBasePath, lbID, dropletsPath) |
|
|
|
req, err := l.client.NewRequest(ctx, http.MethodPost, path, &dropletIDsRequest{IDs: dropletIDs}) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
return l.client.Do(ctx, req, nil) |
|
} |
|
|
|
// RemoveDroplets removes droplets from a load balancer. |
|
func (l *LoadBalancersServiceOp) RemoveDroplets(ctx context.Context, lbID string, dropletIDs ...int) (*Response, error) { |
|
path := fmt.Sprintf("%s/%s/%s", loadBalancersBasePath, lbID, dropletsPath) |
|
|
|
req, err := l.client.NewRequest(ctx, http.MethodDelete, path, &dropletIDsRequest{IDs: dropletIDs}) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
return l.client.Do(ctx, req, nil) |
|
} |
|
|
|
// AddForwardingRules adds forwarding rules to a load balancer. |
|
func (l *LoadBalancersServiceOp) AddForwardingRules(ctx context.Context, lbID string, rules ...ForwardingRule) (*Response, error) { |
|
path := fmt.Sprintf("%s/%s/%s", loadBalancersBasePath, lbID, forwardingRulesPath) |
|
|
|
req, err := l.client.NewRequest(ctx, http.MethodPost, path, &forwardingRulesRequest{Rules: rules}) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
return l.client.Do(ctx, req, nil) |
|
} |
|
|
|
// RemoveForwardingRules removes forwarding rules from a load balancer. |
|
func (l *LoadBalancersServiceOp) RemoveForwardingRules(ctx context.Context, lbID string, rules ...ForwardingRule) (*Response, error) { |
|
path := fmt.Sprintf("%s/%s/%s", loadBalancersBasePath, lbID, forwardingRulesPath) |
|
|
|
req, err := l.client.NewRequest(ctx, http.MethodDelete, path, &forwardingRulesRequest{Rules: rules}) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
return l.client.Do(ctx, req, nil) |
|
}
|
|
|