Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Maintenance mode toggle #4365

Merged
merged 8 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ def setUp(self):
self.AWARD_MAX = 1500
self.AUDITEE_UEI = "AAA123456BBB"
self.award1 = {
"award_reference": f"AWARD-{generate_random_integer(self.AWARD_MIN,self.AWARD_MAX)}"
"award_reference": f"AWARD-{generate_random_integer(self.AWARD_MIN, self.AWARD_MAX)}"
}
self.award2 = {
"award_reference": f"AWARD-{generate_random_integer(self.AWARD_MIN *2,self.AWARD_MAX *2)}"
"award_reference": f"AWARD-{generate_random_integer(self.AWARD_MIN * 2, self.AWARD_MAX * 2)}"
}
self.award3 = {
"award_reference": f"AWARD-{generate_random_integer(self.AWARD_MIN *3,self.AWARD_MAX *3)}"
"award_reference": f"AWARD-{generate_random_integer(self.AWARD_MIN * 3, self.AWARD_MAX * 3)}"
}
self.award_with_longer_ref = {"award_reference": "AWARD-00123"}
self.award_with_shorter_ref = {"award_reference": "AWARD-0123"}
Expand Down
38 changes: 38 additions & 0 deletions backend/audit/management/commands/maintenance_mode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from config import middleware
from django.core.management.base import BaseCommand
import logging

logger = logging.getLogger(__name__)


class Command(BaseCommand):
"""
Django management command for switching "Maintenance Mode".
When switched on, the entire site and requests will feed through
/config/middleware.py.
"""

def add_arguments(self, parser):
parser.add_argument(
"--on",
action="store_true",
help="Activates maintenance mode, which disables user access.",
)
parser.add_argument(
"--off",
action="store_true",
help="Deactivates maintenance mode, which re-enables user access.",
)

def handle(self, *args, **options):
print(
f"Starting switch... Maintenance mode is currently set to {middleware.is_maintenance_on()}."
)
if options.get("off"):
middleware.change_maintenance(False)
logger.info("MAINTENANCE_MODE OFF")
elif options.get("on"):
middleware.change_maintenance(True)
logger.info("MAINTENANCE_MODE ON")
else:
print("Invalid syntax. Please enter this command with --on or --off.")
2 changes: 2 additions & 0 deletions backend/audit/views/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .audit_info_form_view import AuditInfoFormView
from .home import Home
from .home import Maintenance
from .manage_submission import ManageSubmissionView
from .manage_submission_access import (
ChangeOrAddRoleView,
Expand Down Expand Up @@ -50,6 +51,7 @@
EditSubmission,
ExcelFileHandlerView,
Home,
Maintenance,
ManageSubmissionView,
MySubmissions,
no_robots,
Expand Down
13 changes: 13 additions & 0 deletions backend/audit/views/home.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,16 @@ def get(self, request, *args, **kwargs):
template_name = "home.html"
extra_context = {"DISABLE_AUTH": settings.DISABLE_AUTH}
return render(request, template_name, extra_context)


class Maintenance(generic.View):
"""
This is the redirected path for Maintenance mode.

It will return the home template with an error status for every single request
so long as maintenance is enabled.
"""

def get(self, request, *args, **kwargs):
template_name = "503.html"
return render(request, template_name)
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def xform_construct_award_references(audits, findings):
# Transformation recorded.
e2a = {}
for index, audit in enumerate(audits):
e2a[audit.ELECAUDITSID] = f"AWARD-{index+1:04d}"
e2a[audit.ELECAUDITSID] = f"AWARD-{index + 1:04d}"
award_references = []
change_records = []
for find in findings:
Expand Down
Empty file added backend/config/MAINTENANCE_MODE
Empty file.
74 changes: 74 additions & 0 deletions backend/config/middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from dissemination.file_downloads import file_exists
from django.conf import settings
from django.shortcuts import redirect
import boto3

LOCAL_FILENAME = "./runtime/MAINTENANCE_MODE"
S3_FILENAME = "runtime/MAINTENANCE_MODE"


def is_maintenance_on():
"""
Get current status of maintenance mode.
"""

return file_exists(S3_FILENAME)


def change_maintenance(enabled):
"""
Update status of maintenance mode.
"""

s3_client = boto3.client(
"s3",
aws_access_key_id=settings.AWS_PRIVATE_ACCESS_KEY_ID,
aws_secret_access_key=settings.AWS_PRIVATE_SECRET_ACCESS_KEY,
endpoint_url=settings.AWS_S3_ENDPOINT_URL,
)

# turn on.
if enabled:
s3_client.put_object(
Bucket=settings.AWS_PRIVATE_STORAGE_BUCKET_NAME,
Body=LOCAL_FILENAME,
Key=S3_FILENAME,
)

# turn off.
else:
if file_exists(S3_FILENAME):
s3_client.delete_object(
Bucket=settings.AWS_PRIVATE_STORAGE_BUCKET_NAME, Key=S3_FILENAME
)


class MaintenanceCheck:
"""
Middleware that prevents clients from accessing the
FAC application so long as "MAINTENANCE_MODE" (a file in
the S3 bucket) exists.
"""

def __init__(self, get_response):
"""Initializes the middleware."""

self.get_response = get_response

def __call__(self, request):
"""
Check that maintenance mode is disabled before running request.
"""

# redirect to maintenance page.
if is_maintenance_on():
if request.path != "/maintenance":
return redirect("/maintenance")

else:
# redirect to home page if on maintenance.
if request.path == "/maintenance":
return redirect("/")
jperson1 marked this conversation as resolved.
Show resolved Hide resolved

response = self.get_response(request)
return response
Empty file.
3 changes: 2 additions & 1 deletion backend/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"config.middleware.MaintenanceCheck",
]

