Repository: cloudflare/cloudflare-go
Pull request #1149 (2022-12-21 05:09:17)

Adds support for Tiered Caching
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

## Description

Tiered Caching is a common use case for many developers. This adds support for utilizing the library to set tiered cache topologies and manipulate them.

## Has your change been tested?

Yes.

## Screenshots (if appropriate):

## Types of changes

What sort of change does your code introduce/modify?

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)

## Checklist:

- [x] My code follows the code style of this project.
- [x] My change requires a change to the documentation.
- [x] I have updated the documentation accordingly.
- [x] I have added tests to cover my changes.
- [x] All new and existing tests passed.
- [x] This change is using publicly documented (api.cloudflare.com or developers.cloudflare.com) and stable APIs.

[1]: https://help.github.com/articles/closing-issues-using-keywords/


iveelsm: This is still pending documentation updates which are in progress.
codecov-commenter: # [Codecov](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1149?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare) Report
> Merging [#1149](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1149?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare) (7b3ca43) into [master](https://codecov.io/gh/cloudflare/cloudflare-go/commit/4ddb00ca1671fb5e145ef2ed503fd1f970ddd5fe?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare) (4ddb00c) will **decrease** coverage by `0.65%`.
> The diff coverage is `52.85%`.

```diff
@@            Coverage Diff             @@
##           master    #1149      +/-   ##
==========================================
- Coverage   50.11%   49.45%   -0.66%     
==========================================
  Files         121      127       +6     
  Lines       11722    12278     +556     
==========================================
+ Hits         5874     6072     +198     
- Misses       4565     4828     +263     
- Partials     1283     1378      +95     
```


| [Impacted Files](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1149?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare) | Coverage Δ | |
|---|---|---|
| [device\_posture\_rule.go](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1149/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-ZGV2aWNlX3Bvc3R1cmVfcnVsZS5nbw==) | `61.80% <ø> (ø)` | |
| [errors.go](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1149/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-ZXJyb3JzLmdv) | `42.10% <ø> (+1.31%)` | :arrow_up: |
| [pages\_deployment.go](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1149/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-cGFnZXNfZGVwbG95bWVudC5nbw==) | `34.24% <ø> (-0.24%)` | :arrow_down: |
| [pages\_domain.go](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1149/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-cGFnZXNfZG9tYWluLmdv) | `71.27% <ø> (ø)` | |
| [pages\_project.go](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1149/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-cGFnZXNfcHJvamVjdC5nbw==) | `50.81% <ø> (ø)` | |
| [rulesets.go](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1149/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-cnVsZXNldHMuZ28=) | `35.09% <ø> (ø)` | |
| [workers\_account\_settings.go](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1149/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-d29ya2Vyc19hY2NvdW50X3NldHRpbmdzLmdv) | `29.41% <0.00%> (-6.31%)` | :arrow_down: |
| [workers\_tail.go](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1149/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-d29ya2Vyc190YWlsLmdv) | `52.00% <ø> (ø)` | |
| [workers\_secrets.go](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1149/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-d29ya2Vyc19zZWNyZXRzLmdv) | `29.41% <28.00%> (-16.05%)` | :arrow_down: |
| [workers\_cron\_triggers.go](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1149/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-d29ya2Vyc19jcm9uX3RyaWdnZXJzLmdv) | `29.41% <29.41%> (-16.05%)` | :arrow_down: |
| ... and [12 more](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1149/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare) | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare)

github-actions[bot]: Oops! It looks like no changelog entry is attached to this PR. Please include a release note as described in https://github.com/cloudflare/cloudflare-go/blob/master/docs/changelog-process.md.

Example: 

~~~
```release-note:TYPE
Release note
```
~~~

If you do not require a release note to be included, please add the `workflow/skip-changelog-entry` label.
jacobbednarz: how does this interact with the existing methods in `argo.go`?
iveelsm: > how does this interact with the existing methods in `argo.go`?

It doesn't interact at all, rather intentionally. `/argo/tiered_caching` and `/cache/tiered_cache_smart_topology_enable` are tightly coupled endpoints that require interactions with both in order to properly manipulate tiered cache settings. I did not want to make a direct reflection of the API as there will be some refactors and deprecations to the underlying endpoints that will eventual wrangle tiered cache under one banner.

While I could likely remove some of the generic tiered caching work and interact with the existing `argo.go`, I felt it better to contain the interactions in a single file and not to develop dependencies between the two given the upcoming refactor work.
iveelsm: Leaving placeholders for the API changes as those are pending release right now. This should be ready to go @jacobbednarz 

