load_balancer.go raw

   1  package govultr
   2  
   3  import (
   4  	"context"
   5  	"fmt"
   6  	"net/http"
   7  
   8  	"github.com/google/go-querystring/query"
   9  )
  10  
  11  const lbPath = "/v2/load-balancers"
  12  
  13  // LoadBalancerService is the interface to interact with the server endpoints on the Vultr API
  14  // Link : https://www.vultr.com/api/#tag/load-balancer
  15  type LoadBalancerService interface {
  16  	Create(ctx context.Context, createReq *LoadBalancerReq) (*LoadBalancer, *http.Response, error)
  17  	Get(ctx context.Context, lbID string) (*LoadBalancer, *http.Response, error)
  18  	Update(ctx context.Context, lbID string, updateReq *LoadBalancerReq) error
  19  	Delete(ctx context.Context, lbID string) error
  20  	DeleteSSL(ctx context.Context, lbID string) error
  21  	DeleteAutoSSL(ctx context.Context, lbID string) error
  22  	List(ctx context.Context, options *ListOptions) ([]LoadBalancer, *Meta, *http.Response, error)
  23  	CreateForwardingRule(ctx context.Context, lbID string, rule *ForwardingRule) (*ForwardingRule, *http.Response, error)
  24  	GetForwardingRule(ctx context.Context, lbID string, ruleID string) (*ForwardingRule, *http.Response, error)
  25  	DeleteForwardingRule(ctx context.Context, lbID string, RuleID string) error
  26  	ListForwardingRules(ctx context.Context, lbID string, options *ListOptions) ([]ForwardingRule, *Meta, *http.Response, error)
  27  	ListFirewallRules(ctx context.Context, lbID string, options *ListOptions) ([]LBFirewallRule, *Meta, *http.Response, error)
  28  	GetFirewallRule(ctx context.Context, lbID string, ruleID string) (*LBFirewallRule, *http.Response, error)
  29  }
  30  
  31  // LoadBalancerHandler handles interaction with the server methods for the Vultr API
  32  type LoadBalancerHandler struct {
  33  	client *Client
  34  }
  35  
  36  // LoadBalancer represent the structure of a load balancer
  37  type LoadBalancer struct {
  38  	ID              string           `json:"id,omitempty"`
  39  	DateCreated     string           `json:"date_created,omitempty"`
  40  	Region          string           `json:"region,omitempty"`
  41  	Label           string           `json:"label,omitempty"`
  42  	Status          string           `json:"status,omitempty"`
  43  	IPV4            string           `json:"ipv4,omitempty"`
  44  	IPV6            string           `json:"ipv6,omitempty"`
  45  	Instances       []string         `json:"instances,omitempty"`
  46  	Nodes           int              `json:"nodes,omitempty"`
  47  	HealthCheck     *HealthCheck     `json:"health_check,omitempty"`
  48  	GenericInfo     *GenericInfo     `json:"generic_info,omitempty"`
  49  	SSLInfo         *bool            `json:"has_ssl,omitempty"`
  50  	AutoSSL         *AutoSSL         `json:"auto_ssl,omitempty"`
  51  	HTTP2           *bool            `json:"http2,omitempty"`
  52  	HTTP3           *bool            `json:"http3,omitempty"`
  53  	ForwardingRules []ForwardingRule `json:"forwarding_rules,omitempty"`
  54  	FirewallRules   []LBFirewallRule `json:"firewall_rules,omitempty"`
  55  	GlobalRegions   []string         `json:"global_regions,omitempty"`
  56  }
  57  
  58  // LoadBalancerReq gives options for creating or updating a load balancer
  59  type LoadBalancerReq struct {
  60  	Region             string           `json:"region,omitempty"`
  61  	Label              string           `json:"label,omitempty"`
  62  	Instances          []string         `json:"instances,omitempty"`
  63  	Nodes              int              `json:"nodes,omitempty"`
  64  	HealthCheck        *HealthCheck     `json:"health_check,omitempty"`
  65  	StickySessions     *StickySessions  `json:"sticky_session,omitempty"`
  66  	ForwardingRules    []ForwardingRule `json:"forwarding_rules,omitempty"`
  67  	SSL                *SSL             `json:"ssl,omitempty"`
  68  	AutoSSL            *AutoSSL         `json:"auto_ssl,omitempty"`
  69  	SSLRedirect        *bool            `json:"ssl_redirect,omitempty"`
  70  	HTTP2              *bool            `json:"http2,omitempty"`
  71  	HTTP3              *bool            `json:"http3,omitempty"`
  72  	ProxyProtocol      *bool            `json:"proxy_protocol,omitempty"`
  73  	BalancingAlgorithm string           `json:"balancing_algorithm,omitempty"`
  74  	FirewallRules      []LBFirewallRule `json:"firewall_rules,omitempty"`
  75  	Timeout            int              `json:"timeout,omitempty"`
  76  	VPC                *string          `json:"vpc,omitempty"`
  77  	GlobalRegions      []string         `json:"global_regions,omitempty"`
  78  }
  79  
  80  // InstanceList represents instances that are attached to your load balancer
  81  type InstanceList struct {
  82  	InstanceList []string
  83  }
  84  
  85  // HealthCheck represents your health check configuration for your load balancer.
  86  type HealthCheck struct {
  87  	Protocol           string `json:"protocol,omitempty"`
  88  	Port               int    `json:"port,omitempty"`
  89  	Path               string `json:"path,omitempty"`
  90  	CheckInterval      int    `json:"check_interval,omitempty"`
  91  	ResponseTimeout    int    `json:"response_timeout,omitempty"`
  92  	UnhealthyThreshold int    `json:"unhealthy_threshold,omitempty"`
  93  	HealthyThreshold   int    `json:"healthy_threshold,omitempty"`
  94  }
  95  
  96  // GenericInfo represents generic configuration of your load balancer
  97  type GenericInfo struct {
  98  	BalancingAlgorithm string          `json:"balancing_algorithm,omitempty"`
  99  	Timeout            int             `json:"timeout,omitempty"`
 100  	SSLRedirect        *bool           `json:"ssl_redirect,omitempty"`
 101  	StickySessions     *StickySessions `json:"sticky_sessions,omitempty"`
 102  	ProxyProtocol      *bool           `json:"proxy_protocol,omitempty"`
 103  	VPC                string          `json:"vpc,omitempty"`
 104  }
 105  
 106  // StickySessions represents cookie for your load balancer
 107  type StickySessions struct {
 108  	CookieName string `json:"cookie_name,omitempty"`
 109  }
 110  
 111  // ForwardingRules represent a list of forwarding rules
 112  type ForwardingRules struct {
 113  	ForwardRuleList []ForwardingRule `json:"forwarding_rules,omitempty"`
 114  }
 115  
 116  // ForwardingRule represent a single forwarding rule
 117  type ForwardingRule struct {
 118  	RuleID           string `json:"id,omitempty"`
 119  	FrontendProtocol string `json:"frontend_protocol,omitempty"`
 120  	FrontendPort     int    `json:"frontend_port,omitempty"`
 121  	BackendProtocol  string `json:"backend_protocol,omitempty"`
 122  	BackendPort      int    `json:"backend_port,omitempty"`
 123  }
 124  
 125  // LBFirewallRule represent a single firewall rule
 126  type LBFirewallRule struct {
 127  	RuleID string `json:"id,omitempty"`
 128  	Port   int    `json:"port,omitempty"`
 129  	IPType string `json:"ip_type,omitempty"`
 130  	Source string `json:"source,omitempty"`
 131  }
 132  
 133  // SSL represents valid SSL config
 134  type SSL struct {
 135  	PrivateKey     string `json:"private_key,omitempty"`
 136  	Certificate    string `json:"certificate,omitempty"`
 137  	Chain          string `json:"chain,omitempty"`
 138  	PrivateKeyB64  string `json:"private_key_b64,omitempty"`
 139  	CertificateB64 string `json:"certificate_b64,omitempty"`
 140  	ChainB64       string `json:"chain_b64,omitempty"`
 141  }
 142  
 143  // AutoSSL represents valid AutoSSL config
 144  type AutoSSL struct {
 145  	DomainZone string `json:"domain_zone"`
 146  	DomainSub  string `json:"domain_sub,omitempty"`
 147  	Domain     string `json:"domain,omitempty"`
 148  }
 149  
 150  type lbsBase struct {
 151  	LoadBalancers []LoadBalancer `json:"load_balancers"`
 152  	Meta          *Meta          `json:"meta"`
 153  }
 154  
 155  type lbBase struct {
 156  	LoadBalancer *LoadBalancer `json:"load_balancer"`
 157  }
 158  
 159  type lbRulesBase struct {
 160  	ForwardingRules []ForwardingRule `json:"forwarding_rules"`
 161  	Meta            *Meta            `json:"meta"`
 162  }
 163  
 164  type lbRuleBase struct {
 165  	ForwardingRule *ForwardingRule `json:"forwarding_rule"`
 166  }
 167  
 168  type lbFWRulesBase struct {
 169  	FirewallRules []LBFirewallRule `json:"firewall_rules"`
 170  	Meta          *Meta            `json:"meta"`
 171  }
 172  
 173  type lbFWRuleBase struct {
 174  	FirewallRule *LBFirewallRule `json:"firewall_rule"`
 175  }
 176  
 177  // Create a load balancer
 178  func (l *LoadBalancerHandler) Create(ctx context.Context, createReq *LoadBalancerReq) (*LoadBalancer, *http.Response, error) {
 179  	req, err := l.client.NewRequest(ctx, http.MethodPost, lbPath, createReq)
 180  	if err != nil {
 181  		return nil, nil, err
 182  	}
 183  
 184  	var lb = new(lbBase)
 185  	resp, err := l.client.DoWithContext(ctx, req, &lb)
 186  	if err != nil {
 187  		return nil, resp, err
 188  	}
 189  
 190  	return lb.LoadBalancer, resp, nil
 191  }
 192  
 193  // Get a load balancer
 194  func (l *LoadBalancerHandler) Get(ctx context.Context, lbID string) (*LoadBalancer, *http.Response, error) {
 195  	uri := fmt.Sprintf("%s/%s", lbPath, lbID)
 196  	req, err := l.client.NewRequest(ctx, http.MethodGet, uri, nil)
 197  	if err != nil {
 198  		return nil, nil, err
 199  	}
 200  
 201  	var lb = new(lbBase)
 202  	resp, err := l.client.DoWithContext(ctx, req, lb)
 203  	if err != nil {
 204  		return nil, resp, err
 205  	}
 206  
 207  	return lb.LoadBalancer, resp, nil
 208  }
 209  
 210  // Update updates your your load balancer
 211  func (l *LoadBalancerHandler) Update(ctx context.Context, lbID string, updateReq *LoadBalancerReq) error {
 212  	uri := fmt.Sprintf("%s/%s", lbPath, lbID)
 213  	req, err := l.client.NewRequest(ctx, http.MethodPatch, uri, updateReq)
 214  	if err != nil {
 215  		return err
 216  	}
 217  
 218  	_, err = l.client.DoWithContext(ctx, req, nil)
 219  	return err
 220  }
 221  
 222  // Delete a load balancer subscription.
 223  func (l *LoadBalancerHandler) Delete(ctx context.Context, lbID string) error {
 224  	uri := fmt.Sprintf("%s/%s", lbPath, lbID)
 225  	req, err := l.client.NewRequest(ctx, http.MethodDelete, uri, nil)
 226  	if err != nil {
 227  		return err
 228  	}
 229  	_, err = l.client.DoWithContext(ctx, req, nil)
 230  	return err
 231  }
 232  
 233  // List all load balancer subscriptions on the current account.
 234  func (l *LoadBalancerHandler) List(ctx context.Context, options *ListOptions) ([]LoadBalancer, *Meta, *http.Response, error) { //nolint:dupl
 235  	req, err := l.client.NewRequest(ctx, http.MethodGet, lbPath, nil)
 236  	if err != nil {
 237  		return nil, nil, nil, err
 238  	}
 239  
 240  	newValues, err := query.Values(options)
 241  	if err != nil {
 242  		return nil, nil, nil, err
 243  	}
 244  
 245  	req.URL.RawQuery = newValues.Encode()
 246  
 247  	lbs := new(lbsBase)
 248  	resp, err := l.client.DoWithContext(ctx, req, &lbs)
 249  	if err != nil {
 250  		return nil, nil, resp, err
 251  	}
 252  
 253  	return lbs.LoadBalancers, lbs.Meta, resp, nil
 254  }
 255  
 256  // CreateForwardingRule will create a new forwarding rule for your load balancer subscription.
 257  // Note the RuleID will be returned in the ForwardingRule struct
 258  func (l *LoadBalancerHandler) CreateForwardingRule(ctx context.Context, lbID string, rule *ForwardingRule) (*ForwardingRule, *http.Response, error) { //nolint:lll
 259  	uri := fmt.Sprintf("%s/%s/forwarding-rules", lbPath, lbID)
 260  	req, err := l.client.NewRequest(ctx, http.MethodPost, uri, rule)
 261  	if err != nil {
 262  		return nil, nil, err
 263  	}
 264  
 265  	fwRule := new(lbRuleBase)
 266  	resp, err := l.client.DoWithContext(ctx, req, fwRule)
 267  	if err != nil {
 268  		return nil, resp, err
 269  	}
 270  
 271  	return fwRule.ForwardingRule, resp, nil
 272  }
 273  
 274  // GetForwardingRule will get a forwarding rule from your load balancer subscription.
 275  func (l *LoadBalancerHandler) GetForwardingRule(ctx context.Context, lbID, ruleID string) (*ForwardingRule, *http.Response, error) {
 276  	uri := fmt.Sprintf("%s/%s/forwarding-rules/%s", lbPath, lbID, ruleID)
 277  	req, err := l.client.NewRequest(ctx, http.MethodGet, uri, nil)
 278  	if err != nil {
 279  		return nil, nil, err
 280  	}
 281  
 282  	fwRule := new(lbRuleBase)
 283  	resp, err := l.client.DoWithContext(ctx, req, fwRule)
 284  	if err != nil {
 285  		return nil, resp, err
 286  	}
 287  
 288  	return fwRule.ForwardingRule, resp, nil
 289  }
 290  
 291  // ListForwardingRules lists all forwarding rules for a load balancer subscription
 292  func (l *LoadBalancerHandler) ListForwardingRules(ctx context.Context, lbID string, options *ListOptions) ([]ForwardingRule, *Meta, *http.Response, error) { //nolint:dupl,lll
 293  	uri := fmt.Sprintf("%s/%s/forwarding-rules", lbPath, lbID)
 294  	req, err := l.client.NewRequest(ctx, http.MethodGet, uri, nil)
 295  	if err != nil {
 296  		return nil, nil, nil, err
 297  	}
 298  
 299  	newValues, err := query.Values(options)
 300  	if err != nil {
 301  		return nil, nil, nil, err
 302  	}
 303  
 304  	req.URL.RawQuery = newValues.Encode()
 305  
 306  	fwRules := new(lbRulesBase)
 307  	resp, err := l.client.DoWithContext(ctx, req, &fwRules)
 308  	if err != nil {
 309  		return nil, nil, resp, err
 310  	}
 311  
 312  	return fwRules.ForwardingRules, fwRules.Meta, resp, nil
 313  }
 314  
 315  // DeleteForwardingRule removes a forwarding rule from a load balancer subscription
 316  func (l *LoadBalancerHandler) DeleteForwardingRule(ctx context.Context, lbID, ruleID string) error {
 317  	uri := fmt.Sprintf("%s/%s/forwarding-rules/%s", lbPath, lbID, ruleID)
 318  	req, err := l.client.NewRequest(ctx, http.MethodDelete, uri, nil)
 319  	if err != nil {
 320  		return err
 321  	}
 322  
 323  	_, err = l.client.DoWithContext(ctx, req, nil)
 324  	return err
 325  }
 326  
 327  // GetFirewallRule will get a firewall rule from your load balancer subscription.
 328  func (l *LoadBalancerHandler) GetFirewallRule(ctx context.Context, lbID, ruleID string) (*LBFirewallRule, *http.Response, error) {
 329  	uri := fmt.Sprintf("%s/%s/firewall-rules/%s", lbPath, lbID, ruleID)
 330  	req, err := l.client.NewRequest(ctx, http.MethodGet, uri, nil)
 331  	if err != nil {
 332  		return nil, nil, err
 333  	}
 334  
 335  	fwRule := new(lbFWRuleBase)
 336  	resp, err := l.client.DoWithContext(ctx, req, fwRule)
 337  	if err != nil {
 338  		return nil, resp, err
 339  	}
 340  
 341  	return fwRule.FirewallRule, resp, nil
 342  }
 343  
 344  // ListFirewallRules lists all firewall rules for a load balancer subscription
 345  func (l *LoadBalancerHandler) ListFirewallRules(ctx context.Context, lbID string, options *ListOptions) ([]LBFirewallRule, *Meta, *http.Response, error) { //nolint:dupl,lll
 346  	uri := fmt.Sprintf("%s/%s/firewall-rules", lbPath, lbID)
 347  	req, err := l.client.NewRequest(ctx, http.MethodGet, uri, nil)
 348  	if err != nil {
 349  		return nil, nil, nil, err
 350  	}
 351  
 352  	newValues, err := query.Values(options)
 353  	if err != nil {
 354  		return nil, nil, nil, err
 355  	}
 356  
 357  	req.URL.RawQuery = newValues.Encode()
 358  
 359  	fwRules := new(lbFWRulesBase)
 360  	resp, err := l.client.DoWithContext(ctx, req, &fwRules)
 361  	if err != nil {
 362  		return nil, nil, resp, err
 363  	}
 364  
 365  	return fwRules.FirewallRules, fwRules.Meta, resp, nil
 366  }
 367  
 368  // DeleteSSL removes the SSL configuration from a load balancer subscription.
 369  func (l *LoadBalancerHandler) DeleteSSL(ctx context.Context, lbID string) error {
 370  	uri := fmt.Sprintf("%s/%s/ssl", lbPath, lbID)
 371  	req, err := l.client.NewRequest(ctx, http.MethodDelete, uri, nil)
 372  	if err != nil {
 373  		return err
 374  	}
 375  
 376  	_, err = l.client.DoWithContext(ctx, req, nil)
 377  	return err
 378  }
 379  
 380  // DeleteAutoSSL removes the AutoSSL configuration from a load balancer subscription.
 381  func (l *LoadBalancerHandler) DeleteAutoSSL(ctx context.Context, lbID string) error {
 382  	uri := fmt.Sprintf("%s/%s/auto_ssl", lbPath, lbID)
 383  	req, err := l.client.NewRequest(ctx, http.MethodDelete, uri, nil)
 384  	if err != nil {
 385  		return err
 386  	}
 387  
 388  	_, err = l.client.DoWithContext(ctx, req, nil)
 389  	return err
 390  }
 391