dnscontrol/pkg/cloudflare-go/load_balancing.go
Tom Limoncelli 7fd6a74e0c
CLOUDFLAREAPI: CF_REDIRECT/CF_TEMP_REDIRECT should dtrt using Single Redirects (#3002)
Co-authored-by: Josh Zhang <jzhang1@stackoverflow.com>
2024-06-18 17:38:50 -04:00

822 lines
31 KiB
Go

package cloudflare
import (
"context"
"errors"
"fmt"
"net/http"
"time"
"github.com/goccy/go-json"
)
// LoadBalancerPool represents a load balancer pool's properties.
type LoadBalancerPool struct {
ID string `json:"id,omitempty"`
CreatedOn *time.Time `json:"created_on,omitempty"`
ModifiedOn *time.Time `json:"modified_on,omitempty"`
Description string `json:"description"`
Name string `json:"name"`
Enabled bool `json:"enabled"`
MinimumOrigins *int `json:"minimum_origins,omitempty"`
Monitor string `json:"monitor,omitempty"`
Origins []LoadBalancerOrigin `json:"origins"`
NotificationEmail string `json:"notification_email,omitempty"`
Latitude *float32 `json:"latitude,omitempty"`
Longitude *float32 `json:"longitude,omitempty"`
LoadShedding *LoadBalancerLoadShedding `json:"load_shedding,omitempty"`
OriginSteering *LoadBalancerOriginSteering `json:"origin_steering,omitempty"`
Healthy *bool `json:"healthy,omitempty"`
// CheckRegions defines the geographic region(s) from where to run health-checks from - e.g. "WNAM", "WEU", "SAF", "SAM".
// Providing a null/empty value means "all regions", which may not be available to all plan types.
CheckRegions []string `json:"check_regions"`
}
// LoadBalancerOrigin represents a Load Balancer origin's properties.
type LoadBalancerOrigin struct {
Name string `json:"name"`
Address string `json:"address"`
Enabled bool `json:"enabled"`
// Weight of this origin relative to other origins in the pool.
// Based on the configured weight the total traffic is distributed
// among origins within the pool.
//
// When LoadBalancerOriginSteering.Policy="least_outstanding_requests", this
// weight is used to scale the origin's outstanding requests.
// When LoadBalancerOriginSteering.Policy="least_connections", this
// weight is used to scale the origin's open connections.
Weight float64 `json:"weight"`
Header map[string][]string `json:"header"`
// The virtual network subnet ID the origin belongs in.
// Virtual network must also belong to the account.
VirtualNetworkID string `json:"virtual_network_id,omitempty"`
}
// LoadBalancerOriginSteering controls origin selection for new sessions and traffic without session affinity.
type LoadBalancerOriginSteering struct {
// Policy determines the type of origin steering policy to use.
// It defaults to "random" (weighted) when empty or unspecified.
//
// "random": Select an origin randomly.
//
// "hash": Select an origin by computing a hash over the CF-Connecting-IP address.
//
// "least_outstanding_requests": Select an origin by taking into consideration origin weights,
// as well as each origin's number of outstanding requests. Origins with more pending requests
// are weighted proportionately less relative to others.
//
// "least_connections": Select an origin by taking into consideration origin weights,
// as well as each origin's number of open connections. Origins with more open connections
// are weighted proportionately less relative to others. Supported for HTTP/1 and HTTP/2 connections.
Policy string `json:"policy,omitempty"`
}
// LoadBalancerMonitor represents a load balancer monitor's properties.
type LoadBalancerMonitor struct {
ID string `json:"id,omitempty"`
CreatedOn *time.Time `json:"created_on,omitempty"`
ModifiedOn *time.Time `json:"modified_on,omitempty"`
Type string `json:"type"`
Description string `json:"description"`
Method string `json:"method"`
Path string `json:"path"`
Header map[string][]string `json:"header"`
Timeout int `json:"timeout"`
Retries int `json:"retries"`
Interval int `json:"interval"`
ConsecutiveUp int `json:"consecutive_up"`
ConsecutiveDown int `json:"consecutive_down"`
Port uint16 `json:"port,omitempty"`
ExpectedBody string `json:"expected_body"`
ExpectedCodes string `json:"expected_codes"`
FollowRedirects bool `json:"follow_redirects"`
AllowInsecure bool `json:"allow_insecure"`
ProbeZone string `json:"probe_zone"`
}
// LoadBalancer represents a load balancer's properties.
type LoadBalancer struct {
ID string `json:"id,omitempty"`
CreatedOn *time.Time `json:"created_on,omitempty"`
ModifiedOn *time.Time `json:"modified_on,omitempty"`
Description string `json:"description"`
Name string `json:"name"`
TTL int `json:"ttl,omitempty"`
FallbackPool string `json:"fallback_pool"`
DefaultPools []string `json:"default_pools"`
RegionPools map[string][]string `json:"region_pools"`
PopPools map[string][]string `json:"pop_pools"`
CountryPools map[string][]string `json:"country_pools"`
Proxied bool `json:"proxied"`
Enabled *bool `json:"enabled,omitempty"`
Persistence string `json:"session_affinity,omitempty"`
PersistenceTTL int `json:"session_affinity_ttl,omitempty"`
SessionAffinityAttributes *SessionAffinityAttributes `json:"session_affinity_attributes,omitempty"`
Rules []*LoadBalancerRule `json:"rules,omitempty"`
RandomSteering *RandomSteering `json:"random_steering,omitempty"`
AdaptiveRouting *AdaptiveRouting `json:"adaptive_routing,omitempty"`
LocationStrategy *LocationStrategy `json:"location_strategy,omitempty"`
// SteeringPolicy controls pool selection logic.
//
// "off": Select pools in DefaultPools order.
//
// "geo": Select pools based on RegionPools/PopPools/CountryPools.
// For non-proxied requests, the country for CountryPools is determined by LocationStrategy.
//
// "dynamic_latency": Select pools based on RTT (requires health checks).
//
// "random": Selects pools in a random order.
//
// "proximity": Use the pools' latitude and longitude to select the closest pool using
// the Cloudflare PoP location for proxied requests or the location determined by
// LocationStrategy for non-proxied requests.
//
// "least_outstanding_requests": Select a pool by taking into consideration
// RandomSteering weights, as well as each pool's number of outstanding requests.
// Pools with more pending requests are weighted proportionately less relative to others.
//
// "least_connections": Select a pool by taking into consideration
// RandomSteering weights, as well as each pool's number of open connections.
// Pools with more open connections are weighted proportionately less relative to others.
// Supported for HTTP/1 and HTTP/2 connections.
//
// "": Maps to "geo" if RegionPools or PopPools or CountryPools have entries otherwise "off".
SteeringPolicy string `json:"steering_policy,omitempty"`
}
// LoadBalancerLoadShedding contains the settings for controlling load shedding.
type LoadBalancerLoadShedding struct {
DefaultPercent float32 `json:"default_percent,omitempty"`
DefaultPolicy string `json:"default_policy,omitempty"`
SessionPercent float32 `json:"session_percent,omitempty"`
SessionPolicy string `json:"session_policy,omitempty"`
}
// LoadBalancerRule represents a single rule entry for a Load Balancer. Each rules
// is run one after the other in priority order. Disabled rules are skipped.
type LoadBalancerRule struct {
Overrides LoadBalancerRuleOverrides `json:"overrides"`
// Name is required but is only used for human readability
Name string `json:"name"`
Condition string `json:"condition"`
// Priority controls the order of rule execution the lowest value will be invoked first
Priority int `json:"priority"`
// FixedResponse if set and the condition is true we will not run
// routing logic but rather directly respond with the provided fields.
// FixedResponse implies terminates.
FixedResponse *LoadBalancerFixedResponseData `json:"fixed_response,omitempty"`
Disabled bool `json:"disabled"`
// Terminates flag this rule as 'terminating'. No further rules will
// be executed after this one.
Terminates bool `json:"terminates,omitempty"`
}
// LoadBalancerFixedResponseData contains all the data needed to generate
// a fixed response from a Load Balancer. This behavior can be enabled via Rules.
type LoadBalancerFixedResponseData struct {
// MessageBody data to write into the http body
MessageBody string `json:"message_body,omitempty"`
// StatusCode the http status code to response with
StatusCode int `json:"status_code,omitempty"`
// ContentType value of the http 'content-type' header
ContentType string `json:"content_type,omitempty"`
// Location value of the http 'location' header
Location string `json:"location,omitempty"`
}
// LoadBalancerRuleOverrides are the set of field overridable by the rules system.
type LoadBalancerRuleOverrides struct {
// session affinity
Persistence string `json:"session_affinity,omitempty"`
PersistenceTTL *uint `json:"session_affinity_ttl,omitempty"`
SessionAffinityAttrs *LoadBalancerRuleOverridesSessionAffinityAttrs `json:"session_affinity_attributes,omitempty"`
TTL uint `json:"ttl,omitempty"`
SteeringPolicy string `json:"steering_policy,omitempty"`
FallbackPool string `json:"fallback_pool,omitempty"`
DefaultPools []string `json:"default_pools,omitempty"`
PoPPools map[string][]string `json:"pop_pools,omitempty"`
RegionPools map[string][]string `json:"region_pools,omitempty"`
CountryPools map[string][]string `json:"country_pools,omitempty"`
RandomSteering *RandomSteering `json:"random_steering,omitempty"`
AdaptiveRouting *AdaptiveRouting `json:"adaptive_routing,omitempty"`
LocationStrategy *LocationStrategy `json:"location_strategy,omitempty"`
}
// RandomSteering configures pool weights.
//
// SteeringPolicy="random": A random pool is selected with probability
// proportional to pool weights.
//
// SteeringPolicy="least_outstanding_requests": Use pool weights to
// scale each pool's outstanding requests.
//
// SteeringPolicy="least_connections": Use pool weights to
// scale each pool's open connections.
type RandomSteering struct {
DefaultWeight float64 `json:"default_weight,omitempty"`
PoolWeights map[string]float64 `json:"pool_weights,omitempty"`
}
// AdaptiveRouting controls features that modify the routing of requests
// to pools and origins in response to dynamic conditions, such as during
// the interval between active health monitoring requests.
// For example, zero-downtime failover occurs immediately when an origin
// becomes unavailable due to HTTP 521, 522, or 523 response codes.
// If there is another healthy origin in the same pool, the request is
// retried once against this alternate origin.
type AdaptiveRouting struct {
// FailoverAcrossPools extends zero-downtime failover of requests to healthy origins
// from alternate pools, when no healthy alternate exists in the same pool, according
// to the failover order defined by traffic and origin steering.
// When set false (the default) zero-downtime failover will only occur between origins
// within the same pool. See SessionAffinityAttributes for control over when sessions
// are broken or reassigned.
FailoverAcrossPools *bool `json:"failover_across_pools,omitempty"`
}
// LocationStrategy controls location-based steering for non-proxied requests.
// See SteeringPolicy to learn how steering is affected.
type LocationStrategy struct {
// PreferECS determines whether the EDNS Client Subnet (ECS) GeoIP should
// be preferred as the authoritative location.
//
// "always": Always prefer ECS.
//
// "never": Never prefer ECS.
//
// "proximity": (default) Prefer ECS only when SteeringPolicy="proximity".
//
// "geo": Prefer ECS only when SteeringPolicy="geo".
PreferECS string `json:"prefer_ecs,omitempty"`
// Mode determines the authoritative location when ECS is not preferred,
// does not exist in the request, or its GeoIP lookup is unsuccessful.
//
// "pop": (default) Use the Cloudflare PoP location.
//
// "resolver_ip": Use the DNS resolver GeoIP location.
// If the GeoIP lookup is unsuccessful, use the Cloudflare PoP location.
Mode string `json:"mode,omitempty"`
}
// LoadBalancerRuleOverridesSessionAffinityAttrs mimics SessionAffinityAttributes without the
// DrainDuration field as that field can not be overwritten via rules.
type LoadBalancerRuleOverridesSessionAffinityAttrs struct {
SameSite string `json:"samesite,omitempty"`
Secure string `json:"secure,omitempty"`
ZeroDowntimeFailover string `json:"zero_downtime_failover,omitempty"`
Headers []string `json:"headers,omitempty"`
RequireAllHeaders *bool `json:"require_all_headers,omitempty"`
}
// SessionAffinityAttributes represents additional configuration options for session affinity.
type SessionAffinityAttributes struct {
SameSite string `json:"samesite,omitempty"`
Secure string `json:"secure,omitempty"`
DrainDuration int `json:"drain_duration,omitempty"`
ZeroDowntimeFailover string `json:"zero_downtime_failover,omitempty"`
Headers []string `json:"headers,omitempty"`
RequireAllHeaders bool `json:"require_all_headers,omitempty"`
}
// LoadBalancerOriginHealth represents the health of the origin.
type LoadBalancerOriginHealth struct {
Healthy bool `json:"healthy,omitempty"`
RTT Duration `json:"rtt,omitempty"`
FailureReason string `json:"failure_reason,omitempty"`
ResponseCode int `json:"response_code,omitempty"`
}
// LoadBalancerPoolPopHealth represents the health of the pool for given PoP.
type LoadBalancerPoolPopHealth struct {
Healthy bool `json:"healthy,omitempty"`
Origins []map[string]LoadBalancerOriginHealth `json:"origins,omitempty"`
}
// LoadBalancerPoolHealth represents the healthchecks from different PoPs for a pool.
type LoadBalancerPoolHealth struct {
ID string `json:"pool_id,omitempty"`
PopHealth map[string]LoadBalancerPoolPopHealth `json:"pop_health,omitempty"`
}
// loadBalancerPoolResponse represents the response from the load balancer pool endpoints.
type loadBalancerPoolResponse struct {
Response
Result LoadBalancerPool `json:"result"`
}
// loadBalancerPoolListResponse represents the response from the List Pools endpoint.
type loadBalancerPoolListResponse struct {
Response
Result []LoadBalancerPool `json:"result"`
ResultInfo ResultInfo `json:"result_info"`
}
// loadBalancerMonitorResponse represents the response from the load balancer monitor endpoints.
type loadBalancerMonitorResponse struct {
Response
Result LoadBalancerMonitor `json:"result"`
}
// loadBalancerMonitorListResponse represents the response from the List Monitors endpoint.
type loadBalancerMonitorListResponse struct {
Response
Result []LoadBalancerMonitor `json:"result"`
ResultInfo ResultInfo `json:"result_info"`
}
// loadBalancerResponse represents the response from the load balancer endpoints.
type loadBalancerResponse struct {
Response
Result LoadBalancer `json:"result"`
}
// loadBalancerListResponse represents the response from the List Load Balancers endpoint.
type loadBalancerListResponse struct {
Response
Result []LoadBalancer `json:"result"`
ResultInfo ResultInfo `json:"result_info"`
}
// loadBalancerPoolHealthResponse represents the response from the Pool Health Details endpoint.
type loadBalancerPoolHealthResponse struct {
Response
Result LoadBalancerPoolHealth `json:"result"`
}
type CreateLoadBalancerPoolParams struct {
LoadBalancerPool LoadBalancerPool
}
type ListLoadBalancerPoolParams struct {
PaginationOptions
}
type UpdateLoadBalancerPoolParams struct {
LoadBalancer LoadBalancerPool
}
type CreateLoadBalancerMonitorParams struct {
LoadBalancerMonitor LoadBalancerMonitor
}
type ListLoadBalancerMonitorParams struct {
PaginationOptions
}
type UpdateLoadBalancerMonitorParams struct {
LoadBalancerMonitor LoadBalancerMonitor
}
type CreateLoadBalancerParams struct {
LoadBalancer LoadBalancer
}
type ListLoadBalancerParams struct {
PaginationOptions
}
type UpdateLoadBalancerParams struct {
LoadBalancer LoadBalancer
}
var (
ErrMissingPoolID = errors.New("missing required pool ID")
ErrMissingMonitorID = errors.New("missing required monitor ID")
ErrMissingLoadBalancerID = errors.New("missing required load balancer ID")
)
// CreateLoadBalancerPool creates a new load balancer pool.
//
// API reference: https://api.cloudflare.com/#load-balancer-pools-create-pool
func (api *API) CreateLoadBalancerPool(ctx context.Context, rc *ResourceContainer, params CreateLoadBalancerPoolParams) (LoadBalancerPool, error) {
if rc.Level == ZoneRouteLevel {
return LoadBalancerPool{}, fmt.Errorf(errInvalidResourceContainerAccess, ZoneRouteLevel)
}
var uri string
if rc.Level == UserRouteLevel {
uri = "/user/load_balancers/pools"
} else {
uri = fmt.Sprintf("/accounts/%s/load_balancers/pools", rc.Identifier)
}
res, err := api.makeRequestContext(ctx, http.MethodPost, uri, params.LoadBalancerPool)
if err != nil {
return LoadBalancerPool{}, err
}
var r loadBalancerPoolResponse
if err := json.Unmarshal(res, &r); err != nil {
return LoadBalancerPool{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
}
return r.Result, nil
}
// ListLoadBalancerPools lists load balancer pools connected to an account.
//
// API reference: https://api.cloudflare.com/#load-balancer-pools-list-pools
func (api *API) ListLoadBalancerPools(ctx context.Context, rc *ResourceContainer, params ListLoadBalancerPoolParams) ([]LoadBalancerPool, error) {
if rc.Level == ZoneRouteLevel {
return []LoadBalancerPool{}, fmt.Errorf(errInvalidResourceContainerAccess, ZoneRouteLevel)
}
var uri string
if rc.Level == UserRouteLevel {
uri = "/user/load_balancers/pools"
} else {
uri = fmt.Sprintf("/accounts/%s/load_balancers/pools", rc.Identifier)
}
uri = buildURI(uri, params.PaginationOptions)
res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
if err != nil {
return nil, err
}
var r loadBalancerPoolListResponse
if err := json.Unmarshal(res, &r); err != nil {
return nil, fmt.Errorf("%s: %w", errUnmarshalError, err)
}
return r.Result, nil
}
// GetLoadBalancerPool returns the details for a load balancer pool.
//
// API reference: https://api.cloudflare.com/#load-balancer-pools-pool-details
func (api *API) GetLoadBalancerPool(ctx context.Context, rc *ResourceContainer, poolID string) (LoadBalancerPool, error) {
if rc.Level == ZoneRouteLevel {
return LoadBalancerPool{}, fmt.Errorf(errInvalidResourceContainerAccess, ZoneRouteLevel)
}
if poolID == "" {
return LoadBalancerPool{}, ErrMissingPoolID
}
var uri string
if rc.Level == UserRouteLevel {
uri = fmt.Sprintf("/user/load_balancers/pools/%s", poolID)
} else {
uri = fmt.Sprintf("/accounts/%s/load_balancers/pools/%s", rc.Identifier, poolID)
}
res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
if err != nil {
return LoadBalancerPool{}, err
}
var r loadBalancerPoolResponse
if err := json.Unmarshal(res, &r); err != nil {
return LoadBalancerPool{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
}
return r.Result, nil
}
// DeleteLoadBalancerPool disables and deletes a load balancer pool.
//
// API reference: https://api.cloudflare.com/#load-balancer-pools-delete-pool
func (api *API) DeleteLoadBalancerPool(ctx context.Context, rc *ResourceContainer, poolID string) error {
if rc.Level == ZoneRouteLevel {
return fmt.Errorf(errInvalidResourceContainerAccess, ZoneRouteLevel)
}
if poolID == "" {
return ErrMissingPoolID
}
var uri string
if rc.Level == UserRouteLevel {
uri = fmt.Sprintf("/user/load_balancers/pools/%s", poolID)
} else {
uri = fmt.Sprintf("/accounts/%s/load_balancers/pools/%s", rc.Identifier, poolID)
}
if _, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil); err != nil {
return err
}
return nil
}
// UpdateLoadBalancerPool modifies a configured load balancer pool.
//
// API reference: https://api.cloudflare.com/#load-balancer-pools-update-pool
func (api *API) UpdateLoadBalancerPool(ctx context.Context, rc *ResourceContainer, params UpdateLoadBalancerPoolParams) (LoadBalancerPool, error) {
if rc.Level == ZoneRouteLevel {
return LoadBalancerPool{}, fmt.Errorf(errInvalidResourceContainerAccess, ZoneRouteLevel)
}
if params.LoadBalancer.ID == "" {
return LoadBalancerPool{}, ErrMissingPoolID
}
var uri string
if rc.Level == UserRouteLevel {
uri = fmt.Sprintf("/user/load_balancers/pools/%s", params.LoadBalancer.ID)
} else {
uri = fmt.Sprintf("/accounts/%s/load_balancers/pools/%s", rc.Identifier, params.LoadBalancer.ID)
}
res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params.LoadBalancer)
if err != nil {
return LoadBalancerPool{}, err
}
var r loadBalancerPoolResponse
if err := json.Unmarshal(res, &r); err != nil {
return LoadBalancerPool{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
}
return r.Result, nil
}
// CreateLoadBalancerMonitor creates a new load balancer monitor.
//
// API reference: https://api.cloudflare.com/#load-balancer-monitors-create-monitor
func (api *API) CreateLoadBalancerMonitor(ctx context.Context, rc *ResourceContainer, params CreateLoadBalancerMonitorParams) (LoadBalancerMonitor, error) {
if rc.Level == ZoneRouteLevel {
return LoadBalancerMonitor{}, fmt.Errorf(errInvalidResourceContainerAccess, ZoneRouteLevel)
}
var uri string
if rc.Level == UserRouteLevel {
uri = "/user/load_balancers/monitors"
} else {
uri = fmt.Sprintf("/accounts/%s/load_balancers/monitors", rc.Identifier)
}
res, err := api.makeRequestContext(ctx, http.MethodPost, uri, params.LoadBalancerMonitor)
if err != nil {
return LoadBalancerMonitor{}, err
}
var r loadBalancerMonitorResponse
if err := json.Unmarshal(res, &r); err != nil {
return LoadBalancerMonitor{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
}
return r.Result, nil
}
// ListLoadBalancerMonitors lists load balancer monitors connected to an account.
//
// API reference: https://api.cloudflare.com/#load-balancer-monitors-list-monitors
func (api *API) ListLoadBalancerMonitors(ctx context.Context, rc *ResourceContainer, params ListLoadBalancerMonitorParams) ([]LoadBalancerMonitor, error) {
if rc.Level == ZoneRouteLevel {
return []LoadBalancerMonitor{}, fmt.Errorf(errInvalidResourceContainerAccess, ZoneRouteLevel)
}
var uri string
if rc.Level == UserRouteLevel {
uri = "/user/load_balancers/monitors"
} else {
uri = fmt.Sprintf("/accounts/%s/load_balancers/monitors", rc.Identifier)
}
uri = buildURI(uri, params.PaginationOptions)
res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
if err != nil {
return nil, err
}
var r loadBalancerMonitorListResponse
if err := json.Unmarshal(res, &r); err != nil {
return nil, fmt.Errorf("%s: %w", errUnmarshalError, err)
}
return r.Result, nil
}
// GetLoadBalancerMonitor returns the details for a load balancer monitor.
//
// API reference: https://api.cloudflare.com/#load-balancer-monitors-monitor-details
func (api *API) GetLoadBalancerMonitor(ctx context.Context, rc *ResourceContainer, monitorID string) (LoadBalancerMonitor, error) {
if rc.Level == ZoneRouteLevel {
return LoadBalancerMonitor{}, fmt.Errorf(errInvalidResourceContainerAccess, ZoneRouteLevel)
}
if monitorID == "" {
return LoadBalancerMonitor{}, ErrMissingMonitorID
}
var uri string
if rc.Level == UserRouteLevel {
uri = fmt.Sprintf("/user/load_balancers/monitors/%s", monitorID)
} else {
uri = fmt.Sprintf("/accounts/%s/load_balancers/monitors/%s", rc.Identifier, monitorID)
}
res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
if err != nil {
return LoadBalancerMonitor{}, err
}
var r loadBalancerMonitorResponse
if err := json.Unmarshal(res, &r); err != nil {
return LoadBalancerMonitor{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
}
return r.Result, nil
}
// DeleteLoadBalancerMonitor disables and deletes a load balancer monitor.
//
// API reference: https://api.cloudflare.com/#load-balancer-monitors-delete-monitor
func (api *API) DeleteLoadBalancerMonitor(ctx context.Context, rc *ResourceContainer, monitorID string) error {
if rc.Level == ZoneRouteLevel {
return fmt.Errorf(errInvalidResourceContainerAccess, ZoneRouteLevel)
}
if monitorID == "" {
return ErrMissingMonitorID
}
var uri string
if rc.Level == UserRouteLevel {
uri = fmt.Sprintf("/user/load_balancers/monitors/%s", monitorID)
} else {
uri = fmt.Sprintf("/accounts/%s/load_balancers/monitors/%s", rc.Identifier, monitorID)
}
if _, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil); err != nil {
return err
}
return nil
}
// UpdateLoadBalancerMonitor modifies a configured load balancer monitor.
//
// API reference: https://api.cloudflare.com/#load-balancer-monitors-update-monitor
func (api *API) UpdateLoadBalancerMonitor(ctx context.Context, rc *ResourceContainer, params UpdateLoadBalancerMonitorParams) (LoadBalancerMonitor, error) {
if rc.Level == ZoneRouteLevel {
return LoadBalancerMonitor{}, fmt.Errorf(errInvalidResourceContainerAccess, ZoneRouteLevel)
}
if params.LoadBalancerMonitor.ID == "" {
return LoadBalancerMonitor{}, ErrMissingMonitorID
}
var uri string
if rc.Level == UserRouteLevel {
uri = fmt.Sprintf("/user/load_balancers/monitors/%s", params.LoadBalancerMonitor.ID)
} else {
uri = fmt.Sprintf("/accounts/%s/load_balancers/monitors/%s", rc.Identifier, params.LoadBalancerMonitor.ID)
}
res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params.LoadBalancerMonitor)
if err != nil {
return LoadBalancerMonitor{}, err
}
var r loadBalancerMonitorResponse
if err := json.Unmarshal(res, &r); err != nil {
return LoadBalancerMonitor{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
}
return r.Result, nil
}
// CreateLoadBalancer creates a new load balancer.
//
// API reference: https://api.cloudflare.com/#load-balancers-create-load-balancer
func (api *API) CreateLoadBalancer(ctx context.Context, rc *ResourceContainer, params CreateLoadBalancerParams) (LoadBalancer, error) {
if rc.Level != ZoneRouteLevel {
return LoadBalancer{}, fmt.Errorf(errInvalidResourceContainerAccess, rc.Level)
}
uri := fmt.Sprintf("/zones/%s/load_balancers", rc.Identifier)
res, err := api.makeRequestContext(ctx, http.MethodPost, uri, params.LoadBalancer)
if err != nil {
return LoadBalancer{}, err
}
var r loadBalancerResponse
if err := json.Unmarshal(res, &r); err != nil {
return LoadBalancer{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
}
return r.Result, nil
}
// ListLoadBalancers lists load balancers configured on a zone.
//
// API reference: https://api.cloudflare.com/#load-balancers-list-load-balancers
func (api *API) ListLoadBalancers(ctx context.Context, rc *ResourceContainer, params ListLoadBalancerParams) ([]LoadBalancer, error) {
if rc.Level != ZoneRouteLevel {
return []LoadBalancer{}, fmt.Errorf(errInvalidResourceContainerAccess, rc.Level)
}
uri := buildURI(fmt.Sprintf("/zones/%s/load_balancers", rc.Identifier), params.PaginationOptions)
res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
if err != nil {
return nil, err
}
var r loadBalancerListResponse
if err := json.Unmarshal(res, &r); err != nil {
return nil, fmt.Errorf("%s: %w", errUnmarshalError, err)
}
return r.Result, nil
}
// GetLoadBalancer returns the details for a load balancer.
//
// API reference: https://api.cloudflare.com/#load-balancers-load-balancer-details
func (api *API) GetLoadBalancer(ctx context.Context, rc *ResourceContainer, loadbalancerID string) (LoadBalancer, error) {
if rc.Level != ZoneRouteLevel {
return LoadBalancer{}, fmt.Errorf(errInvalidResourceContainerAccess, rc.Level)
}
if loadbalancerID == "" {
return LoadBalancer{}, ErrMissingLoadBalancerID
}
uri := fmt.Sprintf("/zones/%s/load_balancers/%s", rc.Identifier, loadbalancerID)
res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
if err != nil {
return LoadBalancer{}, err
}
var r loadBalancerResponse
if err := json.Unmarshal(res, &r); err != nil {
return LoadBalancer{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
}
return r.Result, nil
}
// DeleteLoadBalancer disables and deletes a load balancer.
//
// API reference: https://api.cloudflare.com/#load-balancers-delete-load-balancer
func (api *API) DeleteLoadBalancer(ctx context.Context, rc *ResourceContainer, loadbalancerID string) error {
if rc.Level != ZoneRouteLevel {
return fmt.Errorf(errInvalidResourceContainerAccess, rc.Level)
}
if loadbalancerID == "" {
return ErrMissingLoadBalancerID
}
uri := fmt.Sprintf("/zones/%s/load_balancers/%s", rc.Identifier, loadbalancerID)
if _, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil); err != nil {
return err
}
return nil
}
// UpdateLoadBalancer modifies a configured load balancer.
//
// API reference: https://api.cloudflare.com/#load-balancers-update-load-balancer
func (api *API) UpdateLoadBalancer(ctx context.Context, rc *ResourceContainer, params UpdateLoadBalancerParams) (LoadBalancer, error) {
if rc.Level != ZoneRouteLevel {
return LoadBalancer{}, fmt.Errorf(errInvalidResourceContainerAccess, rc.Level)
}
if params.LoadBalancer.ID == "" {
return LoadBalancer{}, ErrMissingLoadBalancerID
}
uri := fmt.Sprintf("/zones/%s/load_balancers/%s", rc.Identifier, params.LoadBalancer.ID)
res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params.LoadBalancer)
if err != nil {
return LoadBalancer{}, err
}
var r loadBalancerResponse
if err := json.Unmarshal(res, &r); err != nil {
return LoadBalancer{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
}
return r.Result, nil
}
// GetLoadBalancerPoolHealth fetches the latest healtcheck details for a single
// pool.
//
// API reference: https://api.cloudflare.com/#load-balancer-pools-pool-health-details
func (api *API) GetLoadBalancerPoolHealth(ctx context.Context, rc *ResourceContainer, poolID string) (LoadBalancerPoolHealth, error) {
if rc.Level == ZoneRouteLevel {
return LoadBalancerPoolHealth{}, fmt.Errorf(errInvalidResourceContainerAccess, ZoneRouteLevel)
}
if poolID == "" {
return LoadBalancerPoolHealth{}, ErrMissingPoolID
}
var uri string
if rc.Level == UserRouteLevel {
uri = fmt.Sprintf("/user/load_balancers/pools/%s/health", poolID)
} else {
uri = fmt.Sprintf("/accounts/%s/load_balancers/pools/%s/health", rc.Identifier, poolID)
}
res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
if err != nil {
return LoadBalancerPoolHealth{}, err
}
var r loadBalancerPoolHealthResponse
if err := json.Unmarshal(res, &r); err != nil {
return LoadBalancerPoolHealth{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
}
return r.Result, nil
}