Patch:
diff --git a/.changelog/1149.txt b/.changelog/1149.txt
new file mode 100644
index 000000000..c6c40af07
--- /dev/null
+++ b/.changelog/1149.txt
@@ -0,0 +1,3 @@
+```release-note:enhancement
+tiered_cache: Add support for Tiered Caching interactions for setting Smart and Generic topologies
+```
\ No newline at end of file
diff --git a/tiered_cache.go b/tiered_cache.go
new file mode 100644
index 000000000..77b0ce5bb
--- /dev/null
+++ b/tiered_cache.go
@@ -0,0 +1,316 @@
+package cloudflare
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"net/http"
+	"time"
+)
+
+type TieredCacheType int
+
+const (
+	TieredCacheOff     TieredCacheType = 0
+	TieredCacheGeneric TieredCacheType = 1
+	TieredCacheSmart   TieredCacheType = 2
+)
+
+func (e TieredCacheType) String() string {
+	switch e {
+	case TieredCacheGeneric:
+		return "generic"
+	case TieredCacheSmart:
+		return "smart"
+	case TieredCacheOff:
+		return "off"
+	default:
+		return fmt.Sprintf("%d", int(e))
+	}
+}
+
+type TieredCache struct {
+	Type         TieredCacheType
+	LastModified time.Time
+}
+
+// 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
+// API Reference: 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
+// API Reference: 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
+// API Reference: 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 {
+		var notFoundError *NotFoundError
+		if errors.As(err, &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.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 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 {
+		var notFoundError *NotFoundError
+		if errors.As(err, &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
+}


Test patch:
diff --git a/tiered_cache_test.go b/tiered_cache_test.go
new file mode 100644
index 000000000..c30ada640
--- /dev/null
+++ b/tiered_cache_test.go
@@ -0,0 +1,321 @@
+package cloudflare
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func createSmartTieredCacheHandler(val string, lastModified string) func(http.ResponseWriter, *http.Request) {
+	return func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprintf(w, `{
+			"success": true,
+			"errors": [],
+			"messages": [],
+			"result": {
+				"editable": true,
+				"id": "tiered_cache_smart_topology_enable",
+				"modified_on": "%s",
+				"value": "%s"
+            }
+          }`, lastModified, val)
+	}
+}
+
+func nonexistentSmartTieredCacheHandler() func(http.ResponseWriter, *http.Request) {
+	return func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("content-type", "application/json")
+		w.WriteHeader(404)
+		fmt.Fprintf(w, `{
+			"result": null,
+			"success": false,
+			"errors": [
+				{
+					"code": 1142,
+					"message": "Unable to retrieve tiered_cache_smart_topology_enable setting value. The zone setting does not exist."
+				}
+			],
+			"messages": []
+		}`)
+	}
+}
+
+func createGenericTieredCacheHandler(val string, lastModified string) func(http.ResponseWriter, *http.Request) {
+	return func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprintf(w, `{
+			"success": true,
+			"errors": [],
+			"messages": [],
+			"result": {
+				"id": "tiered_caching",
+				"value": "%s",
+				"modified_on": "%s",
+				"editable": false
+            }
+          }`, val, lastModified)
+	}
+}
+
+func TestGetTieredCache(t *testing.T) {
+	t.Run("can identify when Smart Tiered Cache", func(t *testing.T) {
+		t.Run("is disabled", func(t *testing.T) {
+			setup()
+			defer teardown()
+
+			lastModified := time.Now().Format(time.RFC3339)
+
+			mux.HandleFunc("/zones/"+testZoneID+"/argo/tiered_caching", createGenericTieredCacheHandler("on", lastModified))
+			mux.HandleFunc("/zones/"+testZoneID+"/cache/tiered_cache_smart_topology_enable", createSmartTieredCacheHandler("off", lastModified))
+
+			wanted, _ := time.Parse(time.RFC3339, lastModified)
+			want := TieredCache{
+				Type:         TieredCacheGeneric,
+				LastModified: wanted,
+			}
+
+			got, err := client.GetTieredCache(context.Background(), ZoneIdentifier(testZoneID))
+
+			if assert.NoError(t, err) {
+				assert.Equal(t, want, got)
+			}
+		})
+
+		t.Run("is enabled", func(t *testing.T) {
+			setup()
+			defer teardown()
+
+			lastModified := time.Now().Format(time.RFC3339)
+
+			mux.HandleFunc("/zones/"+testZoneID+"/argo/tiered_caching", createGenericTieredCacheHandler("on", lastModified))
+			mux.HandleFunc("/zones/"+testZoneID+"/cache/tiered_cache_smart_topology_enable", createSmartTieredCacheHandler("on", lastModified))
+
+			wanted, _ := time.Parse(time.RFC3339, lastModified)
+			want := TieredCache{
+				Type:         TieredCacheSmart,
+				LastModified: wanted,
+			}
+
+			got, err := client.GetTieredCache(context.Background(), ZoneIdentifier(testZoneID))
+
+			if assert.NoError(t, err) {
+				assert.Equal(t, want, got)
+			}
+		})
+
+		t.Run("zone setting does not exist", func(t *testing.T) {
+			setup()
+			defer teardown()
+
+			lastModified := time.Now().Format(time.RFC3339)
+
+			mux.HandleFunc("/zones/"+testZoneID+"/argo/tiered_caching", createGenericTieredCacheHandler("on", lastModified))
+			mux.HandleFunc("/zones/"+testZoneID+"/cache/tiered_cache_smart_topology_enable", nonexistentSmartTieredCacheHandler())
+
+			wanted, _ := time.Parse(time.RFC3339, lastModified)
+			want := TieredCache{
+				Type:         TieredCacheGeneric,
+				LastModified: wanted,
+			}
+
+			got, err := client.GetTieredCache(context.Background(), ZoneIdentifier(testZoneID))
+
+			if assert.NoError(t, err) {
+				assert.Equal(t, want, got)
+			}
+		})
+	})
+
+	t.Run("can identify when generic tiered cache", func(t *testing.T) {
+		t.Run("is disabled", func(t *testing.T) {
+			setup()
+			defer teardown()
+
+			lastModified := time.Now().Format(time.RFC3339)
+
+			mux.HandleFunc("/zones/"+testZoneID+"/argo/tiered_caching", createGenericTieredCacheHandler("off", lastModified))
+			mux.HandleFunc("/zones/"+testZoneID+"/cache/tiered_cache_smart_topology_enable", createSmartTieredCacheHandler("on", lastModified))
+
+			wanted, _ := time.Parse(time.RFC3339, lastModified)
+			want := TieredCache{
+				Type:         TieredCacheOff,
+				LastModified: wanted,
+			}
+
+			got, err := client.GetTieredCache(context.Background(), ZoneIdentifier(testZoneID))
+
+			if assert.NoError(t, err) {
+				assert.Equal(t, want, got)
+			}
+		})
+
+		t.Run("is enabled", func(t *testing.T) {
+			setup()
+			defer teardown()
+
+			lastModified := time.Now().Format(time.RFC3339)
+
+			mux.HandleFunc("/zones/"+testZoneID+"/argo/tiered_caching", createGenericTieredCacheHandler("on", lastModified))
+			mux.HandleFunc("/zones/"+testZoneID+"/cache/tiered_cache_smart_topology_enable", nonexistentSmartTieredCacheHandler())
+
+			wanted, _ := time.Parse(time.RFC3339, lastModified)
+			want := TieredCache{
+				Type:         TieredCacheGeneric,
+				LastModified: wanted,
+			}
+
+			got, err := client.GetTieredCache(context.Background(), ZoneIdentifier(testZoneID))
+
+			if assert.NoError(t, err) {
+				assert.Equal(t, want, got)
+			}
+		})
+	})
+
+	t.Run("determines the latest last modified when", func(t *testing.T) {
+		t.Run("smart tiered cache zone setting does not exist", func(t *testing.T) {
+			setup()
+			defer teardown()
+
+			lastModified := time.Now().Format(time.RFC3339)
+
+			mux.HandleFunc("/zones/"+testZoneID+"/argo/tiered_caching", createGenericTieredCacheHandler("on", lastModified))
+			mux.HandleFunc("/zones/"+testZoneID+"/cache/tiered_cache_smart_topology_enable", nonexistentSmartTieredCacheHandler())
+
+			wanted, _ := time.Parse(time.RFC3339, lastModified)
+			want := TieredCache{
+				Type:         TieredCacheGeneric,
+				LastModified: wanted,
+			}
+
+			got, err := client.GetTieredCache(context.Background(), ZoneIdentifier(testZoneID))
+
+			if assert.NoError(t, err) {
+				assert.Equal(t, want, got)
+			}
+		})
+
+		t.Run("generic tiered cache was modified more recently", func(t *testing.T) {
+			setup()
+			defer teardown()
+
+			earlier := time.Now().Add(time.Minute * -5).Format(time.RFC3339)
+			lastModified := time.Now().Format(time.RFC3339)
+
+			mux.HandleFunc("/zones/"+testZoneID+"/argo/tiered_caching", createGenericTieredCacheHandler("on", earlier))
+			mux.HandleFunc("/zones/"+testZoneID+"/cache/tiered_cache_smart_topology_enable", createSmartTieredCacheHandler("on", lastModified))
+
+			wanted, _ := time.Parse(time.RFC3339, lastModified)
+			want := TieredCache{
+				Type:         TieredCacheSmart,
+				LastModified: wanted,
+			}
+
+			got, err := client.GetTieredCache(context.Background(), ZoneIdentifier(testZoneID))
+
+			if assert.NoError(t, err) {
+				assert.Equal(t, want, got)
+			}
+		})
+
+		t.Run("smart tiered cache was modified more recently", func(t *testing.T) {
+			setup()
+			defer teardown()
+
+			earlier := time.Now().Add(time.Minute * -5).Format(time.RFC3339)
+			lastModified := time.Now().Format(time.RFC3339)
+
+			mux.HandleFunc("/zones/"+testZoneID+"/argo/tiered_caching", createGenericTieredCacheHandler("on", lastModified))
+			mux.HandleFunc("/zones/"+testZoneID+"/cache/tiered_cache_smart_topology_enable", createSmartTieredCacheHandler("on", earlier))
+
+			wanted, _ := time.Parse(time.RFC3339, lastModified)
+			want := TieredCache{
+				Type:         TieredCacheSmart,
+				LastModified: wanted,
+			}
+
+			got, err := client.GetTieredCache(context.Background(), ZoneIdentifier(testZoneID))
+
+			if assert.NoError(t, err) {
+				assert.Equal(t, want, got)
+			}
+		})
+	})
+}
+
+func TestSetTieredCache(t *testing.T) {
+	t.Run("can enable tiered caching", func(t *testing.T) {
+		t.Run("using smart caching", func(t *testing.T) {
+			setup()
+			defer teardown()
+
+			lastModified := time.Now().Format(time.RFC3339)
+
+			mux.HandleFunc("/zones/"+testZoneID+"/argo/tiered_caching", createGenericTieredCacheHandler("on", lastModified))
+			mux.HandleFunc("/zones/"+testZoneID+"/cache/tiered_cache_smart_topology_enable", createSmartTieredCacheHandler("on", lastModified))
+
+			wanted, _ := time.Parse(time.RFC3339, lastModified)
+			want := TieredCache{
+				Type:         TieredCacheSmart,
+				LastModified: wanted,
+			}
+
+			got, err := client.SetTieredCache(context.Background(), ZoneIdentifier(testZoneID), TieredCacheSmart)
+
+			if assert.NoError(t, err) {
+				assert.Equal(t, want, got)
+			}
+		})
+
+		t.Run("use generic caching", func(t *testing.T) {
+			setup()
+			defer teardown()
+
+			lastModified := time.Now().Format(time.RFC3339)
+
+			mux.HandleFunc("/zones/"+testZoneID+"/argo/tiered_caching", createGenericTieredCacheHandler("on", lastModified))
+			mux.HandleFunc("/zones/"+testZoneID+"/cache/tiered_cache_smart_topology_enable", nonexistentSmartTieredCacheHandler())
+
+			wanted, _ := time.Parse(time.RFC3339, lastModified)
+			want := TieredCache{
+				Type:         TieredCacheGeneric,
+				LastModified: wanted,
+			}
+
+			got, err := client.SetTieredCache(context.Background(), ZoneIdentifier(testZoneID), TieredCacheGeneric)
+
+			if assert.NoError(t, err) {
+				assert.Equal(t, want, got)
+			}
+		})
+	})
+}
+
+func TestDeleteTieredCache(t *testing.T) {
+	t.Run("can disable tiered caching", func(t *testing.T) {
+		setup()
+		defer teardown()
+
+		lastModified := time.Now().Format(time.RFC3339)
+
+		mux.HandleFunc("/zones/"+testZoneID+"/argo/tiered_caching", createGenericTieredCacheHandler("off", lastModified))
+		mux.HandleFunc("/zones/"+testZoneID+"/cache/tiered_cache_smart_topology_enable", nonexistentSmartTieredCacheHandler())
+
+		wanted, _ := time.Parse(time.RFC3339, lastModified)
+		want := TieredCache{
+			Type:         TieredCacheOff,
+			LastModified: wanted,
+		}
+
+		got, err := client.GetTieredCache(context.Background(), ZoneIdentifier(testZoneID))
+
+		if assert.NoError(t, err) {
+			assert.Equal(t, want, got)
+		}
+	})
+}