0001-start-the-maintenance-application-64868.patch
hobo/maintenance/forms.py | ||
---|---|---|
1 |
# hobo - portal to configure and deploy applications |
|
2 |
# Copyright (C) 2015-2022 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django import forms |
|
18 |
from django.utils.translation import ugettext_lazy as _ |
|
19 | ||
20 | ||
21 |
class MaintenanceForm(forms.Form): |
|
22 |
maintenance_page = forms.BooleanField(required=False, label=_('Enable maintenance page')) |
|
23 |
maintenance_pass_trough_header = forms.CharField( |
|
24 |
required=False, label=_('Maintenance HTTP header pass through') |
|
25 |
) |
|
26 |
disable_cron = forms.BooleanField(required=False, label=_('Disable cron jobs')) |
hobo/maintenance/templates/hobo/maintenance/home.html | ||
---|---|---|
1 |
{% extends "hobo/base.html" %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block breadcrumb %} |
|
5 |
{{ block.super }} |
|
6 |
<a href="{% url 'maintenance-home' %}">{% trans "Maintenance" %}</a> |
|
7 |
{% endblock %} |
|
8 | ||
9 |
{% block appbar %} |
|
10 |
<h2>{% trans 'Maintenance' %}</h2> |
|
11 |
{% endblock %} |
|
12 | ||
13 |
{% block content %} |
|
14 |
{% if pass_through_ips %} |
|
15 |
<div class="infonotice"> |
|
16 |
{% blocktrans %}IP addresses configured to pass through the maintenance page:{% endblocktrans %} {{ pass_through_ips }} |
|
17 |
</div> |
|
18 |
{% else %} |
|
19 |
<div class="warningnotice"> |
|
20 |
{% trans "No IP addresses configured to pass through the maintenance page." %} |
|
21 |
</div> |
|
22 |
{% endif %} |
|
23 | ||
24 |
<form action="." method="post"> |
|
25 |
{% csrf_token %} |
|
26 |
{{ form.as_p }} |
|
27 |
<div class="buttons"> |
|
28 |
<button>{% trans "Submit" %}</button> |
|
29 |
</div> |
|
30 |
</form> |
|
31 |
{% endblock %} |
hobo/maintenance/urls.py | ||
---|---|---|
1 |
# hobo - portal to configure and deploy applications |
|
2 |
# Copyright (C) 2015-2022 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django.conf.urls import url |
|
18 | ||
19 |
from . import views |
|
20 | ||
21 |
urlpatterns = [ |
|
22 |
url(r'^$', views.home, name='maintenance-home'), |
|
23 |
] |
hobo/maintenance/views.py | ||
---|---|---|
1 |
# hobo - portal to configure and deploy applications |
|
2 |
# Copyright (C) 2015-2022 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django.conf import settings |
|
18 |
from django.urls import reverse_lazy |
|
19 |
from django.utils.functional import cached_property |
|
20 |
from django.views.generic import FormView |
|
21 | ||
22 |
from hobo.environment.utils import get_setting_variable |
|
23 | ||
24 |
from .forms import MaintenanceForm |
|
25 | ||
26 | ||
27 |
class HomeView(FormView): |
|
28 |
template_name = 'hobo/maintenance/home.html' |
|
29 |
form_class = MaintenanceForm |
|
30 |
success_url = reverse_lazy('maintenance-home') |
|
31 | ||
32 |
@cached_property |
|
33 |
def maintenance_page_variable(self): |
|
34 |
return get_setting_variable('MAINTENANCE_PAGE') |
|
35 | ||
36 |
@cached_property |
|
37 |
def maintenance_pass_trough_header_variable(self): |
|
38 |
return get_setting_variable('MAINTENANCE_PASS_THROUGH_HEADER') |
|
39 | ||
40 |
@cached_property |
|
41 |
def tenant_disable_cron_jobs_variable(self): |
|
42 |
return get_setting_variable('TENANT_DISABLE_CRON_JOBS') |
|
43 | ||
44 |
def get_initial(self): |
|
45 |
initial = super().get_initial() |
|
46 |
initial['maintenance_page'] = bool(self.maintenance_page_variable.json) |
|
47 |
initial['maintenance_pass_trough_header'] = self.maintenance_pass_trough_header_variable.value |
|
48 |
initial['disable_cron'] = bool(self.tenant_disable_cron_jobs_variable.json) |
|
49 |
return initial |
|
50 | ||
51 |
def form_valid(self, form): |
|
52 |
self.maintenance_page_variable.json = form.cleaned_data['maintenance_page'] |
|
53 |
self.maintenance_page_variable.save() |
|
54 |
self.maintenance_pass_trough_header_variable.value = form.cleaned_data[ |
|
55 |
'maintenance_pass_trough_header' |
|
56 |
] |
|
57 |
self.maintenance_pass_trough_header_variable.save() |
|
58 |
self.tenant_disable_cron_jobs_variable.json = form.cleaned_data['disable_cron'] |
|
59 |
self.tenant_disable_cron_jobs_variable.save() |
|
60 |
return super().form_valid(form) |
|
61 | ||
62 |
def get_context_data(self, **kwargs): |
|
63 |
ctx = super().get_context_data(**kwargs) |
|
64 |
pass_through_ips_setting = getattr(settings, 'MAINTENANCE_PASS_THROUGH_IPS', []) |
|
65 |
ctx['pass_through_ips'] = ', '.join(pass_through_ips_setting) |
|
66 |
return ctx |
|
67 | ||
68 | ||
69 |
home = HomeView.as_view() |
hobo/settings.py | ||
---|---|---|
46 | 46 |
'hobo.debug', |
47 | 47 |
'hobo.environment', |
48 | 48 |
'hobo.franceconnect', |
49 |
'hobo.maintenance', |
|
49 | 50 |
'hobo.matomo', |
50 | 51 |
'hobo.profile', |
51 | 52 |
'hobo.seo', |
hobo/templates/hobo/home.html | ||
---|---|---|
117 | 117 |
<a class="button button-paragraph" href="{% url 'environment-home' %}">{% trans 'Services' %}</a> |
118 | 118 |
<a class="button button-paragraph" href="{% url 'environment-variables' %}">{% trans 'Variables' %}</a> |
119 | 119 |
<a class="button button-paragraph" href="{% url 'debug-home' %}">{% trans 'Debugging' %}</a> |
120 |
<a class="button button-paragraph" href="{% url 'maintenance-home' %}">{% trans 'Maintenance' %}</a> |
|
120 | 121 | |
121 | 122 |
</aside> |
122 | 123 |
{% endblock %} |
hobo/urls.py | ||
---|---|---|
23 | 23 |
from .emails.urls import urlpatterns as emails_urls |
24 | 24 |
from .environment.urls import urlpatterns as environment_urls |
25 | 25 |
from .franceconnect.urls import urlpatterns as franceconnect_urls |
26 |
from .maintenance.urls import urlpatterns as maintenance_urls |
|
26 | 27 |
from .matomo.urls import urlpatterns as matomo_urls |
27 | 28 |
from .profile.urls import urlpatterns as profile_urls |
28 | 29 |
from .seo.urls import urlpatterns as seo_urls |
... | ... | |
45 | 46 |
url(r'^sms/', decorated_includes(admin_required, include(sms_urls))), |
46 | 47 |
url(r'^debug/', decorated_includes(admin_required, include(debug_urls))), |
47 | 48 |
url(r'^applications/', decorated_includes(admin_required, include(applications_urls))), |
49 |
url(r'^maintenance/', decorated_includes(admin_required, include(maintenance_urls))), |
|
48 | 50 |
url(r'^api/health/$', health_json, name='health-json'), |
49 | 51 |
url(r'^menu.json$', menu_json, name='menu_json'), |
50 | 52 |
url(r'^hobos.json$', hobo), |
tests/test_maintenance.py | ||
---|---|---|
1 | 1 |
import mock |
2 | 2 |
from test_manager import login |
3 | 3 | |
4 |
from hobo.environment.models import Variable |
|
5 | ||
4 | 6 | |
5 | 7 |
def test_maintenance_middleware(app, admin_user, db, monkeypatch, settings): |
6 | 8 |
app = login(app) |
... | ... | |
21 | 23 |
settings.MAINTENANCE_PASS_THROUGH_HEADER = 'X-Entrouvert' |
22 | 24 |
resp = app.get('/', headers={'X-Entrouvert': 'yes'}) |
23 | 25 |
assert resp.status_code == 200 |
26 | ||
27 | ||
28 |
def test_manage(app, admin_user, settings): |
|
29 |
assert Variable.objects.filter(name='SETTING_MAINTENANCE_PAGE').count() == 0 |
|
30 |
assert Variable.objects.filter(name='SETTING_MAINTENANCE_PASS_THROUGH_HEADER').count() == 0 |
|
31 |
assert Variable.objects.filter(name='TENANT_DISABLE_CRON_JOBS').count() == 0 |
|
32 |
assert not getattr(settings, 'MAINTENANCE_PASS_THROUGH_IPS', []) |
|
33 | ||
34 |
login(app) |
|
35 |
resp = app.get('/maintenance/') |
|
36 |
assert 'No IP addresses configured' in resp.text |
|
37 |
resp.form.set('maintenance_page', True) |
|
38 |
resp.form.set('maintenance_pass_trough_header', 'X-Entrouvert') |
|
39 |
resp.form.set('disable_cron', True) |
|
40 |
resp = resp.form.submit().follow() |
|
41 |
assert Variable.objects.filter(name='SETTING_MAINTENANCE_PAGE').get().value == 'true' |
|
42 |
assert ( |
|
43 |
Variable.objects.filter(name='SETTING_MAINTENANCE_PASS_THROUGH_HEADER').get().value == 'X-Entrouvert' |
|
44 |
) |
|
45 |
assert Variable.objects.filter(name='SETTING_TENANT_DISABLE_CRON_JOBS').get().value == 'true' |
|
46 | ||
47 |
resp.form.set('maintenance_page', False) |
|
48 |
resp.form.set('maintenance_pass_trough_header', '') |
|
49 |
resp.form.set('disable_cron', False) |
|
50 |
resp = resp.form.submit().follow() |
|
51 |
assert Variable.objects.filter(name='SETTING_MAINTENANCE_PAGE').get().value == 'false' |
|
52 |
assert Variable.objects.filter(name='SETTING_MAINTENANCE_PASS_THROUGH_HEADER').get().value == '' |
|
53 |
assert Variable.objects.filter(name='SETTING_TENANT_DISABLE_CRON_JOBS').get().value == 'false' |
|
54 | ||
55 |
settings.MAINTENANCE_PASS_THROUGH_IPS = ['127.0.0.1', '127.0.0.2'] |
|
56 |
resp = app.get('/maintenance/') |
|
57 |
assert 'IP addresses configured to pass through the maintenance page: 127.0.0.1, 127.0.0.2' in resp.text |
|
24 |
- |