From 339b6752af88426cb4c69b964e403f57174c5ecf Mon Sep 17 00:00:00 2001 From: kevinhowbrook Date: Wed, 13 Feb 2019 12:55:37 +0000 Subject: [PATCH] Fix AttributeError on Cloudflare cache invalidation (#5150) --- CHANGELOG.txt | 1 + docs/releases/2.5.rst | 1 + wagtail/contrib/frontend_cache/backends.py | 2 +- wagtail/contrib/frontend_cache/tests.py | 47 +++++++++++++++++++++- 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index a54822c93a..659a27a1c3 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -39,6 +39,7 @@ Changelog * Fix: User add/edit forms now support form widgets with JS/CSS media (Damian Grinwis) * Fix: Rich text processing now preserves non-breaking spaces instead of converting them to normal spaces (Wesley van Lee) * Fix: Prevent autocomplete dropdowns from appearing over date choosers on Chrome (Kevin Howbrook) + * Fix: Prevent crash when logging HTTP errors from Cloudflare (Kevin Howbrook) 2.4 (19.12.2018) diff --git a/docs/releases/2.5.rst b/docs/releases/2.5.rst index ec0c234261..6ccb064126 100644 --- a/docs/releases/2.5.rst +++ b/docs/releases/2.5.rst @@ -55,6 +55,7 @@ Bug fixes * User add/edit forms now support form widgets with JS/CSS media (Damian Grinwis) * Rich text processing now preserves non-breaking spaces instead of converting them to normal spaces (Wesley van Lee) * Prevent autocomplete dropdowns from appearing over date choosers on Chrome (Kevin Howbrook) + * Prevent crash when logging HTTP errors on Cloudflare cache purging (Kevin Howbrook) Upgrade considerations diff --git a/wagtail/contrib/frontend_cache/backends.py b/wagtail/contrib/frontend_cache/backends.py index 88424b1ce1..281594d8af 100644 --- a/wagtail/contrib/frontend_cache/backends.py +++ b/wagtail/contrib/frontend_cache/backends.py @@ -100,7 +100,7 @@ class CloudflareBackend(BaseBackend): except requests.exceptions.HTTPError as e: for url in urls: - logger.error("Couldn't purge '%s' from Cloudflare. HTTPError: %d %s", url, e.response.status_code, e.message) + logging.exception("Couldn't purge '%s' from Cloudflare. HTTPError: %d", url, e.response.status_code) return if response_json['success'] is False: diff --git a/wagtail/contrib/frontend_cache/tests.py b/wagtail/contrib/frontend_cache/tests.py index 0f25faf25e..5bebdc02b7 100644 --- a/wagtail/contrib/frontend_cache/tests.py +++ b/wagtail/contrib/frontend_cache/tests.py @@ -1,6 +1,7 @@ from unittest import mock from urllib.error import HTTPError, URLError +import requests from django.core.exceptions import ImproperlyConfigured from django.test import TestCase from django.test.utils import override_settings @@ -78,12 +79,23 @@ class TestBackendConfiguration(TestCase): hdrs={}, fp=None ) - self._test_http_with_side_effect(urlopen_side_effect=http_error) + with self.assertLogs(level='ERROR') as log_output: + self._test_http_with_side_effect(urlopen_side_effect=http_error) + + self.assertIn( + "Couldn't purge 'http://www.wagtail.io/home/events/christmas/' from HTTP cache. HTTPError: 500 Internal Server Error", + log_output.output[0] + ) def test_http_urlerror(self): """Test that `HTTPBackend.purge` can handle `URLError`""" url_error = URLError(reason='just for tests') - self._test_http_with_side_effect(urlopen_side_effect=url_error) + with self.assertLogs(level='ERROR') as log_output: + self._test_http_with_side_effect(urlopen_side_effect=url_error) + self.assertIn( + "Couldn't purge 'http://www.wagtail.io/home/events/christmas/' from HTTP cache. URLError: just for tests", + log_output.output[0] + ) @mock.patch('wagtail.contrib.frontend_cache.backends.urlopen') def _test_http_with_side_effect(self, urlopen_mock, urlopen_side_effect): @@ -308,3 +320,34 @@ class TestPurgeBatchClass(TestCase): batch.purge() self.assertEqual(batch.urls, ['http://localhost/events/', 'http://localhost/events/past/', 'http://localhost/foo']) + + @mock.patch('wagtail.contrib.frontend_cache.backends.requests.delete') + def test_http_error_on_cloudflare_purge_batch(self, requests_delete_mock): + backend_settings = { + 'cloudflare': { + 'BACKEND': 'wagtail.contrib.frontend_cache.backends.CloudflareBackend', + 'EMAIL': 'test@test.com', + 'TOKEN': 'this is the token', + 'ZONEID': 'this is a zone id', + }, + } + + class MockResponse: + def __init__(self, status_code=200): + self.status_code = status_code + + http_error = requests.exceptions.HTTPError(response=MockResponse(status_code=500)) + requests_delete_mock.side_effect = http_error + + page = EventIndex.objects.get(url_path='/home/events/') + + batch = PurgeBatch() + batch.add_page(page) + + with self.assertLogs(level='ERROR') as log_output: + batch.purge(backend_settings=backend_settings) + + self.assertIn( + "Couldn't purge 'http://localhost/events/' from Cloudflare. HTTPError: 500", + log_output.output[0] + )