From a5b544631bbde21b28ff749dcd53d35e5f5bf64d Mon Sep 17 00:00:00 2001 From: Albert O'Connor Date: Tue, 8 Nov 2016 14:23:35 -0500 Subject: [PATCH] Update the Cloudflare backend to use the v4 API Fixes #3146 --- CHANGELOG.txt | 1 + CONTRIBUTORS.rst | 1 + docs/reference/contrib/frontendcache.rst | 8 ++- docs/releases/1.8.rst | 20 ++++++++ setup.py | 1 + .../contrib/wagtailfrontendcache/backends.py | 50 +++++++++++++------ wagtail/contrib/wagtailfrontendcache/tests.py | 3 ++ 7 files changed, 67 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 90c7027c8f..5fef53f387 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -15,6 +15,7 @@ Changelog * `PageChooserBlock` now accepts a `target_model` option to specify the required page type (Tim Heap) * Modeladmin forms now respect `fields` / `exclude` options passed on custom model forms (Thejaswi Puthraya) * Added new StreamField block type `StaticBlock` (Benoît Vogel) + * Updated Cloudflare cache module to use the v4 API (Albert O'Connor) * Fix: `AbstractForm` now respects custom `get_template` methods on the page model (Gagaro) * Fix: Use specific page model for the parent page in the explore index (Gagaro) * Fix: Remove responsive styles in embed when there is no ratio available (Gagaro) diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index 2fd3f7cc4a..2b50635e7a 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -184,6 +184,7 @@ Contributors * Thejaswi Puthraya * Benoît Vogel * Manuel E. Gutierrez +* Albert O'Connor Translators =========== diff --git a/docs/reference/contrib/frontendcache.rst b/docs/reference/contrib/frontendcache.rst index f62d8591e5..8f98a793cf 100644 --- a/docs/reference/contrib/frontendcache.rst +++ b/docs/reference/contrib/frontendcache.rst @@ -60,12 +60,17 @@ Finally, make sure you have configured your frontend cache to accept PURGE reque - `Squid `_ +.. _frontendcache_cloudflare: + Cloudflare ^^^^^^^^^^ Firstly, you need to register an account with Cloudflare if you haven't already got one. You can do this here: `Cloudflare Sign up `_ -Add an item into the ``WAGTAILFRONTENDCACHE`` and set the ``BACKEND`` parameter to ``wagtail.contrib.wagtailfrontendcache.backends.CloudflareBackend``. This backend requires two extra parameters, ``EMAIL`` (your Cloudflare account email) and ``TOKEN`` (your API token from Cloudflare). +Add an item into the ``WAGTAILFRONTENDCACHE`` and set the ``BACKEND`` parameter to ``wagtail.contrib.wagtailfrontendcache.backends.CloudflareBackend``. This backend requires three extra parameters, ``EMAIL`` (your Cloudflare account email), ``TOKEN`` (your API token from Cloudflare), and ``ZONEID`` (for zone id for your domain, see below). + +To find the ``ZONEID`` for your domain, read the `Cloudflare API Documentation `_ + .. code-block:: python @@ -76,6 +81,7 @@ Add an item into the ``WAGTAILFRONTENDCACHE`` and set the ``BACKEND`` parameter 'BACKEND': 'wagtail.contrib.wagtailfrontendcache.backends.CloudflareBackend', 'EMAIL': 'your-cloudflare-email-address@example.com', 'TOKEN': 'your cloudflare api token', + 'ZONEID': 'your cloudflare domain zone id', }, } diff --git a/docs/releases/1.8.rst b/docs/releases/1.8.rst index b55d271493..5ef19bd250 100644 --- a/docs/releases/1.8.rst +++ b/docs/releases/1.8.rst @@ -41,6 +41,7 @@ Minor features * ``PageChooserBlock`` now accepts a ``target_model`` option to specify the required page type (Tim Heap) * Modeladmin forms now respect ``fields`` / ``exclude`` options passed on custom model forms (Thejaswi Puthraya) * Added new StreamField block type ``StaticBlock`` for blocks that occupy a position in a stream but otherwise have no configuration; see :ref:`streamfield_staticblock` (Benoît Vogel) + * Updated Cloudflare cache module to use the v4 API (Albert O'Connor) Bug fixes @@ -103,3 +104,22 @@ As a precaution against accidental data loss, this release introduces a new "bul operations = [ migrations.RunPython(add_bulk_delete_permission, remove_bulk_delete_permission), ] + + +Cloudflare cache module now requires a ``ZONEID`` setting +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``wagtail.contrib.wagtailfrontendcache.backends.CloudflareBackend`` module has been updated to use Cloudflare's v4 API, replacing the previous v1 implementation (which is `unsupported as of November 9th, 2016 `_). The new API requires users to supply a *zone identifier*, which should be passed as the ``ZONEID`` field of the ``WAGTAILFRONTENDCACHE`` setting: + +.. code-block:: python + + WAGTAILFRONTENDCACHE = { + 'cloudflare': { + 'BACKEND': 'wagtail.contrib.wagtailfrontendcache.backends.CloudflareBackend', + 'EMAIL': 'your-cloudflare-email-address@example.com', + 'TOKEN': 'your cloudflare api token', + 'ZONEID': 'your cloudflare domain zone id', + }, + } + +For details of how to obtain the zone identifier, see `the Cloudflare API documentation `_. diff --git a/setup.py b/setup.py index e9f2ec0f90..bc3e34e61f 100755 --- a/setup.py +++ b/setup.py @@ -31,6 +31,7 @@ install_requires = [ "html5lib>=0.999,<1", "Unidecode>=0.04.14", "Willow>=0.4,<0.5", + "requests>=2.11.1,<3.0", ] # Testing dependencies diff --git a/wagtail/contrib/wagtailfrontendcache/backends.py b/wagtail/contrib/wagtailfrontendcache/backends.py index ea2b7aa1c4..0c58779531 100644 --- a/wagtail/contrib/wagtailfrontendcache/backends.py +++ b/wagtail/contrib/wagtailfrontendcache/backends.py @@ -1,12 +1,12 @@ from __future__ import absolute_import, unicode_literals -import json import logging import uuid +import requests from django.core.exceptions import ImproperlyConfigured from django.utils.six.moves.urllib.error import HTTPError, URLError -from django.utils.six.moves.urllib.parse import urlencode, urlparse, urlunparse +from django.utils.six.moves.urllib.parse import urlparse, urlunparse from django.utils.six.moves.urllib.request import Request, urlopen from wagtail import __version__ @@ -65,26 +65,44 @@ class CloudflareBackend(BaseBackend): def __init__(self, params): self.cloudflare_email = params.pop('EMAIL') self.cloudflare_token = params.pop('TOKEN') + self.cloudflare_zoneid = params.pop('ZONEID') def purge(self, url): try: - response = urlopen('https://www.cloudflare.com/api_json.html', data=urlencode({ - 'email': self.cloudflare_email, - 'tkn': self.cloudflare_token, - 'a': 'zone_file_purge', - 'z': urlparse(url).netloc, - 'url': url - }).encode('utf-8')) - except HTTPError as e: - logger.error("Couldn't purge '%s' from Cloudflare. HTTPError: %d %s", url, e.code, e.reason) + purge_url = 'https://api.cloudflare.com/client/v4/zones/{0}/purge_cache'.format(self.cloudflare_zoneid) + + headers = { + "X-Auth-Email": self.cloudflare_email, + "X-Auth-Key": self.cloudflare_token, + "Content-Type": "application/json", + } + + data = {"files": [url]} + + response = requests.delete( + purge_url, + json=data, + headers=headers, + ) + + try: + response_json = response.json() + except ValueError: + if response.status_code != 200: + response.raise_for_status() + else: + logger.error("Couldn't purge '%s' from Cloudflare. Unexpected JSON parse error.", url) + + except requests.exceptions.HTTPError as e: + logger.error("Couldn't purge '%s' from Cloudflare. HTTPError: %d %s", url, e.response.status_code, e.message) return - except URLError as e: - logger.error("Couldn't purge '%s' from Cloudflare. URLError: %s", url, e.reason) + except requests.exceptions.InvalidURL as e: + logger.error("Couldn't purge '%s' from Cloudflare. URLError: %s", url, e.message) return - response_json = json.loads(response.read().decode('utf-8')) - if response_json['result'] == 'error': - logger.error("Couldn't purge '%s' from Cloudflare. Cloudflare error '%s'", url, response_json['msg']) + if response_json['success'] is False: + error_messages = ', '.join([err['message'] for err in response_json['errors']]) + logger.error("Couldn't purge '%s' from Cloudflare. Cloudflare errors '%s'", url, error_messages) return diff --git a/wagtail/contrib/wagtailfrontendcache/tests.py b/wagtail/contrib/wagtailfrontendcache/tests.py index 5d3edbae14..76964b5cc4 100644 --- a/wagtail/contrib/wagtailfrontendcache/tests.py +++ b/wagtail/contrib/wagtailfrontendcache/tests.py @@ -39,6 +39,7 @@ class TestBackendConfiguration(TestCase): 'BACKEND': 'wagtail.contrib.wagtailfrontendcache.backends.CloudflareBackend', 'EMAIL': 'test@test.com', 'TOKEN': 'this is the token', + 'ZONEID': 'this is a zone id', }, }) @@ -94,6 +95,7 @@ class TestBackendConfiguration(TestCase): 'BACKEND': 'wagtail.contrib.wagtailfrontendcache.backends.CloudflareBackend', 'EMAIL': 'test@test.com', 'TOKEN': 'this is the token', + 'ZONEID': 'this is a zone id', } }) @@ -109,6 +111,7 @@ class TestBackendConfiguration(TestCase): 'BACKEND': 'wagtail.contrib.wagtailfrontendcache.backends.CloudflareBackend', 'EMAIL': 'test@test.com', 'TOKEN': 'this is the token', + 'ZONEID': 'this is a zone id', } }, backends=['cloudflare'])