apache: Reload apache using component if config changes

We often change apache configuration for an app and when a new version of the
config is deployed the changes go un-applied until FreedomBox is restarted. Allow
specifying that config has changed in an particular version and reload of
configuration is needed.

Tests:

- Unit tests pass. The feature used in transmission works.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
Sunil Mohan Adapa 2023-04-26 15:28:40 -07:00 committed by James Valleroy
parent 3c4771ed00
commit 1406f53019
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
2 changed files with 74 additions and 1 deletions

View File

@ -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."""

View File

@ -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):