deluge: Fix app installation on Debian testing

Closes: #2322.

On Debian stable, mark deluge app as not available because deluge-web is not
working in this distribution.

- Default deluged configuration directory location changed. I didn't do
  migration from the old location because deluge app hasn't been working on
  Debian stable.
- Make deluge-web systemd service start after deluged service to prevent
  Connection Manager popup in deluge-web after system reboot.

Tests performed:
- On Debian stable:
  - Deluge app is not installable from the app page.
  - All functional tests are skipped.
- On Debian testing:
  - All tests pass.
  - After reboot, deluge-web service started after deluge service and there
    is no Connection Manager popup in deluge web UI.
  - Changing torrent download directory works.

Signed-off-by: Veiko Aasa <veiko17@disroot.org>
[sunil: Add type signature to an overridden method]
Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
This commit is contained in:
Veiko Aasa 2024-12-05 12:32:32 +02:00 committed by Sunil Mohan Adapa
parent 41675eec39
commit 85b6e00001
No known key found for this signature in database
GPG Key ID: 43EA1CFF0AA7C5F2
4 changed files with 48 additions and 68 deletions

View File

@ -11,6 +11,7 @@ from plinth.modules.apache.components import Webserver
from plinth.modules.backups.components import BackupRestore from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import (Firewall, from plinth.modules.firewall.components import (Firewall,
FirewallLocalProtection) FirewallLocalProtection)
from plinth.modules.upgrades import get_current_release
from plinth.modules.users import add_user_to_share_group from plinth.modules.users import add_user_to_share_group
from plinth.modules.users.components import UsersAndGroups from plinth.modules.users.components import UsersAndGroups
from plinth.package import Packages from plinth.package import Packages
@ -26,12 +27,26 @@ _description = [
SYSTEM_USER = 'debian-deluged' SYSTEM_USER = 'debian-deluged'
class DelugePackages(Packages):
"""Mark deluge app as not available in Debian Bookworm.
deluge-web is broken in Debian Bookworm. Related bug report:
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1031593
"""
def has_unavailable_packages(self) -> bool | None:
if get_current_release()[1] == 'bookworm':
return True
return super().has_unavailable_packages()
class DelugeApp(app_module.App): class DelugeApp(app_module.App):
"""FreedomBox app for Deluge.""" """FreedomBox app for Deluge."""
app_id = 'deluge' app_id = 'deluge'
_version = 8 _version = 9
def __init__(self) -> None: def __init__(self) -> None:
"""Create components for the app.""" """Create components for the app."""
@ -64,7 +79,7 @@ class DelugeApp(app_module.App):
allowed_groups=list(groups)) allowed_groups=list(groups))
self.add(shortcut) self.add(shortcut)
packages = Packages('packages-deluge', ['deluged', 'deluge-web']) packages = DelugePackages('packages-deluge', ['deluged', 'deluge-web'])
self.add(packages) self.add(packages)
dropin_configs = DropinConfigs( dropin_configs = DropinConfigs(

View File

@ -13,7 +13,7 @@ clients = [{
backup = { backup = {
'config': { 'config': {
'directories': ['/var/lib/deluged/.config'] 'directories': ['/var/lib/deluged/config']
}, },
'services': ['deluged', 'deluge-web'] 'services': ['deluged', 'deluge-web']
} }

View File

@ -13,10 +13,10 @@ from plinth.actions import privileged
from plinth.modules.deluge.utils import Config from plinth.modules.deluge.utils import Config
DELUGED_DEFAULT_FILE = '/etc/default/deluged' DELUGED_DEFAULT_FILE = '/etc/default/deluged'
DELUGE_CONF_DIR = pathlib.Path('/var/lib/deluged/.config/deluge/') DELUGE_CONF_DIR = pathlib.Path('/var/lib/deluged/config/')
DELUGE_WEB_SYSTEMD_SERVICE_PATH = '/etc/systemd/system/deluge-web.service' DELUGE_WEB_SYSTEMD_SERVICE_PATH = '/etc/systemd/system/deluge-web.service'
DELUGE_WEB_SYSTEMD_SERVICE = ''' DELUGE_WEB_SYSTEMD_SERVICE = f'''
# #
# This file is managed and overwritten by Plinth. If you wish to edit # This file is managed and overwritten by Plinth. If you wish to edit
# it, disable Deluge in Plinth, remove this file and manage it manually. # it, disable Deluge in Plinth, remove this file and manage it manually.
@ -25,9 +25,10 @@ DELUGE_WEB_SYSTEMD_SERVICE = '''
Description=Deluge Web Interface Description=Deluge Web Interface
Documentation=man:deluge-web(1) Documentation=man:deluge-web(1)
After=network.target After=network.target
After=deluged.service
[Service] [Service]
ExecStart=bash -c "/usr/bin/deluge-web --base=deluge $(/usr/bin/deluge-web --version | grep deluge-web | cut -f2 -d' ' | grep -q '^1.' && echo '' || echo '--do-not-daemonize')" ExecStart=/usr/bin/deluge-web --config {DELUGE_CONF_DIR} --base=deluge --do-not-daemonize
Restart=on-failure Restart=on-failure
User=debian-deluged User=debian-deluged
Group=debian-deluged Group=debian-deluged

View File

@ -35,14 +35,12 @@ class TestDelugeApp(functional.BaseAppTests):
'nogroupuser') 'nogroupuser')
assert not functional.is_available(session_browser, 'deluge') assert not functional.is_available(session_browser, 'deluge')
functional.login(session_browser)
def test_upload_torrent(self, session_browser): def test_upload_torrent(self, session_browser):
"""Test uploading a torrent.""" """Test uploading a torrent."""
functional.app_enable(session_browser, 'deluge') functional.app_enable(session_browser, 'deluge')
_remove_all_torrents(session_browser) _remove_all_torrents(session_browser)
_upload_sample_torrent(session_browser) _upload_sample_torrent(session_browser)
assert _get_number_of_torrents(session_browser) == 1 assert len(_get_torrents(session_browser)) == 1
@pytest.mark.backups @pytest.mark.backups
def test_backup_restore(self, session_browser): def test_backup_restore(self, session_browser):
@ -55,7 +53,7 @@ class TestDelugeApp(functional.BaseAppTests):
_remove_all_torrents(session_browser) _remove_all_torrents(session_browser)
functional.backup_restore(session_browser, 'deluge', 'test_deluge') functional.backup_restore(session_browser, 'deluge', 'test_deluge')
assert functional.service_is_running(session_browser, 'deluge') assert functional.service_is_running(session_browser, 'deluge')
assert _get_number_of_torrents(session_browser) == 1 assert len(_get_torrents(session_browser)) == 1
def _get_active_window_title(browser): def _get_active_window_title(browser):
@ -81,49 +79,30 @@ def _ensure_logged_in(browser):
# After a backup restore, service may not be available immediately # After a backup restore, service may not be available immediately
functional.eventually(service_is_available) functional.eventually(service_is_available)
time.sleep(1) # Wait for Ext.js application in initialize functional.eventually(browser.is_element_present_by_id, ['add'])
if _get_active_window_title(browser) != 'Login': def logged_in():
return active_window_title = _get_active_window_title(browser)
browser.find_by_id('_password').first.fill('deluge') # Change Default Password window appears once.
_click_active_window_button(browser, 'Login') if active_window_title == 'Change Default Password':
_click_active_window_button(browser, 'No')
assert functional.eventually( if active_window_title == 'Login':
lambda: _get_active_window_title(browser) != 'Login') browser.find_by_id('_password').first.fill('deluge')
functional.eventually(browser.is_element_not_present_by_css, _click_active_window_button(browser, 'Login')
args=['#add.x-item-disabled'], timeout=0.3)
return browser.is_element_not_present_by_css('#add .x-item-disabled')
def _open_connection_manager(browser): functional.eventually(logged_in)
"""Open the connection manager dialog if not already open."""
title = 'Connection Manager'
if _get_active_window_title(browser) == title:
return
browser.find_by_css('button.x-deluge-connection-manager').first.click()
functional.eventually(lambda: _get_active_window_title(browser) == title)
def _ensure_connected(browser):
"""Type the connection password if required and start Deluge daemon."""
_ensure_logged_in(browser)
# Change Default Password window appears once.
if _get_active_window_title(browser) == 'Change Default Password':
_click_active_window_button(browser, 'No')
assert functional.eventually(browser.is_element_not_present_by_css,
args=['#add.x-item-disabled'])
def _remove_all_torrents(browser): def _remove_all_torrents(browser):
"""Remove all torrents from deluge.""" """Remove all torrents from deluge."""
_ensure_connected(browser) _ensure_logged_in(browser)
while browser.find_by_css('#torrentGrid .torrent-name'):
browser.find_by_css('#torrentGrid .torrent-name').first.click()
for torrent in _get_torrents(browser):
torrent.click()
# Click remove toolbar button # Click remove toolbar button
browser.find_by_id('remove').first.click() browser.find_by_id('remove').first.click()
@ -155,9 +134,9 @@ def _click_active_window_button(browser, button_text):
def _upload_sample_torrent(browser): def _upload_sample_torrent(browser):
"""Upload a sample torrent into deluge.""" """Upload a sample torrent into deluge."""
_ensure_connected(browser) _ensure_logged_in(browser)
number_of_torrents = _get_number_of_torrents(browser) number_of_torrents = len(_get_torrents(browser))
# Click add toolbar button # Click add toolbar button
browser.find_by_id('add').first.click() browser.find_by_id('add').first.click()
@ -168,35 +147,20 @@ def _upload_sample_torrent(browser):
file_path = os.path.join(os.path.dirname(__file__), 'data', file_path = os.path.join(os.path.dirname(__file__), 'data',
'sample.torrent') 'sample.torrent')
browser.attach_file('file', file_path)
if browser.find_by_id('fileUploadForm'): # deluge-web 2.x
browser.attach_file('file', file_path)
else: # deluge-web 1.x
browser.find_by_css('button.x-deluge-add-file').first.click()
# Add from file window appears
functional.eventually(
lambda: _get_active_window_title(browser) == 'Add from File')
# Attach file
browser.attach_file('file', file_path)
# Click Add
_click_active_window_button(browser, 'Add')
functional.eventually(
lambda: _get_active_window_title(browser) == 'Add Torrents')
# Click Add # Click Add
time.sleep(1) time.sleep(1)
_click_active_window_button(browser, 'Add') _click_active_window_button(browser, 'Add')
functional.eventually( functional.eventually(
lambda: _get_number_of_torrents(browser) > number_of_torrents) lambda: len(_get_torrents(browser)) > number_of_torrents)
def _get_number_of_torrents(browser): def _get_torrents(browser):
"""Return the number torrents currently in deluge.""" """Return list of torrents currently in deluge."""
_ensure_connected(browser) _ensure_logged_in(browser)
# wait until torrent list is loaded
functional.eventually(browser.is_element_present_by_css, ['.x-deluge-all'])
return len(browser.find_by_css('#torrentGrid .torrent-name')) return browser.find_by_css('#torrentGrid .torrent-name')