start the maintenance application (#64868)
This commit is contained in:
parent
1700420a1e
commit
7c4d274df9
|
@ -0,0 +1,29 @@
|
|||
# hobo - portal to configure and deploy applications
|
||||
# Copyright (C) 2015-2022 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class MaintenanceForm(forms.Form):
|
||||
maintenance_page = forms.BooleanField(required=False, label=_('Enable maintenance page'))
|
||||
maintenance_page_message = forms.CharField(
|
||||
required=False, widget=forms.Textarea, label=_('Maintenance page message')
|
||||
)
|
||||
maintenance_pass_trough_header = forms.CharField(
|
||||
required=False, label=_('Maintenance HTTP header pass through')
|
||||
)
|
||||
disable_cron = forms.BooleanField(required=False, label=_('Disable cron jobs'))
|
|
@ -0,0 +1,17 @@
|
|||
from django.core.management.base import BaseCommand, CommandError
|
||||
|
||||
from hobo.environment.models import Variable
|
||||
from hobo.environment.utils import get_setting_variable
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Toggle maintenance page'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
maintenance_page_variable = get_setting_variable('MAINTENANCE_PAGE')
|
||||
if not bool(maintenance_page_variable.json):
|
||||
self.stdout.write(self.style.SUCCESS('The maintenance page is already disabled.'))
|
||||
return
|
||||
maintenance_page_variable.json = False
|
||||
maintenance_page_variable.save()
|
||||
self.stdout.write(self.style.SUCCESS('Maintenance page disabled.'))
|
|
@ -0,0 +1,21 @@
|
|||
{% extends "hobo/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block breadcrumb %}
|
||||
{{ block.super }}
|
||||
<a href="{% url 'maintenance-home' %}">{% trans "Maintenance" %}</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block appbar %}
|
||||
<h2>{% trans 'Maintenance' %}</h2>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form action="." method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<div class="buttons">
|
||||
<button>{% trans "Submit" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -0,0 +1,8 @@
|
|||
{% load i18n %}
|
||||
|
||||
<html>
|
||||
<body>
|
||||
<h1>{% trans "This site is currently unavailable." %}</h1>
|
||||
<p>{{ maintenance_message|default:"" }}</p>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,23 @@
|
|||
# hobo - portal to configure and deploy applications
|
||||
# Copyright (C) 2015-2022 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.home, name='maintenance-home'),
|
||||
]
|
|
@ -0,0 +1,77 @@
|
|||
# hobo - portal to configure and deploy applications
|
||||
# Copyright (C) 2015-2022 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.conf import settings
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.functional import cached_property
|
||||
from django.views.generic import FormView
|
||||
|
||||
from hobo.environment.utils import get_setting_variable
|
||||
|
||||
from .forms import MaintenanceForm
|
||||
|
||||
|
||||
class HomeView(FormView):
|
||||
template_name = 'hobo/maintenance/home.html'
|
||||
form_class = MaintenanceForm
|
||||
success_url = reverse_lazy('maintenance-home')
|
||||
|
||||
@cached_property
|
||||
def maintenance_page_variable(self):
|
||||
return get_setting_variable('MAINTENANCE_PAGE')
|
||||
|
||||
@cached_property
|
||||
def maintenance_page_message_variable(self):
|
||||
return get_setting_variable('MAINTENANCE_PAGE_MESSAGE')
|
||||
|
||||
@cached_property
|
||||
def maintenance_pass_trough_header_variable(self):
|
||||
return get_setting_variable('MAINTENANCE_PASS_THROUGH_HEADER')
|
||||
|
||||
@cached_property
|
||||
def tenant_disable_cron_jobs_variable(self):
|
||||
return get_setting_variable('TENANT_DISABLE_CRON_JOBS')
|
||||
|
||||
def get_initial(self):
|
||||
initial = super().get_initial()
|
||||
initial['maintenance_page'] = bool(self.maintenance_page_variable.json)
|
||||
initial['maintenance_page_message'] = self.maintenance_page_message_variable.value
|
||||
initial['maintenance_pass_trough_header'] = self.maintenance_pass_trough_header_variable.value
|
||||
initial['disable_cron'] = bool(self.tenant_disable_cron_jobs_variable.json)
|
||||
return initial
|
||||
|
||||
def form_valid(self, form):
|
||||
self.maintenance_page_variable.json = form.cleaned_data['maintenance_page']
|
||||
self.maintenance_page_variable.save()
|
||||
self.maintenance_page_message_variable.value = form.cleaned_data['maintenance_page_message']
|
||||
self.maintenance_page_message_variable.save()
|
||||
|
||||
self.maintenance_pass_trough_header_variable.value = form.cleaned_data[
|
||||
'maintenance_pass_trough_header'
|
||||
]
|
||||
self.maintenance_pass_trough_header_variable.save()
|
||||
self.tenant_disable_cron_jobs_variable.json = form.cleaned_data['disable_cron']
|
||||
self.tenant_disable_cron_jobs_variable.save()
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
pass_through_ips_setting = getattr(settings, 'MAINTENANCE_PASS_THROUGH_IPS', [])
|
||||
ctx['pass_through_ips'] = ', '.join(pass_through_ips_setting)
|
||||
return ctx
|
||||
|
||||
|
||||
home = HomeView.as_view()
|
|
@ -15,7 +15,7 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.conf import settings
|
||||
from django.http import HttpResponse
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
|
||||
|
@ -38,6 +38,9 @@ class MaintenanceMiddleware:
|
|||
def __call__(self, request):
|
||||
maintenance_mode = getattr(settings, 'MAINTENANCE_PAGE', None)
|
||||
if maintenance_mode and not pass_through(request):
|
||||
maintenance_msg = _('The site is under maintenance')
|
||||
return HttpResponse('<h1>%s</h1>' % maintenance_msg, status=503)
|
||||
maintenance_message = getattr(settings, 'MAINTENANCE_PAGE_MESSAGE', '')
|
||||
context = {'maintenance_message': maintenance_message}
|
||||
return TemplateResponse(
|
||||
request, 'hobo/maintenance/maintenance_page.html', context=context, status=503
|
||||
).render()
|
||||
return self.get_response(request)
|
||||
|
|
|
@ -46,6 +46,7 @@ INSTALLED_APPS = (
|
|||
'hobo.debug',
|
||||
'hobo.environment',
|
||||
'hobo.franceconnect',
|
||||
'hobo.maintenance',
|
||||
'hobo.matomo',
|
||||
'hobo.profile',
|
||||
'hobo.seo',
|
||||
|
|
|
@ -117,6 +117,6 @@ $(function() {
|
|||
<a class="button button-paragraph" href="{% url 'environment-home' %}">{% trans 'Services' %}</a>
|
||||
<a class="button button-paragraph" href="{% url 'environment-variables' %}">{% trans 'Variables' %}</a>
|
||||
<a class="button button-paragraph" href="{% url 'debug-home' %}">{% trans 'Debugging' %}</a>
|
||||
|
||||
{% if show_maintenance_menu %}<a class="button button-paragraph" href="{% url 'maintenance-home' %}">{% trans 'Maintenance' %}</a>{% endif %}
|
||||
</aside>
|
||||
{% endblock %}
|
||||
|
|
|
@ -23,6 +23,7 @@ from .debug.urls import urlpatterns as debug_urls
|
|||
from .emails.urls import urlpatterns as emails_urls
|
||||
from .environment.urls import urlpatterns as environment_urls
|
||||
from .franceconnect.urls import urlpatterns as franceconnect_urls
|
||||
from .maintenance.urls import urlpatterns as maintenance_urls
|
||||
from .matomo.urls import urlpatterns as matomo_urls
|
||||
from .profile.urls import urlpatterns as profile_urls
|
||||
from .seo.urls import urlpatterns as seo_urls
|
||||
|
@ -45,6 +46,7 @@ urlpatterns = [
|
|||
url(r'^sms/', decorated_includes(admin_required, include(sms_urls))),
|
||||
url(r'^debug/', decorated_includes(admin_required, include(debug_urls))),
|
||||
url(r'^applications/', decorated_includes(admin_required, include(applications_urls))),
|
||||
url(r'^maintenance/', decorated_includes(admin_required, include(maintenance_urls))),
|
||||
url(r'^api/health/$', health_json, name='health-json'),
|
||||
url(r'^menu.json$', menu_json, name='menu_json'),
|
||||
url(r'^hobos.json$', hobo),
|
||||
|
|
|
@ -39,6 +39,7 @@ class Home(TemplateView):
|
|||
context['has_authentic'] = bool(Authentic.objects.filter(secondary=False))
|
||||
context['has_global_title'] = Variable.objects.filter(name='global_title').exists()
|
||||
context['has_default_from_email'] = Variable.objects.filter(name='default_from_email').exists()
|
||||
context['show_maintenance_menu'] = bool(getattr(settings, 'MAINTENANCE_PASS_THROUGH_IPS', []))
|
||||
return context
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import mock
|
||||
from test_manager import login
|
||||
|
||||
from hobo.environment.models import Variable
|
||||
from hobo.environment.utils import get_setting_variable
|
||||
from hobo.maintenance.management.commands.disable_maintenance_page import Command
|
||||
|
||||
|
||||
def test_maintenance_middleware(app, admin_user, db, monkeypatch, settings):
|
||||
app = login(app)
|
||||
|
@ -9,7 +13,12 @@ def test_maintenance_middleware(app, admin_user, db, monkeypatch, settings):
|
|||
|
||||
settings.MAINTENANCE_PAGE = True
|
||||
resp = app.get('/', status=503)
|
||||
assert 'The site is under maintenance' in resp.text
|
||||
assert 'This site is currently unavailable.' in resp.text
|
||||
|
||||
# check custom maintenance message
|
||||
settings.MAINTENANCE_PAGE_MESSAGE = 'foobar'
|
||||
resp = app.get('/', status=503)
|
||||
assert 'foobar' in resp.text
|
||||
|
||||
settings.MAINTENANCE_PASS_THROUGH_IPS = ['127.0.0.1']
|
||||
resp = app.get('/')
|
||||
|
@ -21,3 +30,56 @@ def test_maintenance_middleware(app, admin_user, db, monkeypatch, settings):
|
|||
settings.MAINTENANCE_PASS_THROUGH_HEADER = 'X-Entrouvert'
|
||||
resp = app.get('/', headers={'X-Entrouvert': 'yes'})
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
def test_manage(app, admin_user, settings):
|
||||
assert Variable.objects.filter(name='SETTING_MAINTENANCE_PAGE').count() == 0
|
||||
assert Variable.objects.filter(name='SETTING_MAINTENANCE_MESSAGE').count() == 0
|
||||
assert Variable.objects.filter(name='SETTING_MAINTENANCE_PASS_THROUGH_HEADER').count() == 0
|
||||
assert Variable.objects.filter(name='TENANT_DISABLE_CRON_JOBS').count() == 0
|
||||
assert not getattr(settings, 'MAINTENANCE_PASS_THROUGH_IPS', [])
|
||||
|
||||
login(app)
|
||||
resp = app.get('/')
|
||||
assert 'Maintenance' not in resp.text
|
||||
settings.MAINTENANCE_PASS_THROUGH_IPS = ['127.0.0.1']
|
||||
resp = app.get('/')
|
||||
assert 'Maintenance' in resp.text
|
||||
|
||||
resp = app.get('/maintenance/')
|
||||
resp.form.set('maintenance_page', True)
|
||||
resp.form.set('maintenance_page_message', 'Foo')
|
||||
resp.form.set('maintenance_pass_trough_header', 'X-Entrouvert')
|
||||
resp.form.set('disable_cron', True)
|
||||
resp = resp.form.submit().follow()
|
||||
assert Variable.objects.filter(name='SETTING_MAINTENANCE_PAGE').get().value == 'true'
|
||||
assert Variable.objects.filter(name='SETTING_MAINTENANCE_PAGE_MESSAGE').get().value == 'Foo'
|
||||
assert (
|
||||
Variable.objects.filter(name='SETTING_MAINTENANCE_PASS_THROUGH_HEADER').get().value == 'X-Entrouvert'
|
||||
)
|
||||
assert Variable.objects.filter(name='SETTING_TENANT_DISABLE_CRON_JOBS').get().value == 'true'
|
||||
|
||||
resp.form.set('maintenance_page', False)
|
||||
resp.form.set('maintenance_page_message', '')
|
||||
resp.form.set('maintenance_pass_trough_header', '')
|
||||
resp.form.set('disable_cron', False)
|
||||
resp = resp.form.submit().follow()
|
||||
assert Variable.objects.filter(name='SETTING_MAINTENANCE_PAGE').get().value == 'false'
|
||||
assert Variable.objects.filter(name='SETTING_MAINTENANCE_PAGE_MESSAGE').get().value == ''
|
||||
assert Variable.objects.filter(name='SETTING_MAINTENANCE_PASS_THROUGH_HEADER').get().value == ''
|
||||
assert Variable.objects.filter(name='SETTING_TENANT_DISABLE_CRON_JOBS').get().value == 'false'
|
||||
|
||||
|
||||
def test_disable_maintenance_page_command(db):
|
||||
maintenance_page_variable = get_setting_variable('MAINTENANCE_PAGE')
|
||||
assert not bool(maintenance_page_variable.json)
|
||||
command = Command()
|
||||
command.handle()
|
||||
maintenance_page_variable = get_setting_variable('MAINTENANCE_PAGE')
|
||||
assert not bool(maintenance_page_variable.json)
|
||||
maintenance_page_variable.json = True
|
||||
maintenance_page_variable.save()
|
||||
assert bool(maintenance_page_variable.json)
|
||||
command.handle()
|
||||
maintenance_page_variable = get_setting_variable('MAINTENANCE_PAGE')
|
||||
assert not bool(maintenance_page_variable.json)
|
||||
|
|
Loading…
Reference in New Issue