Skip to content

Commit

Permalink
Adds support for Tiered Caching
Browse files Browse the repository at this point in the history
Adds support for utilizing previously undocumented APIs for Tiered Caching endpoints. It allows for setting zones to utilize either generic, smart or off.

Implementation:

* Adds three new api methods for manipulating tiered cache settings
  * GetTieredCache which returns the current setting
  * SetTieredCache which allows for manipulation of existing settings
  * DeleteTieredCache which allows for unsetting tiered cache
  • Loading branch information
Mikey Sleevi authored and iveelsm committed Dec 22, 2022
1 parent 75edb98 commit 131cef7
Show file tree
Hide file tree
Showing 3 changed files with 630 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .changelog/1149.txt
@@ -0,0 +1,3 @@
```release-note:enhancement
tiered_cache: Add support for Tiered Caching interactions for setting Smart and Generic topologies
```
306 changes: 306 additions & 0 deletions tiered_cache.go
@@ -0,0 +1,306 @@
package cloudflare

import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"time"
)

type TieredCacheType int

const (
TieredCacheOff TieredCacheType = 0
TieredCacheGeneric TieredCacheType = 1
TieredCacheSmart TieredCacheType = 2
)

type TieredCache struct {
Type TieredCacheType
LastModified time.Time
}

const notFoundError = "Unable to retrieve tiered_cache_smart_topology_enable setting value. The zone setting does not exist. (1142)"

// GetTieredCache allows you to retrieve the current Tiered Cache Settings for a Zone.
// This function does not support custom topologies, only Generic and Smart Tiered Caching.
//
// API Reference: https://api.cloudflare.com/#smart-tiered-cache-get-smart-tiered-cache-setting
//
// https://api.cloudflare.com/#tiered-cache-get-tiered-cache-setting
func (api *API) GetTieredCache(ctx context.Context, rc *ResourceContainer) (TieredCache, error) {
var lastModified time.Time

generic, err := getGenericTieredCache(api, ctx, rc)
if err != nil {
return TieredCache{}, err
}
lastModified = generic.LastModified

smart, err := getSmartTieredCache(api, ctx, rc)
if err != nil {
return TieredCache{}, err
}

if smart.LastModified.After(lastModified) {
lastModified = smart.LastModified
}

if generic.Type == TieredCacheOff {
return TieredCache{Type: TieredCacheOff, LastModified: lastModified}, nil
}

if smart.Type == TieredCacheOff {
return TieredCache{Type: TieredCacheGeneric, LastModified: lastModified}, nil
}

return TieredCache{Type: TieredCacheSmart, LastModified: lastModified}, nil
}

// SetTieredCache allows you to set a zone's tiered cache topology between the available types.
// Using the value of TieredCacheOff will disable Tiered Cache entirely.
//
// API Reference: https://api.cloudflare.com/#smart-tiered-cache-patch-smart-tiered-cache-setting
//
// https://api.cloudflare.com/#tiered-cache-patch-tiered-cache-setting
func (api *API) SetTieredCache(ctx context.Context, rc *ResourceContainer, value TieredCacheType) (TieredCache, error) {
if value == TieredCacheOff {
return api.DeleteTieredCache(ctx, rc)
}

var lastModified time.Time

if value == TieredCacheGeneric {
result, err := deleteSmartTieredCache(api, ctx, rc)
if err != nil {
return TieredCache{}, err
}
lastModified = result.LastModified

result, err = enableGenericTieredCache(api, ctx, rc)
if err != nil {
return TieredCache{}, err
}

if result.LastModified.After(lastModified) {
lastModified = result.LastModified
}
return TieredCache{Type: TieredCacheGeneric, LastModified: lastModified}, nil
}

result, err := enableGenericTieredCache(api, ctx, rc)
if err != nil {
return TieredCache{}, err
}
lastModified = result.LastModified

result, err = enableSmartTieredCache(api, ctx, rc)
if err != nil {
return TieredCache{}, err
}

if result.LastModified.After(lastModified) {
lastModified = result.LastModified
}
return TieredCache{Type: TieredCacheSmart, LastModified: lastModified}, nil
}

// DeleteTieredCache allows you to delete the tiered cache settings for a zone.
// This is equivalent to using SetTieredCache with the value of TieredCacheOff.
//
// API Reference: https://api.cloudflare.com/#smart-tiered-cache-delete-smart-tiered-cache-setting
//
// https://api.cloudflare.com/#tiered-cache-patch-tiered-cache-setting
func (api *API) DeleteTieredCache(ctx context.Context, rc *ResourceContainer) (TieredCache, error) {
var lastModified time.Time

result, err := deleteSmartTieredCache(api, ctx, rc)
if err != nil {
return TieredCache{}, err
}
lastModified = result.LastModified

result, err = disableGenericTieredCache(api, ctx, rc)
if err != nil {
return TieredCache{}, err
}

if result.LastModified.After(lastModified) {
lastModified = result.LastModified
}
return TieredCache{Type: TieredCacheOff, LastModified: lastModified}, nil
}

type tieredCacheResult struct {
ID string `json:"id"`
Value string `json:"value,omitempty"`
LastModified time.Time `json:"modified_on"`
}

type tieredCacheResponse struct {
Result tieredCacheResult `json:"result"`
Response
}

type tieredCacheSetting struct {
Value string `json:"value"`
}

