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 }