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/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/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) 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