mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-21 07:55:00 +00:00
*: Use privileged decorator for service actions
Tests:
- DONE: Unit tests work
- DONE: Transmission
- DONE: Enabling/disabling an app with a daemon works: transmission
- DONE: Showing the status of whether the app is enabled with daemon
is-enabled works.
- DONE: A message is shown if app is enabled and service is not running
- DONE: Service is stopped and re-started during backup
- DONE: Adding user to share group during initial setup restarts the service
- Not tested: Enabling/disabling a service with alias works (no such apps)
- DONE: Restarting/try-restarting a service works
- DONE: Masking/unmasking works
- DONE: rsyslog is masked after initial setup
- DONE: systemd-journald is try-restarted during initial setup
- DONE: Avahi, email, security initial setup works
- DONE: Fail2ban is unmasked and enabled
- DONE: Enabling/disabling fail2ban is security app works
- DONE: Enabling/disabling password authentication in SSH works
- ?? Let's encrypt
- Services are try-restarted during certificate setup, obtain, renew
- Not tested: upgrade pagekite from version 1
Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
parent
1dcbfce713
commit
222563a482
130
actions/service
130
actions/service
@ -1,130 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""
|
||||
Wrapper to list and handle system services
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
|
||||
from plinth import action_utils
|
||||
from plinth import app as app_module
|
||||
from plinth import cfg, module_loader
|
||||
from plinth.daemon import Daemon, RelatedDaemon
|
||||
|
||||
cfg.read()
|
||||
module_config_path = os.path.join(cfg.config_dir, 'modules-enabled')
|
||||
|
||||
|
||||
def add_service_action(subparsers, action, help):
|
||||
parser = subparsers.add_parser(action, help=help)
|
||||
parser.add_argument('service', help='name of the service')
|
||||
|
||||
|
||||
def parse_arguments():
|
||||
"""Return parsed command line arguments as dictionary."""
|
||||
parser = argparse.ArgumentParser()
|
||||
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
|
||||
|
||||
add_service_action(subparsers, 'start', 'start a service')
|
||||
add_service_action(subparsers, 'stop', 'stop a service')
|
||||
add_service_action(subparsers, 'enable', 'enable a service')
|
||||
add_service_action(subparsers, 'disable', 'disable a service')
|
||||
add_service_action(subparsers, 'restart', 'restart a service')
|
||||
add_service_action(subparsers, 'try-restart',
|
||||
'restart a service if running')
|
||||
add_service_action(subparsers, 'reload', 'reload a service')
|
||||
add_service_action(subparsers, 'is-running', 'status of a service')
|
||||
add_service_action(subparsers, 'is-enabled', 'status a service')
|
||||
add_service_action(subparsers, 'mask', 'unmask a service')
|
||||
add_service_action(subparsers, 'unmask', 'unmask a service')
|
||||
|
||||
subparsers.required = True
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def subcommand_start(arguments):
|
||||
action_utils.service_start(arguments.service)
|
||||
|
||||
|
||||
def subcommand_stop(arguments):
|
||||
action_utils.service_stop(arguments.service)
|
||||
|
||||
|
||||
def subcommand_enable(arguments):
|
||||
action_utils.service_enable(arguments.service)
|
||||
|
||||
|
||||
def subcommand_disable(arguments):
|
||||
action_utils.service_disable(arguments.service)
|
||||
|
||||
|
||||
def subcommand_restart(arguments):
|
||||
action_utils.service_restart(arguments.service)
|
||||
|
||||
|
||||
def subcommand_try_restart(arguments):
|
||||
action_utils.service_try_restart(arguments.service)
|
||||
|
||||
|
||||
def subcommand_reload(arguments):
|
||||
action_utils.service_reload(arguments.service)
|
||||
|
||||
|
||||
def subcommand_mask(arguments):
|
||||
action_utils.service_mask(arguments.service)
|
||||
|
||||
|
||||
def subcommand_unmask(arguments):
|
||||
action_utils.service_unmask(arguments.service)
|
||||
|
||||
|
||||
def subcommand_is_enabled(arguments):
|
||||
print(action_utils.service_is_enabled(arguments.service))
|
||||
|
||||
|
||||
def subcommand_is_running(arguments):
|
||||
print(action_utils.service_is_running(arguments.service))
|
||||
|
||||
|
||||
def _get_managed_services():
|
||||
"""Get a set of all services managed by FreedomBox."""
|
||||
services = set()
|
||||
module_loader.load_modules()
|
||||
app_module.apps_init()
|
||||
for app in app_module.App.list():
|
||||
components = app.get_components_of_type(Daemon)
|
||||
for component in components:
|
||||
services.add(component.unit)
|
||||
if component.alias:
|
||||
services.add(component.alias)
|
||||
|
||||
components = app.get_components_of_type(RelatedDaemon)
|
||||
for component in components:
|
||||
services.add(component.unit)
|
||||
|
||||
return services
|
||||
|
||||
|
||||
def _assert_service_is_managed_by_plinth(service_name):
|
||||
managed_services = _get_managed_services()
|
||||
if service_name not in managed_services:
|
||||
msg = ("The service '%s' is not managed by FreedomBox. Access is only "
|
||||
"permitted for services listed in the 'managed_services' "
|
||||
"variable of any FreedomBox app.") % service_name
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
def main():
|
||||
"""Parse arguments and perform all duties."""
|
||||
arguments = parse_arguments()
|
||||
|
||||
subcommand = arguments.subcommand.replace('-', '_')
|
||||
subcommand_method = globals()['subcommand_' + subcommand]
|
||||
if hasattr(arguments, 'service'):
|
||||
_assert_service_is_managed_by_plinth(arguments.service)
|
||||
subcommand_method(arguments)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@ -1,7 +1,5 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""
|
||||
Component for managing a background daemon or any systemd unit.
|
||||
"""
|
||||
"""Component for managing a background daemon or any systemd unit."""
|
||||
|
||||
import socket
|
||||
import subprocess
|
||||
@ -11,7 +9,7 @@ from django.utils.text import format_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from django.utils.translation import gettext_lazy
|
||||
|
||||
from plinth import action_utils, actions, app
|
||||
from plinth import action_utils, app
|
||||
|
||||
|
||||
class Daemon(app.LeaderComponent):
|
||||
@ -70,15 +68,17 @@ class Daemon(app.LeaderComponent):
|
||||
|
||||
def enable(self):
|
||||
"""Run operations to enable the daemon/unit."""
|
||||
actions.superuser_run('service', ['enable', self.unit])
|
||||
from plinth.privileged import service as service_privileged
|
||||
service_privileged.enable(self.unit)
|
||||
if self.alias:
|
||||
actions.superuser_run('service', ['enable', self.alias])
|
||||
service_privileged.enable(self.alias)
|
||||
|
||||
def disable(self):
|
||||
"""Run operations to disable the daemon/unit."""
|
||||
actions.superuser_run('service', ['disable', self.unit])
|
||||
from plinth.privileged import service as service_privileged
|
||||
service_privileged.disable(self.unit)
|
||||
if self.alias:
|
||||
actions.superuser_run('service', ['disable', self.alias])
|
||||
service_privileged.disable(self.alias)
|
||||
|
||||
def is_running(self):
|
||||
"""Return whether the daemon/unit is running."""
|
||||
|
||||
@ -1,11 +1,8 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""
|
||||
FreedomBox app for service discovery.
|
||||
"""
|
||||
"""FreedomBox app for service discovery."""
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from plinth import actions
|
||||
from plinth import app as app_module
|
||||
from plinth import cfg, menu
|
||||
from plinth.daemon import Daemon
|
||||
@ -14,6 +11,7 @@ from plinth.modules.config import get_hostname
|
||||
from plinth.modules.firewall.components import Firewall
|
||||
from plinth.modules.names.components import DomainType
|
||||
from plinth.package import Packages
|
||||
from plinth.privileged import service as service_privileged
|
||||
from plinth.signals import domain_added, domain_removed, post_hostname_change
|
||||
from plinth.utils import format_lazy
|
||||
|
||||
@ -90,7 +88,7 @@ class AvahiApp(app_module.App):
|
||||
# Reload avahi-daemon now that first-run does not reboot. After
|
||||
# performing FreedomBox Service (Plinth) package installation, new
|
||||
# Avahi files will be available and require restart.
|
||||
actions.superuser_run('service', ['reload', 'avahi-daemon'])
|
||||
service_privileged.reload('avahi-daemon')
|
||||
self.enable()
|
||||
|
||||
|
||||
|
||||
@ -12,10 +12,11 @@ TODO:
|
||||
|
||||
import logging
|
||||
|
||||
from plinth import action_utils, actions
|
||||
from plinth import action_utils
|
||||
from plinth import app as app_module
|
||||
from plinth import setup
|
||||
from plinth.modules.apache import privileged as apache_privileged
|
||||
from plinth.privileged import service as service_privileged
|
||||
|
||||
from .components import BackupRestore
|
||||
|
||||
@ -318,12 +319,12 @@ class SystemServiceHandler(ServiceHandler):
|
||||
"""Stop the service."""
|
||||
self.was_running = action_utils.service_is_running(self.service)
|
||||
if self.was_running:
|
||||
actions.superuser_run('service', ['stop', self.service])
|
||||
service_privileged.stop(self.service)
|
||||
|
||||
def restart(self):
|
||||
"""Restart the service if it was earlier running."""
|
||||
if self.was_running:
|
||||
actions.superuser_run('service', ['start', self.service])
|
||||
service_privileged.start(self.service)
|
||||
|
||||
|
||||
class ApacheServiceHandler(ServiceHandler):
|
||||
|
||||
@ -13,6 +13,7 @@ from plinth.modules.apache import (get_users_with_website, user_of_uws_url,
|
||||
uws_url_of_user)
|
||||
from plinth.modules.names.components import DomainType
|
||||
from plinth.package import Packages
|
||||
from plinth.privileged import service as service_privileged
|
||||
from plinth.signals import domain_added
|
||||
|
||||
from . import privileged
|
||||
@ -82,14 +83,14 @@ class ConfigApp(app_module.App):
|
||||
|
||||
# systemd-journald is socket activated, it may not be running and it
|
||||
# does not support reload.
|
||||
actions.superuser_run('service', ['try-restart', 'systemd-journald'])
|
||||
service_privileged.try_restart('systemd-journald')
|
||||
# rsyslog when enabled, is activated by syslog.socket (shipped by
|
||||
# systemd). See:
|
||||
# https://www.freedesktop.org/wiki/Software/systemd/syslog/ .
|
||||
actions.superuser_run('service', ['disable', 'rsyslog'])
|
||||
service_privileged.disable('rsyslog')
|
||||
# Ensure that rsyslog is not started by something else as it is
|
||||
# installed by default on Debian systems.
|
||||
actions.superuser_run('service', ['mask', 'rsyslog'])
|
||||
service_privileged.mask('rsyslog')
|
||||
|
||||
|
||||
def get_domainname():
|
||||
|
||||
@ -7,7 +7,7 @@ from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
import plinth.app
|
||||
from plinth import actions, cfg, frontpage, menu
|
||||
from plinth import cfg, frontpage, menu
|
||||
from plinth.daemon import Daemon
|
||||
from plinth.modules.apache.components import Webserver
|
||||
from plinth.modules.backups.components import BackupRestore
|
||||
@ -15,6 +15,7 @@ from plinth.modules.config import get_domainname
|
||||
from plinth.modules.firewall.components import Firewall
|
||||
from plinth.modules.letsencrypt.components import LetsEncrypt
|
||||
from plinth.package import Packages, uninstall
|
||||
from plinth.privileged import service as service_privileged
|
||||
from plinth.signals import domain_added, domain_removed
|
||||
from plinth.utils import format_lazy
|
||||
|
||||
@ -189,9 +190,9 @@ class EmailApp(plinth.app.App):
|
||||
privileged.setup_spam()
|
||||
|
||||
# Restart daemons
|
||||
actions.superuser_run('service', ['try-restart', 'postfix'])
|
||||
actions.superuser_run('service', ['try-restart', 'dovecot'])
|
||||
actions.superuser_run('service', ['try-restart', 'rspamd'])
|
||||
service_privileged.try_restart('postfix')
|
||||
service_privileged.try_restart('dovecot')
|
||||
service_privileged.try_restart('rspamd')
|
||||
|
||||
# Expose to public internet
|
||||
if old_version == 0:
|
||||
|
||||
@ -5,8 +5,9 @@ import logging
|
||||
import pathlib
|
||||
import threading
|
||||
|
||||
from plinth import actions, app
|
||||
from plinth import app
|
||||
from plinth.modules.names.components import DomainName
|
||||
from plinth.privileged import service as service_privileged
|
||||
|
||||
from . import privileged
|
||||
|
||||
@ -168,7 +169,7 @@ class LetsEncrypt(app.FollowerComponent):
|
||||
self._copy_self_signed_certificates([domain])
|
||||
|
||||
for daemon in self.daemons:
|
||||
actions.superuser_run('service', ['try-restart', daemon])
|
||||
service_privileged.try_restart(daemon)
|
||||
|
||||
def get_status(self):
|
||||
"""Return the status of certificates for all interested domains.
|
||||
@ -213,7 +214,7 @@ class LetsEncrypt(app.FollowerComponent):
|
||||
self._copy_letsencrypt_certificates(interested_domains, lineage)
|
||||
|
||||
for daemon in self.daemons:
|
||||
actions.superuser_run('service', ['try-restart', daemon])
|
||||
service_privileged.try_restart(daemon)
|
||||
|
||||
def on_certificate_renewed(self, domains, lineage):
|
||||
"""Handle event when a certificate is renewed.
|
||||
@ -247,7 +248,7 @@ class LetsEncrypt(app.FollowerComponent):
|
||||
self._copy_self_signed_certificates(interested_domains)
|
||||
|
||||
for daemon in self.daemons:
|
||||
actions.superuser_run('service', ['try-restart', daemon])
|
||||
service_privileged.try_restart(daemon)
|
||||
|
||||
def on_certificate_deleted(self, domains, lineage):
|
||||
"""Handle event when a certificate is deleted.
|
||||
|
||||
@ -9,6 +9,7 @@ from plinth.daemon import Daemon
|
||||
from plinth.modules.backups.components import BackupRestore
|
||||
from plinth.modules.names.components import DomainType
|
||||
from plinth.package import Packages
|
||||
from plinth.privileged import service as service_privileged
|
||||
from plinth.utils import format_lazy
|
||||
|
||||
from . import manifest, utils
|
||||
@ -103,5 +104,4 @@ class PagekiteApp(app_module.App):
|
||||
self.enable()
|
||||
|
||||
if old_version == 1:
|
||||
actions.superuser_run('service',
|
||||
['try-restart', PagekiteApp.DAEMON])
|
||||
service_privileged.try_restart(PagekiteApp.DAEMON)
|
||||
|
||||
@ -7,12 +7,12 @@ from collections import defaultdict
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from plinth import actions
|
||||
from plinth import app as app_module
|
||||
from plinth import menu
|
||||
from plinth.daemon import Daemon, RelatedDaemon
|
||||
from plinth.modules.backups.components import BackupRestore
|
||||
from plinth.package import Packages
|
||||
from plinth.privileged import service as service_privileged
|
||||
|
||||
from . import manifest, privileged
|
||||
|
||||
@ -55,7 +55,7 @@ class SecurityApp(app_module.App):
|
||||
if not old_version:
|
||||
enable_fail2ban()
|
||||
|
||||
actions.superuser_run('service', ['reload', 'fail2ban'])
|
||||
service_privileged.reload('fail2ban')
|
||||
|
||||
# Migrate to new config file.
|
||||
enabled = privileged.get_restricted_access_enabled()
|
||||
@ -66,8 +66,8 @@ class SecurityApp(app_module.App):
|
||||
|
||||
def enable_fail2ban():
|
||||
"""Unmask, enable and run the fail2ban service."""
|
||||
actions.superuser_run('service', ['unmask', 'fail2ban'])
|
||||
actions.superuser_run('service', ['enable', 'fail2ban'])
|
||||
service_privileged.unmask('fail2ban')
|
||||
service_privileged.enable('fail2ban')
|
||||
|
||||
|
||||
def set_restricted_access(enabled):
|
||||
|
||||
@ -5,9 +5,10 @@ from django.contrib import messages
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from plinth import action_utils, actions
|
||||
from plinth import action_utils
|
||||
from plinth.modules import security
|
||||
from plinth.modules.upgrades import is_backports_requested
|
||||
from plinth.privileged import service as service_privileged
|
||||
from plinth.views import AppView
|
||||
|
||||
from . import privileged
|
||||
@ -63,9 +64,9 @@ def _apply_changes(request, old_status, new_status):
|
||||
|
||||
if old_status['fail2ban_enabled'] != new_status['fail2ban_enabled']:
|
||||
if new_status['fail2ban_enabled']:
|
||||
actions.superuser_run('service', ['enable', 'fail2ban'])
|
||||
service_privileged.enable('fail2ban')
|
||||
else:
|
||||
actions.superuser_run('service', ['disable', 'fail2ban'])
|
||||
service_privileged.disable('fail2ban')
|
||||
|
||||
|
||||
def report(request):
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
from django.contrib import messages
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from plinth import actions
|
||||
from plinth.modules import ssh
|
||||
from plinth.privileged import service as service_privileged
|
||||
from plinth.views import AppView
|
||||
|
||||
from . import privileged
|
||||
@ -48,7 +48,7 @@ class SshAppView(AppView):
|
||||
if passwd_auth_changed:
|
||||
privileged.set_password_authentication(
|
||||
not new_config['password_auth_disabled'])
|
||||
actions.superuser_run('service', ['reload', 'ssh'])
|
||||
service_privileged.reload('ssh')
|
||||
messages.success(self.request, _('Configuration updated'))
|
||||
|
||||
return super().form_valid(form)
|
||||
|
||||
@ -7,11 +7,11 @@ import subprocess
|
||||
from django.utils.text import format_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from plinth import actions
|
||||
from plinth import app as app_module
|
||||
from plinth import cfg, menu
|
||||
from plinth.daemon import Daemon
|
||||
from plinth.package import Packages
|
||||
from plinth.privileged import service as service_privileged
|
||||
|
||||
from . import privileged
|
||||
from .components import UsersAndGroups
|
||||
@ -131,4 +131,4 @@ def add_user_to_share_group(username, service=None):
|
||||
if username not in group_members:
|
||||
privileged.add_user_to_group(username, 'freedombox-share')
|
||||
if service:
|
||||
actions.superuser_run('service', ['try-restart', service])
|
||||
service_privileged.try_restart(service)
|
||||
|
||||
@ -10,6 +10,7 @@ from plinth.modules.apache.components import Webserver
|
||||
from plinth.modules.backups.components import BackupRestore
|
||||
from plinth.modules.firewall.components import Firewall
|
||||
from plinth.package import Packages
|
||||
from plinth.privileged import service as service_privileged
|
||||
from plinth.utils import format_lazy
|
||||
|
||||
from . import manifest, privileged
|
||||
@ -106,7 +107,7 @@ class WordPressApp(app_module.App):
|
||||
self.enable()
|
||||
elif old_version < 3:
|
||||
# Apply changes to Apache configuration from v2 to v3.
|
||||
actions.superuser_run('service', ['reload', 'apache2'])
|
||||
service_privileged.reload('apache2')
|
||||
|
||||
|
||||
class WordPressBackupRestore(BackupRestore):
|
||||
|
||||
10
plinth/privileged/__init__.py
Normal file
10
plinth/privileged/__init__.py
Normal file
@ -0,0 +1,10 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""Package holding all the privileged actions outside of apps."""
|
||||
|
||||
from .service import (disable, enable, is_enabled, is_running, mask, reload,
|
||||
restart, start, stop, try_restart, unmask)
|
||||
|
||||
__all__ = [
|
||||
'disable', 'enable', 'is_enabled', 'is_running', 'mask', 'reload',
|
||||
'restart', 'start', 'stop', 'try_restart', 'unmask'
|
||||
]
|
||||
118
plinth/privileged/service.py
Normal file
118
plinth/privileged/service.py
Normal file
@ -0,0 +1,118 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""List and handle system services."""
|
||||
|
||||
import os
|
||||
|
||||
from plinth import action_utils
|
||||
from plinth import app as app_module
|
||||
from plinth import cfg, module_loader
|
||||
from plinth.actions import privileged
|
||||
from plinth.daemon import Daemon, RelatedDaemon
|
||||
|
||||
cfg.read()
|
||||
module_config_path = os.path.join(cfg.config_dir, 'modules-enabled')
|
||||
|
||||
|
||||
@privileged
|
||||
def start(service: str):
|
||||
"""Start a service."""
|
||||
_assert_service_is_managed_by_plinth(service)
|
||||
action_utils.service_start(service)
|
||||
|
||||
|
||||
@privileged
|
||||
def stop(service: str):
|
||||
"""Stop a running service."""
|
||||
_assert_service_is_managed_by_plinth(service)
|
||||
action_utils.service_stop(service)
|
||||
|
||||
|
||||
@privileged
|
||||
def enable(service: str):
|
||||
"""Enable a service so that it start on system boot."""
|
||||
_assert_service_is_managed_by_plinth(service)
|
||||
action_utils.service_enable(service)
|
||||
|
||||
|
||||
@privileged
|
||||
def disable(service: str):
|
||||
"""Disable a service so that it does not start on system boot."""
|
||||
_assert_service_is_managed_by_plinth(service)
|
||||
action_utils.service_disable(service)
|
||||
|
||||
|
||||
@privileged
|
||||
def restart(service: str):
|
||||
"""Restart a service."""
|
||||
_assert_service_is_managed_by_plinth(service)
|
||||
action_utils.service_restart(service)
|
||||
|
||||
|
||||
@privileged
|
||||
def try_restart(service: str):
|
||||
"""Restart a service if it is running."""
|
||||
_assert_service_is_managed_by_plinth(service)
|
||||
action_utils.service_try_restart(service)
|
||||
|
||||
|
||||
@privileged
|
||||
def reload(service: str):
|
||||
"""Reload a service."""
|
||||
_assert_service_is_managed_by_plinth(service)
|
||||
action_utils.service_reload(service)
|
||||
|
||||
|
||||
@privileged
|
||||
def mask(service: str):
|
||||
"""Mask a service."""
|
||||
_assert_service_is_managed_by_plinth(service)
|
||||
action_utils.service_mask(service)
|
||||
|
||||
|
||||
@privileged
|
||||
def unmask(service: str):
|
||||
"""Unmask a service."""
|
||||
_assert_service_is_managed_by_plinth(service)
|
||||
action_utils.service_unmask(service)
|
||||
|
||||
|
||||
@privileged
|
||||
def is_enabled(service: str) -> bool:
|
||||
"""Return whether a service is enabled."""
|
||||
_assert_service_is_managed_by_plinth(service)
|
||||
return action_utils.service_is_enabled(service)
|
||||
|
||||
|
||||
@privileged
|
||||
def is_running(service: str) -> bool:
|
||||
"""Return whether a service is running."""
|
||||
_assert_service_is_managed_by_plinth(service)
|
||||
return action_utils.service_is_running(service)
|
||||
|
||||
|
||||
def _get_managed_services():
|
||||
"""Get a set of all services managed by FreedomBox."""
|
||||
services = set()
|
||||
module_loader.load_modules()
|
||||
app_module.apps_init()
|
||||
for app in app_module.App.list():
|
||||
components = app.get_components_of_type(Daemon)
|
||||
for component in components:
|
||||
services.add(component.unit)
|
||||
if component.alias:
|
||||
services.add(component.alias)
|
||||
|
||||
components = app.get_components_of_type(RelatedDaemon)
|
||||
for component in components:
|
||||
services.add(component.unit)
|
||||
|
||||
return services
|
||||
|
||||
|
||||
def _assert_service_is_managed_by_plinth(service_name):
|
||||
managed_services = _get_managed_services()
|
||||
if service_name not in managed_services:
|
||||
msg = ("The service '%s' is not managed by FreedomBox. Access is only "
|
||||
"permitted for services listed in the 'managed_services' "
|
||||
"variable of any FreedomBox app.") % service_name
|
||||
raise ValueError(msg)
|
||||
@ -4,14 +4,23 @@ Test module for component managing system daemons and other systemd units.
|
||||
"""
|
||||
|
||||
import socket
|
||||
import subprocess
|
||||
from unittest.mock import Mock, call, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from plinth.app import App, FollowerComponent
|
||||
from plinth.app import App, FollowerComponent, Info
|
||||
from plinth.daemon import (Daemon, RelatedDaemon, app_is_running,
|
||||
diagnose_netcat, diagnose_port_listening)
|
||||
|
||||
privileged_modules_to_mock = ['plinth.privileged.service']
|
||||
|
||||
|
||||
class AppTest(App):
|
||||
"""Test application that contains a daemon."""
|
||||
|
||||
app_id = 'test-app'
|
||||
|
||||
|
||||
@pytest.fixture(name='daemon')
|
||||
def fixture_daemon():
|
||||
@ -19,6 +28,17 @@ def fixture_daemon():
|
||||
return Daemon('test-daemon', 'test-unit')
|
||||
|
||||
|
||||
@pytest.fixture(name='app_list')
|
||||
def fixture_app_list(daemon):
|
||||
"""A list of apps on which tests are to be run."""
|
||||
app1 = AppTest()
|
||||
app1.add(Info('test-app', 1))
|
||||
app1.add(daemon)
|
||||
with patch('plinth.app.App.list') as app_list:
|
||||
app_list.return_value = [app1]
|
||||
yield app_list
|
||||
|
||||
|
||||
def test_initialization():
|
||||
"""Test that component is initialized properly."""
|
||||
with pytest.raises(ValueError):
|
||||
@ -56,25 +76,51 @@ def test_is_enabled(service_is_enabled, daemon):
|
||||
service_is_enabled.assert_has_calls([call('test-unit', strict_check=True)])
|
||||
|
||||
|
||||
@patch('plinth.actions.superuser_run')
|
||||
def test_enable(superuser_run, daemon):
|
||||
@patch('subprocess.run')
|
||||
@patch('subprocess.call')
|
||||
def test_enable(subprocess_call, subprocess_run, app_list, mock_privileged,
|
||||
daemon):
|
||||
"""Test that enabling the daemon works."""
|
||||
daemon.enable()
|
||||
superuser_run.assert_has_calls([call('service', ['enable', 'test-unit'])])
|
||||
subprocess_call.assert_has_calls(
|
||||
[call(['systemctl', 'enable', 'test-unit'])])
|
||||
subprocess_run.assert_any_call(['systemctl', 'start', 'test-unit'],
|
||||
stdout=subprocess.DEVNULL, check=False)
|
||||
|
||||
subprocess_call.reset_mock()
|
||||
daemon.alias = 'test-unit-2'
|
||||
daemon.enable()
|
||||
superuser_run.assert_has_calls([
|
||||
call('service', ['enable', 'test-unit']),
|
||||
call('service', ['enable', 'test-unit-2'])
|
||||
subprocess_call.assert_has_calls([
|
||||
call(['systemctl', 'enable', 'test-unit']),
|
||||
call(['systemctl', 'enable', 'test-unit-2'])
|
||||
])
|
||||
subprocess_run.assert_any_call(['systemctl', 'start', 'test-unit'],
|
||||
stdout=subprocess.DEVNULL, check=False)
|
||||
subprocess_run.assert_any_call(['systemctl', 'start', 'test-unit-2'],
|
||||
stdout=subprocess.DEVNULL, check=False)
|
||||
|
||||
|
||||
@patch('plinth.actions.superuser_run')
|
||||
def test_disable(superuser_run, daemon):
|
||||
@patch('subprocess.run')
|
||||
@patch('subprocess.call')
|
||||
def test_disable(subprocess_call, subprocess_run, mock_privileged, daemon):
|
||||
"""Test that disabling the daemon works."""
|
||||
daemon.disable()
|
||||
superuser_run.assert_has_calls([call('service', ['disable', 'test-unit'])])
|
||||
subprocess_call.assert_has_calls(
|
||||
[call(['systemctl', 'disable', 'test-unit'])])
|
||||
subprocess_run.assert_any_call(['systemctl', 'stop', 'test-unit'],
|
||||
stdout=subprocess.DEVNULL, check=False)
|
||||
|
||||
subprocess_call.reset_mock()
|
||||
daemon.alias = 'test-unit-2'
|
||||
daemon.disable()
|
||||
subprocess_call.assert_has_calls([
|
||||
call(['systemctl', 'disable', 'test-unit']),
|
||||
call(['systemctl', 'disable', 'test-unit-2'])
|
||||
])
|
||||
subprocess_run.assert_any_call(['systemctl', 'stop', 'test-unit'],
|
||||
stdout=subprocess.DEVNULL, check=False)
|
||||
subprocess_run.assert_any_call(['systemctl', 'stop', 'test-unit-2'],
|
||||
stdout=subprocess.DEVNULL, check=False)
|
||||
|
||||
|
||||
@patch('plinth.action_utils.service_is_running')
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user