Skip to content

Commit

Permalink
Add Cloudflare utilities
Browse files Browse the repository at this point in the history
  • Loading branch information
harrislapiroff committed Apr 25, 2018
1 parent c121520 commit f94d459
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 0 deletions.
Empty file added cloudflare/__init__.py
Empty file.
Empty file added cloudflare/tests/__init__.py
Empty file.
52 changes: 52 additions & 0 deletions cloudflare/tests/test_cache_tag_purge.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from unittest.mock import patch

from django.test import TestCase, override_settings

from cloudflare.utils import purge_tags_from_cache, purge_all_from_cache


@override_settings(WAGTAILFRONTENDCACHE={
'cloudflare': {
'BACKEND': 'wagtail.contrib.wagtailfrontendcache.backends.CloudflareBackend',
'EMAIL': 'CLOUDFLARE_FAKE_EMAIL',
'TOKEN': 'CLOUDFLARE_FAKE_TOKEN',
'ZONEID': 'CLOUDFLARE_FAKE_ZONE',
}
})
class TestCacheTags(TestCase):

@patch('cloudflare.utils.requests.delete')
def test_cache_tag_purge(self, requests_delete):
"""
Should fire an appropriate looking HTTP request to Cloudflare's
purge API endpoint
"""
purge_tags_from_cache(['tag-1', 'tag-2'])
requests_delete.assert_called_with(
'https://api.cloudflare.com/client/v4/zones/CLOUDFLARE_FAKE_ZONE/purge_cache',
json={
'tags': ['tag-1', 'tag-2']
},
headers={
'X-Auth-Email': 'CLOUDFLARE_FAKE_EMAIL',
'Content-Type': 'application/json',
'X-Auth-Key': 'CLOUDFLARE_FAKE_TOKEN'
}
)

@patch('cloudflare.utils.requests.delete')
def test_cache_purge_all(self, requests_delete):
"""
Should fire an appropriate looking HTTP request to Cloudflare's
purge API endpoint
"""
purge_all_from_cache()
requests_delete.assert_called_with(
'https://api.cloudflare.com/client/v4/zones/CLOUDFLARE_FAKE_ZONE/purge_cache',
json={},
headers={
'X-Auth-Email': 'CLOUDFLARE_FAKE_EMAIL',
'Content-Type': 'application/json',
'X-Auth-Key': 'CLOUDFLARE_FAKE_TOKEN'
}
)
72 changes: 72 additions & 0 deletions cloudflare/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import logging
import requests
import json

from typing import Iterable

from wagtail.contrib.wagtailfrontendcache.utils import get_backends
from wagtail.contrib.wagtailfrontendcache.backends import CloudflareBackend


logger = logging.getLogger('wagtail.frontendcache')


def _for_every_cloudflare_backend(func: callable) -> callable:
"Decorator to run a function once for every Cloudflare backend"

def inner(*args, backend_settings=None, backends=None, **kwargs):
for backend_name, backend in get_backends(backend_settings=backend_settings, backends=backends).items():
if not isinstance(backend, CloudflareBackend):
continue
func(*args, backend=backend, **kwargs)

return inner


def _purge(backend: CloudflareBackend, data={}) -> None:
"Send a delete request to the Cloudflare API"
purge_url = 'https://api.cloudflare.com/client/v4/zones/{0}/purge_cache'.format(backend.cloudflare_zoneid)
string_data = json.dumps(data)

response = requests.delete(
purge_url,
json=data,
headers={
"X-Auth-Email": backend.cloudflare_email,
"X-Auth-Key": backend.cloudflare_token,
"Content-Type": "application/json",
},
)

try:
response_json = response.json()
except ValueError:
if response.status_code != 200:
response.raise_for_status()
else:
logger.error("Couldn't purge from Cloudflare with data: %s. Unexpected JSON parse error.", string_data)

except requests.exceptions.HTTPError as e:
logger.error("Couldn't purge from Cloudflare with data: %s. HTTPError: %d %s", string_data, e.response.status_code, e.message)
return

except requests.exceptions.InvalidURL as e:
logger.error("Couldn't purge from Cloudflare with data: %s. URLError: %s", string_data, e.message)
return

if response_json['success'] is False:
error_messages = ', '.join([str(err['message']) for err in response_json['errors']])
logger.error("Couldn't purge from Cloudflare with data: %s. Cloudflare errors '%s'", string_data, error_messages)
return


@_for_every_cloudflare_backend
def purge_tags_from_cache(tags: Iterable, backend: CloudflareBackend) -> None:
"Purge tags by list. Requires an enterprise Cloudflare subscription"
_purge(backend=backend, data={"tags": tags})


@_for_every_cloudflare_backend
def purge_all_from_cache(backend: CloudflareBackend) -> None:
"Purge an entire zone"
_purge(backend=backend)
1 change: 1 addition & 0 deletions securedrop/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
'accounts',
'autocomplete',
'blog',
'cloudflare',
'common',
'home',
'marketing',
Expand Down

0 comments on commit f94d459

Please sign in to comment.