From 37eb3a614a32701b3f66e9d6797ce8071e278dea Mon Sep 17 00:00:00 2001 From: Tanner Prestegard <tanner.prestegard@ligo.org> Date: Wed, 26 Jun 2019 11:10:46 -0500 Subject: [PATCH] Add maintenance mode functionality --- config/settings/base.py | 5 +++ config/settings/container/base.py | 12 ++++++ gracedb/core/decorators.py | 9 +++++ gracedb/core/middleware/maintenance.py | 55 ++++++++++++++++++++++++++ gracedb/templates/maintenance.html | 36 +++++++++++++++++ 5 files changed, 117 insertions(+) create mode 100644 gracedb/core/decorators.py create mode 100644 gracedb/core/middleware/maintenance.py create mode 100644 gracedb/templates/maintenance.html diff --git a/config/settings/base.py b/config/settings/base.py index 8b3c9d067..4c0156b30 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -21,6 +21,10 @@ def get_from_env(envvar, default_value=None, fail_if_not_found=True): 'Could not get environment variable {0}'.format(envvar)) return value +# Maintenance mode +MAINTENANCE_MODE = False +MAINTENANCE_MODE_MESSAGE = None + # Version --------------------------------------------------------------------- PROJECT_VERSION = '2.6.3' @@ -307,6 +311,7 @@ AUTHENTICATION_BACKENDS = [ # List of middleware classes to use. MIDDLEWARE = [ + 'core.middleware.maintenance.MaintenanceModeMiddleware', 'events.middleware.PerformanceMiddleware', 'core.middleware.accept.AcceptMiddleware', 'core.middleware.api.ClientVersionMiddleware', diff --git a/config/settings/container/base.py b/config/settings/container/base.py index 54943380b..8fad4fa12 100644 --- a/config/settings/container/base.py +++ b/config/settings/container/base.py @@ -61,6 +61,18 @@ TWILIO_AUTH_TOKEN = os.environ.get('DJANGO_TWILIO_AUTH_TOKEN', None) if TWILIO_AUTH_TOKEN is None: raise ImproperlyConfigured('Could not get Twilio auth token from envvars.') +# Get maintenance mode settings from environment +maintenance_mode = get_from_env( + 'DJANGO_MAINTENANCE_MODE_ACTIVE', + default_value=False, + fail_if_not_found=False +) +if (isinstance(maintenance_mode, str) and + maintenance_mode.lower() in ['true', 't', '1']): + MAINTENANCE_MODE = True +MAINTENANCE_MODE_MESSAGE = \ + get_from_env('DJANGO_MAINTENANCE_MODE_MESSAGE', fail_if_not_found=False) + # Get email settings from environment EMAIL_BACKEND = 'django_ses.SESBackend' AWS_SES_ACCESS_KEY_ID = get_from_env('AWS_SES_ACCESS_KEY_ID') diff --git a/gracedb/core/decorators.py b/gracedb/core/decorators.py new file mode 100644 index 000000000..9ba4d4393 --- /dev/null +++ b/gracedb/core/decorators.py @@ -0,0 +1,9 @@ +from functools import wraps + + +def ignore_maintenance_mode(view): + @wraps(view) + def inner(request, *args, **kwargs): + return view(request, *args, **kwargs) + inner.__dict__['ignore_maintenance_mode'] = True + return inner diff --git a/gracedb/core/middleware/maintenance.py b/gracedb/core/middleware/maintenance.py new file mode 100644 index 000000000..a60fac9d9 --- /dev/null +++ b/gracedb/core/middleware/maintenance.py @@ -0,0 +1,55 @@ +from django.conf import settings +from django.http import HttpResponse +from django.shortcuts import render +from django.urls import resolve + +import logging + +# Set up logger +logger = logging.getLogger(__name__) + + +class MaintenanceModeMiddleware(object): + accept_header_name = 'HTTP_ACCEPT' + default_message = 'The site is temporarily down for maintenance.' + + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + # Process request ----------------------------------------------------- + if settings.MAINTENANCE_MODE is True: + + # Check if the view specifies to ignore maintenance mode + ignore_maintenance = \ + self.check_for_ignore_maintenance_mode(request) + + if not ignore_maintenance: + # Get message to display + maintenance_message = self.get_message() + + accept_header = request.META.get(self.accept_header_name, None) + if accept_header and 'text/html' in accept_header: + # Attempt to handle browsers + context = {'message': maintenance_message} + return render(request, 'maintenance.html', context=context, + status=503) + else: + # Anything else (likely client API requests) + return HttpResponse(maintenance_message, status=503) + + # Otherwise, get response and return with no further processing ------- + response = self.get_response(request) + return response + + @staticmethod + def check_for_ignore_maintenance_mode(request): + resolver_match = resolve(request.path) + view_func = resolver_match.func + return view_func.__dict__.get('ignore_maintenance_mode', False) + + def get_message(self): + message = settings.MAINTENANCE_MODE_MESSAGE + if message is None: + message = self.default_message + return message diff --git a/gracedb/templates/maintenance.html b/gracedb/templates/maintenance.html new file mode 100644 index 000000000..3348159e9 --- /dev/null +++ b/gracedb/templates/maintenance.html @@ -0,0 +1,36 @@ +{% extends "base.html" %} + +{% block title %}Maintenance{% endblock %} +{% block heading %}{% endblock %} + +{% block headcontents %} +{{ block.super }} +<style> +.maintenance-message { + background-color: red; + color: white; + text-align: center; + display: inline-block; + border-radius: 5px; + padding: 10px; + min-width: 400px; + max-width: 600px; + box-shadow: 0 0 20px rgba(0,0,0,.2); +} +.maintenance-message p { + font-weight: bold; + font-size: 2rem; + margin: 0; +} +</style> +{% endblock %} + +{% block content %} + +<div align="center" style="padding: 30px;"> + <div class="maintenance-message"> + <p>{{ message | safe }}</p> + </div> +</div> + +{% endblock %} -- GitLab