ROOT_URLCONF = "config.urls"
Expand Down Expand Up @@ -223,9 +224,9 @@

STATIC_URL = "/static/"


# Environment specific configurations
DEBUG = False

if ENVIRONMENT not in ["DEVELOPMENT", "PREVIEW", "STAGING", "PRODUCTION"]:
DATABASES = {
"default": env.dj_db_url(
Expand Down
1 change: 1 addition & 0 deletions backend/config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
# home page & robots.txt
path("", auditviews.Home.as_view(), name="Home"),
path("robots.txt", auditviews.no_robots, name="no_robots"),
path("maintenance", auditviews.Maintenance.as_view(), name="Maintenance"),
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

if settings.ENABLE_DEBUG_TOOLBAR:
Expand Down
2 changes: 1 addition & 1 deletion backend/dissemination/summary_reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,7 @@ def generate_summary_report(report_ids, include_private=False):
(filename, workbook_bytes, tpw) = prepare_workbook_for_download(workbook)
t1 = time.time()
logger.info(
f"SUMMARY_REPORTS generate_summary_report\n\ttotal: {t1-t0} ttri: {ttri} tgrdd: {tgrdd} tcw: {tcw} tpw: {tpw}"
f"SUMMARY_REPORTS generate_summary_report\n\ttotal: {t1 - t0} ttri: {ttri} tgrdd: {tgrdd} tcw: {tcw} tpw: {tpw}"
)
return (filename, workbook_bytes)

Expand Down
2 changes: 1 addition & 1 deletion backend/schemas/scripts/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def process_spec(WBNT):
password = generate_password()
for ndx, sheet in enumerate(WBNT.sheets):
print("########################")
print(f"## Processing sheet {ndx+1}")
print(f"## Processing sheet {ndx + 1}")
print("########################")
ws = create_protected_sheet(wb, sheet, password, ndx)
if sheet.hide_col_from is not None:
Expand Down
2 changes: 1 addition & 1 deletion backend/support/management/commands/fac_s3.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def handle(self, *args, **options):
for file in files:
full_path = os.path.join(subdir, file)
s3_client.upload_file(full_path, bucket_name, object_name + file)
print(f"Copied {full_path} to {bucket_name} {object_name+file}.")
print(f"Copied {full_path} to {bucket_name} {object_name + file}.")
return

if options["download"]:
Expand Down
12 changes: 12 additions & 0 deletions backend/templates/503.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{% extends "base.html" %}
{% block content %}
<div class="grid-container grid-row margin-y-15">
<div class="tablet:grid-col-10 tablet:grid-offset-1">
<h1 class="font-sans-2xl">503 Error: Service Unavailable</h1>
<div class="font-sans-lg">
<p>The server is temporarily busy. Please try again later.</p>
</div>
{% if exception %}<p class="text-base">Error code: {{ exception }}</p>{% endif %}
</div>
</div>
{% endblock content %}
Loading