From 107f83885216e0e1aab5453b27f58030a609fdaf Mon Sep 17 00:00:00 2001 From: Ee Durbin Date: Wed, 25 Sep 2024 10:42:11 -0400 Subject: [PATCH 1/2] Add a utility for purging Fastly by Surrogate-Key --- fastly/utils.py | 20 ++++++++++++++++++++ pydotorg/settings/base.py | 10 ++++++++-- pydotorg/settings/cabotage.py | 8 +++++++- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/fastly/utils.py b/fastly/utils.py index 42637aeb2..8bc9a8b80 100644 --- a/fastly/utils.py +++ b/fastly/utils.py @@ -20,3 +20,23 @@ def purge_url(path): return response return None + + +def purge_surrogate_key(key): + """ + Purge a Fastly.com Surrogate-Key given a key. + """ + if settings.DEBUG: + return + + api_key = getattr(settings, 'FASTLY_API_KEY', None) + service_id = getattr(settings, 'FASTLY_SERVICE_ID', None) + if api_key and service_id: + response = requests.request( + "POST", + f'https://api.fastly.com/service/{service_id}/purge/{key}', + headers={'Fastly-Key': api_key}, + ) + return response + + return None diff --git a/pydotorg/settings/base.py b/pydotorg/settings/base.py index 2c392b355..70ec472f9 100644 --- a/pydotorg/settings/base.py +++ b/pydotorg/settings/base.py @@ -285,8 +285,10 @@ MAILING_LIST_PSF_MEMBERS = "psf-members-announce-request@python.org" ### Fastly ### -FASTLY_API_KEY = False # Set to Fastly API key in production to allow pages to - # be purged on save +FASTLY_SERVICE_ID = False # Set to a Fastly Service ID in production to allow + # purges by Surrogate-Key +FASTLY_API_KEY = False # Set to Fastly API key in production to allow + # pages to be purged on save # Jobs JOB_THRESHOLD_DAYS = 90 @@ -349,6 +351,10 @@ GLOBAL_SURROGATE_KEY = 'pydotorg-app' +### pydotorg.settings.cabotage.add_surrogate_keys_to_static + +STATIC_SURROGATE_KEY = 'pydotorg-static' + ### PyCon Integration for Sponsor Voucher Codes PYCON_API_KEY = config("PYCON_API_KEY", default="deadbeef-dead-beef-dead-beefdeadbeef") PYCON_API_SECRET = config("PYCON_API_SECRET", default="deadbeef-dead-beef-dead-beefdeadbeef") diff --git a/pydotorg/settings/cabotage.py b/pydotorg/settings/cabotage.py index 4661fbf66..2effbacf7 100644 --- a/pydotorg/settings/cabotage.py +++ b/pydotorg/settings/cabotage.py @@ -53,6 +53,11 @@ }, } +def add_surrogate_keys_to_static(headers, path, url): + headers['Surrogate-Key'] = STATIC_SURROGATE_KEY + +WHITENOISE_ADD_HEADERS_FUNCTION = add_surrogate_keys_to_static + EMAIL_HOST = config('EMAIL_HOST') EMAIL_HOST_USER = config('EMAIL_HOST_USER') EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD') @@ -63,7 +68,8 @@ PEP_REPO_PATH = None PEP_ARTIFACT_URL = config('PEP_ARTIFACT_URL') -# Fastly API Key +# Fastly +FASTLY_SERVICE_ID = config('FASTLY_SERVICE_ID') FASTLY_API_KEY = config('FASTLY_API_KEY') SECURE_SSL_REDIRECT = True From 4729534c7391522bb4889b05fba7cd0d43cdef09 Mon Sep 17 00:00:00 2001 From: Ee Durbin Date: Wed, 25 Sep 2024 10:51:08 -0400 Subject: [PATCH 2/2] add a postdeploy step to purge surroage-keys for static files --- Procfile | 1 + pydotorg/management/__init__.py | 0 pydotorg/management/commands/__init__.py | 0 pydotorg/management/commands/postdeploy.py | 14 ++++++++++++++ 4 files changed, 15 insertions(+) create mode 100644 pydotorg/management/__init__.py create mode 100644 pydotorg/management/commands/__init__.py create mode 100644 pydotorg/management/commands/postdeploy.py diff --git a/Procfile b/Procfile index 16deb5f5b..cce927ff8 100644 --- a/Procfile +++ b/Procfile @@ -2,3 +2,4 @@ release: python manage.py migrate --noinput web: bin/start-nginx gunicorn -c gunicorn.conf pydotorg.wsgi worker: celery -A pydotorg worker -l INFO worker-beat: celery -A pydotorg beat -l INFO --scheduler django_celery_beat.schedulers:DatabaseScheduler +postdeploy: python manage.py postdeploy diff --git a/pydotorg/management/__init__.py b/pydotorg/management/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pydotorg/management/commands/__init__.py b/pydotorg/management/commands/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pydotorg/management/commands/postdeploy.py b/pydotorg/management/commands/postdeploy.py new file mode 100644 index 000000000..17c518e31 --- /dev/null +++ b/pydotorg/management/commands/postdeploy.py @@ -0,0 +1,14 @@ +from django.core.management.base import BaseCommand +from django.conf import settings + +from fastly.utils import purge_surrogate_key + + +class Command(BaseCommand): + """ Do things after deployment is complete """ + + def handle(self, *args, **kwargs): + # If we have a STATIC_SURROGATE_KEY set, purge static files to ensure + # that anything cached mid-deploy is ignored (like 404s). + if settings.STATIC_SURROGATE_KEY: + purge_surrogate_key(settings.STATIC_SURROGATE_KEY)