func getGenericTieredCache(api *API, ctx context.Context, rc *ResourceContainer) (TieredCache, error) {
uri := fmt.Sprintf("/zones/%s/argo/tiered_caching", rc.Identifier)
res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
if err != nil {
return TieredCache{Type: TieredCacheOff}, err
}

var response tieredCacheResponse
err = json.Unmarshal(res, &response)
if err != nil {
return TieredCache{Type: TieredCacheOff}, err
}

if !response.Success {
return TieredCache{Type: TieredCacheOff}, errors.New("Request to retrieve generic tiered cache failed")
}

if response.Result.Value == "off" {
return TieredCache{Type: TieredCacheOff, LastModified: response.Result.LastModified}, nil
}

return TieredCache{Type: TieredCacheGeneric, LastModified: response.Result.LastModified}, nil
}

func getSmartTieredCache(api *API, ctx context.Context, rc *ResourceContainer) (TieredCache, error) {
uri := fmt.Sprintf("/zones/%s/cache/tiered_cache_smart_topology_enable", rc.Identifier)
res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
if err != nil {
if err.Error() == notFoundError {
return TieredCache{Type: TieredCacheOff}, nil
}
return TieredCache{Type: TieredCacheOff}, err
}

var response tieredCacheResponse
err = json.Unmarshal(res, &response)
if err != nil {
return TieredCache{Type: TieredCacheOff}, err
}

if !response.Success {
return TieredCache{Type: TieredCacheOff}, errors.New("Request to retrieve smart tiered cache failed")
}

if response.Result.Value == "off" {
return TieredCache{Type: TieredCacheOff, LastModified: response.Result.LastModified}, nil
}
return TieredCache{Type: TieredCacheSmart, LastModified: response.Result.LastModified}, nil
}

func enableGenericTieredCache(api *API, ctx context.Context, rc *ResourceContainer) (TieredCache, error) {
uri := fmt.Sprintf("/zones/%s/argo/tiered_caching", rc.Identifier)
setting := tieredCacheSetting{
Value: "on",
}
body, err := json.Marshal(setting)
if err != nil {
return TieredCache{Type: TieredCacheOff}, err
}

res, err := api.makeRequestContext(ctx, http.MethodPatch, uri, body)
if err != nil {
return TieredCache{Type: TieredCacheOff}, err
}

var response tieredCacheResponse
err = json.Unmarshal(res, &response)
if err != nil {
return TieredCache{Type: TieredCacheOff}, err
}

if !response.Success {
return TieredCache{Type: TieredCacheOff}, errors.New("Request to enable generic tiered cache failed")
}

return TieredCache{Type: TieredCacheGeneric, LastModified: response.Result.LastModified}, nil
}

func enableSmartTieredCache(api *API, ctx context.Context, rc *ResourceContainer) (TieredCache, error) {
uri := fmt.Sprintf("/zones/%s/cache/tiered_cache_smart_topology_enable", rc.Identifier)
setting := tieredCacheSetting{
Value: "on",
}
body, err := json.Marshal(setting)
if err != nil {
return TieredCache{Type: TieredCacheOff}, err
}

res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, body)
if err != nil {
return TieredCache{Type: TieredCacheOff}, err
}

var response tieredCacheResponse
err = json.Unmarshal(res, &response)
if err != nil {
return TieredCache{Type: TieredCacheOff}, err
}

if !response.Success {
return TieredCache{Type: TieredCacheOff}, errors.New("Request to enable smart tiered cache failed")
}

return TieredCache{Type: TieredCacheSmart, LastModified: response.Result.LastModified}, nil
}

func disableGenericTieredCache(api *API, ctx context.Context, rc *ResourceContainer) (TieredCache, error) {
uri := fmt.Sprintf("/zones/%s/argo/tiered_caching", rc.Identifier)
setting := tieredCacheSetting{
Value: "off",
}
body, err := json.Marshal(setting)
if err != nil {
return TieredCache{Type: TieredCacheOff}, err
}

res, err := api.makeRequestContext(ctx, http.MethodPatch, uri, body)
if err != nil {
return TieredCache{Type: TieredCacheOff}, err
}

var response tieredCacheResponse
err = json.Unmarshal(res, &response)
if err != nil {
return TieredCache{Type: TieredCacheOff}, err
}

if !response.Success {
return TieredCache{Type: TieredCacheOff}, errors.New("Request to disable generic tiered cache failed")
}

return TieredCache{Type: TieredCacheOff, LastModified: response.Result.LastModified}, nil
}

func deleteSmartTieredCache(api *API, ctx context.Context, rc *ResourceContainer) (TieredCache, error) {
uri := fmt.Sprintf("/zones/%s/cache/tiered_cache_smart_topology_enable", rc.Identifier)
res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
if err != nil {
if err.Error() == notFoundError {
return TieredCache{Type: TieredCacheOff}, nil
}
return TieredCache{Type: TieredCacheOff}, err
}

var response tieredCacheResponse
err = json.Unmarshal(res, &response)
if err != nil {
return TieredCache{Type: TieredCacheOff}, err
}

if !response.Success {
return TieredCache{Type: TieredCacheOff}, errors.New("Request to disable smart tiered cache failed")
}

return TieredCache{Type: TieredCacheOff, LastModified: response.Result.LastModified}, nil
}

0 comments on commit 131cef7

Please sign in to comment.