diff --git a/plinth/modules/apache/components.py b/plinth/modules/apache/components.py index 32449e045..d993d3235 100644 --- a/plinth/modules/apache/components.py +++ b/plinth/modules/apache/components.py @@ -8,6 +8,7 @@ from django.utils.text import format_lazy from django.utils.translation import gettext_lazy from plinth import action_utils, app +from plinth.privileged import service as service_privileged from . import privileged @@ -16,7 +17,7 @@ class Webserver(app.LeaderComponent): """Component to enable/disable Apache configuration.""" def __init__(self, component_id, web_name, kind='config', urls=None, - expect_redirects=False): + expect_redirects=False, last_updated_version=None): """Initialize the web server component. component_id should be a unique ID across all components of an app and @@ -33,6 +34,9 @@ class Webserver(app.LeaderComponent): urls is a list of URLs over which a HTTP services will be available due to this component. This list is only used for running diagnostics. + last_updated_version is the app version in which the web server + configuration/site/module file was updated. Using this, web server will + be automatically reloaded or restarted as necessary during app upgrade. """ super().__init__(component_id) @@ -40,6 +44,7 @@ class Webserver(app.LeaderComponent): self.kind = kind self.urls = urls or [] self.expect_redirects = expect_redirects + self.last_updated_version = last_updated_version def is_enabled(self): """Return whether the Apache configuration is enabled.""" @@ -71,6 +76,28 @@ class Webserver(app.LeaderComponent): return results + def setup(self, old_version): + """Restart/reload web server if configuration files changed.""" + if not old_version: + # App is being freshly setup. After setup, app will be enabled + # which will result in reload/restart of web server. + return + + if old_version >= self.last_updated_version: + # Already using the latest configuration. Web server reload/restart + # is not necessary. + return + + if not self.app.is_enabled(): + # App is currently disabled, web server will reloaded/restarted + # when the app is enabled. + return + + if self.kind == 'module': + service_privileged.restart('apache2') + else: + service_privileged.reload('apache2') + class Uwsgi(app.LeaderComponent): """Component to enable/disable uWSGI configuration.""" diff --git a/plinth/modules/apache/tests/test_components.py b/plinth/modules/apache/tests/test_components.py index 0eece880d..35a120c33 100644 --- a/plinth/modules/apache/tests/test_components.py +++ b/plinth/modules/apache/tests/test_components.py @@ -8,6 +8,7 @@ from unittest.mock import call, patch import pytest +from plinth import app from plinth.modules.apache.components import (Uwsgi, Webserver, check_url, diagnose_url, diagnose_url_on_all) @@ -95,6 +96,51 @@ def test_webserver_diagnose(diagnose_url_on_all, diagnose_url): [call('{host}url1', check_certificate=False, expect_redirects=False)]) +@patch('plinth.privileged.service.restart') +@patch('plinth.privileged.service.reload') +def test_webserver_setup(service_reload, service_restart): + """Test that component restart/reloads web server during app upgrades.""" + + class AppTest(app.App): + app_id = 'testapp' + enabled = False + + def is_enabled(self): + return self.enabled + + app1 = AppTest() + webserver1 = Webserver('test-webserver1', 'test-config', + last_updated_version=5) + for version in (0, 5, 6): + webserver1.setup(old_version=version) + service_reload.assert_not_called() + service_restart.assert_not_called() + + app1.enabled = False + webserver2 = Webserver('test-webserver2', 'test-config', + last_updated_version=5) + app1.add(webserver2) + webserver2.setup(old_version=3) + service_reload.assert_not_called() + service_restart.assert_not_called() + + app1.enabled = True + webserver3 = Webserver('test-webserver3', 'test-config', + last_updated_version=5) + app1.add(webserver3) + webserver3.setup(old_version=3) + service_reload.assert_has_calls([call('apache2')]) + service_restart.assert_not_called() + service_reload.reset_mock() + + webserver4 = Webserver('test-webserver', 'test-module', 'module', + last_updated_version=5) + app1.add(webserver4) + webserver4.setup(old_version=3) + service_restart.assert_has_calls([call('apache2')]) + service_reload.assert_not_called() + + def test_uwsgi_init(): """Test that uWSGI component can be initialized.""" with pytest.raises(ValueError):