Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for LoadBalancers #125

Merged
merged 1 commit into from
Feb 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
vendor/
2 changes: 2 additions & 0 deletions godo.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type Client struct {
Storage StorageService
StorageActions StorageActionsService
Tags TagsService
LoadBalancers LoadBalancersService

// Optional function called after every successful request made to the DO APIs
onRequestCompleted RequestCompletionCallback
Expand Down Expand Up @@ -167,6 +168,7 @@ func NewClient(httpClient *http.Client) *Client {
c.Storage = &StorageServiceOp{client: c}
c.StorageActions = &StorageActionsServiceOp{client: c}
c.Tags = &TagsServiceOp{client: c}
c.LoadBalancers = &LoadBalancersServiceOp{client: c}

return c
}
Expand Down
274 changes: 274 additions & 0 deletions load_balancers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
package godo

import (
"fmt"
)

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(lbID string) (*LoadBalancer, *Response, error)
List(opt *ListOptions) ([]LoadBalancer, *Response, error)
Create(lbr *LoadBalancerRequest) (*LoadBalancer, *Response, error)
Update(lbID string, lbr *LoadBalancerRequest) (*LoadBalancer, *Response, error)
Delete(lbID string) (*Response, error)
AddDroplets(lbID string, dropletIDs ...int) (*Response, error)
RemoveDroplets(lbID string, dropletIDs ...int) (*Response, error)
AddForwardingRules(lbID string, rules ...ForwardingRule) (*Response, error)
RemoveForwardingRules(lbID string, rules ...ForwardingRule) (*Response, error)
}

// LoadBalancer represents a DigitalOcean load balancer configuration.
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"`
RedirectHttpToHttps bool `json:"redirect_http_to_https,omitempty"`
}

// String creates a human-readable description of a LoadBalancer.
func (l LoadBalancer) String() string {
return Stringify(l)
}

// 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"`
RedirectHttpToHttps bool `json:"redirect_http_to_https,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(lbID string) (*LoadBalancer, *Response, error) {
path := fmt.Sprintf("%s/%s", loadBalancersBasePath, lbID)

req, err := l.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}

root := new(loadBalancerRoot)
resp, err := l.client.Do(req, root)
if err != nil {
return nil, resp, err
}

return root.LoadBalancer, resp, err
}

// List load balancers, with optional pagination.
func (l *LoadBalancersServiceOp) List(opt *ListOptions) ([]LoadBalancer, *Response, error) {
path, err := addOptions(loadBalancersBasePath, opt)
if err != nil {
return nil, nil, err
}

req, err := l.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}

root := new(loadBalancersRoot)
resp, err := l.client.Do(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(lbr *LoadBalancerRequest) (*LoadBalancer, *Response, error) {
req, err := l.client.NewRequest("POST", loadBalancersBasePath, lbr)
if err != nil {
return nil, nil, err
}

root := new(loadBalancerRoot)
resp, err := l.client.Do(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(lbID string, lbr *LoadBalancerRequest) (*LoadBalancer, *Response, error) {
path := fmt.Sprintf("%s/%s", loadBalancersBasePath, lbID)

req, err := l.client.NewRequest("PUT", path, lbr)
if err != nil {
return nil, nil, err
}

root := new(loadBalancerRoot)
resp, err := l.client.Do(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(ldID string) (*Response, error) {
path := fmt.Sprintf("%s/%s", loadBalancersBasePath, ldID)

req, err := l.client.NewRequest("DELETE", path, nil)
if err != nil {
return nil, err
}

return l.client.Do(req, nil)
}

// AddDroplets adds droplets to a load balancer.
func (l *LoadBalancersServiceOp) AddDroplets(lbID string, dropletIDs ...int) (*Response, error) {
path := fmt.Sprintf("%s/%s/%s", loadBalancersBasePath, lbID, dropletsPath)

req, err := l.client.NewRequest("POST", path, &dropletIDsRequest{IDs: dropletIDs})
if err != nil {
return nil, err
}

return l.client.Do(req, nil)
}

// RemoveDroplets removes droplets from a load balancer.
func (l *LoadBalancersServiceOp) RemoveDroplets(lbID string, dropletIDs ...int) (*Response, error) {
path := fmt.Sprintf("%s/%s/%s", loadBalancersBasePath, lbID, dropletsPath)

req, err := l.client.NewRequest("DELETE", path, &dropletIDsRequest{IDs: dropletIDs})
if err != nil {
return nil, err
}

return l.client.Do(req, nil)
}

// AddForwardingRules adds forwarding rules to a load balancer.
func (l *LoadBalancersServiceOp) AddForwardingRules(lbID string, rules ...ForwardingRule) (*Response, error) {
path := fmt.Sprintf("%s/%s/%s", loadBalancersBasePath, lbID, forwardingRulesPath)

req, err := l.client.NewRequest("POST", path, &forwardingRulesRequest{Rules: rules})
if err != nil {
return nil, err
}

return l.client.Do(req, nil)
}

// RemoveForwardingRules removes forwarding rules from a load balancer.
func (l *LoadBalancersServiceOp) RemoveForwardingRules(lbID string, rules ...ForwardingRule) (*Response, error) {
path := fmt.Sprintf("%s/%s/%s", loadBalancersBasePath, lbID, forwardingRulesPath)

req, err := l.client.NewRequest("DELETE", path, &forwardingRulesRequest{Rules: rules})
if err != nil {
return nil, err
}

return l.client.Do(req, nil)
}
Loading