mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-03-04 08:53:42 +00:00
upgrades: Use privileged decorator for actions
Tests:
- DONE: Functional tests work
- DONE: Initial setup works
- DONE: Automatic upgrades are enable by default
- DONE: apt preferences have been updated
- DONE: Enabling backports works
- DONE: Configuration file is created
- DONE: Correct status is shown in the app page
- DONE: Enabling/disabling automatic upgrades works
- DONE: Configuration file is updated
- DONE: Correct status is shown in the app page
- DONE: Manual triggering of updates work
- DONE: Log is shown properly in the app page
- DONE: Checking for distribution upgrade works
- DONE: Distribution upgrade from stable to testing works
- DONE: When running on btrfs distribution, snapshot is created before.
- DONE: Snapshots will be disable before upgrade and re-enabled later.
- DONE: When searx is enabled before upgrade, it's uwsgi will be disabled and
re-enabled later.
- Failures due to freedombox package not being the latest version (with the
changes).
- DONE: Development Vagrant box
- DONE: Automatic updates are disabled during development setup
- DONE: Development Container
- DONE: Automatic updates are disabled during development setup
- DONE: On stable, backports are enabled when running tests
Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
parent
11a27d8efc
commit
66c1ddc404
2
Vagrantfile
vendored
2
Vagrantfile
vendored
@ -16,7 +16,7 @@ Vagrant.configure(2) do |config|
|
||||
end
|
||||
config.vm.provision "shell", run: 'always', inline: <<-SHELL
|
||||
# Disable automatic upgrades
|
||||
/vagrant/actions/upgrades disable-auto
|
||||
echo -e 'APT::Periodic::Update-Package-Lists "0";\nAPT::Periodic::Unattended-Upgrade "0";' > //etc/apt/apt.conf.d/20auto-upgrades
|
||||
# Do not run system plinth
|
||||
systemctl stop plinth
|
||||
systemctl disable plinth
|
||||
|
||||
@ -193,7 +193,7 @@ mount -o remount /freedombox
|
||||
if [[ "{distribution}" == "stable" && ! -e $BACKPORTS_SOURCES_LIST ]]
|
||||
then
|
||||
echo "> In container: Enable backports"
|
||||
/freedombox/actions/upgrades activate-backports
|
||||
/freedombox/actions/actions upgrades activate_backports --no-args
|
||||
fi
|
||||
|
||||
echo "> In container: Upgrade packages"
|
||||
@ -698,7 +698,10 @@ def _setup(image_file, distribution):
|
||||
return
|
||||
|
||||
logger.info('In container: Disabling automatic updates temporarily')
|
||||
_runc(image_file, ['/usr/share/plinth/actions/upgrades', 'disable-auto'])
|
||||
contents = 'APT::Periodic::Update-Package-Lists "0";\n' \
|
||||
'APT::Periodic::Unattended-Upgrade "0";\n'
|
||||
_runc(image_file, ['tee', '/etc/apt/apt.conf.d/20auto-upgrades'],
|
||||
input=contents.encode())
|
||||
|
||||
logger.info('In container: Disabling FreedomBox service')
|
||||
_runc(image_file, ['systemctl', 'disable', 'plinth'],
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""
|
||||
FreedomBox app for upgrades.
|
||||
"""
|
||||
"""FreedomBox app for upgrades."""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
@ -13,14 +10,13 @@ from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import gettext_noop
|
||||
|
||||
import plinth
|
||||
from plinth import actions
|
||||
from plinth import app as app_module
|
||||
from plinth import cfg, glib, kvstore, menu
|
||||
from plinth.daemon import RelatedDaemon
|
||||
from plinth.modules.backups.components import BackupRestore
|
||||
from plinth.package import Packages
|
||||
|
||||
from . import manifest
|
||||
from . import manifest, privileged
|
||||
|
||||
first_boot_steps = [
|
||||
{
|
||||
@ -48,10 +44,6 @@ BACKPORTS_REQUESTED_KEY = 'upgrades_backports_requested'
|
||||
|
||||
DIST_UPGRADE_ENABLED_KEY = 'upgrades_dist_upgrade_enabled'
|
||||
|
||||
SOURCES_LIST = '/etc/apt/sources.list'
|
||||
|
||||
BACKPORTS_SOURCES_LIST = '/etc/apt/sources.list.d/freedombox2.list'
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -140,11 +132,11 @@ class UpgradesApp(app_module.App):
|
||||
|
||||
# Enable automatic upgrades but only on first install
|
||||
if not old_version and not cfg.develop:
|
||||
actions.superuser_run('upgrades', ['enable-auto'])
|
||||
privileged.enable_auto()
|
||||
|
||||
# Update apt preferences whenever on first install and on version
|
||||
# increment.
|
||||
actions.superuser_run('upgrades', ['setup'])
|
||||
privileged.setup()
|
||||
|
||||
# When upgrading from a version without first boot wizard for
|
||||
# backports, assume backports have been requested.
|
||||
@ -161,30 +153,10 @@ class UpgradesApp(app_module.App):
|
||||
setup_repositories(None)
|
||||
|
||||
|
||||
def is_enabled():
|
||||
"""Return whether the module is enabled."""
|
||||
output = actions.run('upgrades', ['check-auto'])
|
||||
return 'True' in output.split()
|
||||
|
||||
|
||||
def enable():
|
||||
"""Enable the module."""
|
||||
actions.superuser_run('upgrades', ['enable-auto'])
|
||||
|
||||
|
||||
def disable():
|
||||
"""Disable the module."""
|
||||
actions.superuser_run('upgrades', ['disable-auto'])
|
||||
|
||||
|
||||
def setup_repositories(_):
|
||||
"""Setup apt repositories for backports."""
|
||||
if is_backports_requested():
|
||||
command = ['activate-backports']
|
||||
if cfg.develop:
|
||||
command.append('--develop')
|
||||
|
||||
actions.superuser_run('upgrades', command)
|
||||
privileged.activate_backports(cfg.develop)
|
||||
|
||||
|
||||
def check_dist_upgrade(_):
|
||||
@ -196,12 +168,8 @@ def check_dist_upgrade(_):
|
||||
def try_start_dist_upgrade(test=False):
|
||||
"""Try to start dist upgrade."""
|
||||
from plinth.notification import Notification
|
||||
command = ['start-dist-upgrade']
|
||||
if test:
|
||||
command.append('--test')
|
||||
|
||||
output = actions.superuser_run('upgrades', command)
|
||||
result = json.loads(output)
|
||||
result = privileged.start_dist_upgrade(test)
|
||||
dist_upgrade_started = result['dist_upgrade_started']
|
||||
reason = result['reason']
|
||||
if 'found-previous' in reason:
|
||||
@ -270,7 +238,7 @@ def set_dist_upgrade_enabled(enabled=True):
|
||||
|
||||
def is_backports_enabled():
|
||||
"""Return whether backports are enabled in the system configuration."""
|
||||
return os.path.exists(BACKPORTS_SOURCES_LIST)
|
||||
return os.path.exists(privileged.BACKPORTS_SOURCES_LIST)
|
||||
|
||||
|
||||
def get_current_release():
|
||||
|
||||
196
actions/upgrades → plinth/modules/upgrades/privileged.py
Executable file → Normal file
196
actions/upgrades → plinth/modules/upgrades/privileged.py
Executable file → Normal file
@ -1,30 +1,26 @@
|
||||
#!/usr/bin/python3
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""
|
||||
Configures or runs unattended-upgrades
|
||||
"""
|
||||
"""Configure or run unattended-upgrades."""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from typing import List, Tuple
|
||||
from typing import List, Tuple, Union
|
||||
|
||||
from plinth.action_utils import (apt_hold, apt_hold_flag, apt_hold_freedombox,
|
||||
apt_unhold_freedombox, debconf_set_selections,
|
||||
is_package_manager_busy, run_apt_command,
|
||||
service_daemon_reload, service_restart)
|
||||
from plinth.actions import privileged
|
||||
from plinth.modules.apache.components import check_url
|
||||
from plinth.modules.snapshot import is_apt_snapshots_enabled
|
||||
from plinth.modules.snapshot import is_supported as snapshot_is_supported
|
||||
from plinth.modules.snapshot import load_augeas as snapshot_load_augeas
|
||||
from plinth.modules.upgrades import (BACKPORTS_SOURCES_LIST, SOURCES_LIST,
|
||||
get_current_release, is_backports_current)
|
||||
|
||||
SOURCES_LIST = '/etc/apt/sources.list'
|
||||
BACKPORTS_SOURCES_LIST = '/etc/apt/sources.list.d/freedombox2.list'
|
||||
|
||||
AUTO_CONF_FILE = '/etc/apt/apt.conf.d/20auto-upgrades'
|
||||
LOG_FILE = '/var/log/unattended-upgrades/unattended-upgrades.log'
|
||||
@ -32,7 +28,8 @@ DPKG_LOG_FILE = '/var/log/unattended-upgrades/unattended-upgrades-dpkg.log'
|
||||
RELEASE_FILE_URL = \
|
||||
'https://deb.debian.org/debian/dists/{}/Release'
|
||||
|
||||
APT_PREFERENCES_FREEDOMBOX = '''Explanation: This file is managed by FreedomBox, do not edit.
|
||||
APT_PREFERENCES_FREEDOMBOX = \
|
||||
'''Explanation: This file is managed by FreedomBox, do not edit.
|
||||
Explanation: Allow carefully selected updates to 'freedombox' from backports.
|
||||
Package: src:freedombox
|
||||
Pin: release a={}-backports
|
||||
@ -42,7 +39,8 @@ Pin-Priority: 500
|
||||
# Whenever these preferences needs to change, increment the version number
|
||||
# upgrades app. This ensures that setup is run again and the new contents are
|
||||
# overwritten on the old file.
|
||||
APT_PREFERENCES_APPS = '''Explanation: This file is managed by FreedomBox, do not edit.
|
||||
APT_PREFERENCES_APPS = \
|
||||
'''Explanation: This file is managed by FreedomBox, do not edit.
|
||||
Explanation: matrix-synapse shall not be available in Debian stable but
|
||||
Explanation: only in backports. Upgrade priority of packages that have needed
|
||||
Explanation: versions only in backports.
|
||||
@ -104,7 +102,7 @@ Description=Upgrade to new stable Debian release
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/share/plinth/actions/upgrades dist-upgrade
|
||||
ExecStart=/usr/share/plinth/actions/actions upgrades dist_upgrade --no-args
|
||||
KillMode=process
|
||||
TimeoutSec=12hr
|
||||
'''
|
||||
@ -116,46 +114,18 @@ dist_upgrade_flag = pathlib.Path(
|
||||
'/var/lib/freedombox/dist-upgrade-in-progress')
|
||||
|
||||
|
||||
def parse_arguments():
|
||||
"""Return parsed command line arguments as dictionary"""
|
||||
parser = argparse.ArgumentParser()
|
||||
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
|
||||
|
||||
subparsers.add_parser('run', help='Upgrade packages on the system')
|
||||
subparsers.add_parser('check-auto',
|
||||
help='Check if automatic upgrades are enabled')
|
||||
subparsers.add_parser('enable-auto', help='Enable automatic upgrades')
|
||||
subparsers.add_parser('disable-auto', help='Disable automatic upgrades.')
|
||||
subparsers.add_parser('get-log', help='Print the automatic upgrades log')
|
||||
|
||||
subparsers.add_parser('setup', help='Setup apt preferences')
|
||||
|
||||
activate_backports = subparsers.add_parser(
|
||||
'activate-backports', help='Activate backports if possible')
|
||||
activate_backports.add_argument('--develop', required=False, default=False,
|
||||
action='store_true',
|
||||
help='Development mode')
|
||||
|
||||
start_dist_upgrade = subparsers.add_parser(
|
||||
'start-dist-upgrade', help='Check and start dist upgrade process')
|
||||
start_dist_upgrade.add_argument(
|
||||
'--test', required=False, default=False, action='store_true',
|
||||
help='Test dist-upgrade from stable to testing')
|
||||
subparsers.add_parser('dist-upgrade', help='Perform dist upgrade')
|
||||
|
||||
subparsers.required = True
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def _release_held_freedombox():
|
||||
"""In case freedombox package was left in held state by an interrupted
|
||||
process, release it."""
|
||||
"""If freedombox package was left in held state, release it.
|
||||
|
||||
This can happen due to an interrupted process.
|
||||
"""
|
||||
if apt_hold_flag.exists() and not is_package_manager_busy():
|
||||
apt_unhold_freedombox()
|
||||
|
||||
|
||||
def _run():
|
||||
"""Run unattended-upgrades"""
|
||||
@privileged
|
||||
def run():
|
||||
"""Run unattended-upgrades."""
|
||||
subprocess.run(['dpkg', '--configure', '-a'], check=False)
|
||||
run_apt_command(['--fix-broken', 'install'])
|
||||
_release_held_freedombox()
|
||||
@ -166,18 +136,6 @@ def _run():
|
||||
start_new_session=True)
|
||||
|
||||
|
||||
def subcommand_run(_):
|
||||
"""Run unattended-upgrades"""
|
||||
try:
|
||||
_run()
|
||||
except FileNotFoundError:
|
||||
print('Error: systemctl is not available.', file=sys.stderr)
|
||||
sys.exit(2)
|
||||
except Exception as error:
|
||||
print('Error: {0}'.format(error), file=sys.stderr)
|
||||
sys.exit(3)
|
||||
|
||||
|
||||
def _check_auto() -> bool:
|
||||
"""Check if automatic upgrades are enabled."""
|
||||
arguments = [
|
||||
@ -193,45 +151,48 @@ def _check_auto() -> bool:
|
||||
return bool(update_interval)
|
||||
|
||||
|
||||
def subcommand_check_auto(_):
|
||||
"""Check if automatic upgrades are enabled"""
|
||||
try:
|
||||
print(_check_auto())
|
||||
except subprocess.CalledProcessError as error:
|
||||
print('Error: {0}'.format(error), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
@privileged
|
||||
def check_auto() -> bool:
|
||||
"""Check if automatic upgrades are enabled."""
|
||||
return _check_auto()
|
||||
|
||||
|
||||
def subcommand_enable_auto(_):
|
||||
"""Enable automatic upgrades"""
|
||||
@privileged
|
||||
def enable_auto():
|
||||
"""Enable automatic upgrades."""
|
||||
with open(AUTO_CONF_FILE, 'w', encoding='utf-8') as conffile:
|
||||
conffile.write('APT::Periodic::Update-Package-Lists "1";\n')
|
||||
conffile.write('APT::Periodic::Unattended-Upgrade "1";\n')
|
||||
|
||||
|
||||
def subcommand_disable_auto(_):
|
||||
"""Disable automatic upgrades"""
|
||||
@privileged
|
||||
def disable_auto():
|
||||
"""Disable automatic upgrades."""
|
||||
with open(AUTO_CONF_FILE, 'w', encoding='utf-8') as conffile:
|
||||
conffile.write('APT::Periodic::Update-Package-Lists "0";\n')
|
||||
conffile.write('APT::Periodic::Unattended-Upgrade "0";\n')
|
||||
|
||||
|
||||
def subcommand_get_log(_):
|
||||
"""Print the automatic upgrades log."""
|
||||
@privileged
|
||||
def get_log() -> str:
|
||||
"""Return the automatic upgrades log."""
|
||||
log_lines = []
|
||||
try:
|
||||
print('==> ' + os.path.basename(LOG_FILE))
|
||||
log_lines.append('==> ' + os.path.basename(LOG_FILE))
|
||||
with open(LOG_FILE, 'r', encoding='utf-8') as file_handle:
|
||||
print(file_handle.read())
|
||||
log_lines.append(file_handle.read())
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
try:
|
||||
print('==> ' + os.path.basename(DPKG_LOG_FILE))
|
||||
log_lines.append('==> ' + os.path.basename(DPKG_LOG_FILE))
|
||||
with open(DPKG_LOG_FILE, 'r', encoding='utf-8') as file_handle:
|
||||
print(file_handle.read())
|
||||
log_lines.append(file_handle.read())
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
return '\n'.join(log_lines)
|
||||
|
||||
|
||||
def _get_protocol() -> str:
|
||||
"""Return the protocol to use for newly added repository sources."""
|
||||
@ -278,8 +239,10 @@ def _check_and_backports_sources(develop=False):
|
||||
if os.path.exists(old_sources_list):
|
||||
os.remove(old_sources_list)
|
||||
|
||||
from plinth.modules.upgrades import (get_current_release,
|
||||
is_backports_current)
|
||||
if is_backports_current():
|
||||
print('Repositories list up-to-date. Skipping update.')
|
||||
logging.info('Repositories list up-to-date. Skipping update.')
|
||||
return
|
||||
|
||||
try:
|
||||
@ -290,25 +253,26 @@ def _check_and_backports_sources(develop=False):
|
||||
for line in default_origin.readlines()
|
||||
]
|
||||
except FileNotFoundError:
|
||||
print('Could not open /etc/dpkg/origins/default')
|
||||
logging.info('Could not open /etc/dpkg/origins/default')
|
||||
return
|
||||
|
||||
if not any(matches):
|
||||
print('System is running a derivative of Debian. Skip enabling '
|
||||
'backports.')
|
||||
logging.info('System is running a derivative of Debian. Skip enabling '
|
||||
'backports.')
|
||||
return
|
||||
|
||||
release, dist = get_current_release()
|
||||
if release == 'unstable' or (release == 'testing' and not develop):
|
||||
print(f'System release is {release}. Skip enabling backports.')
|
||||
logging.info(f'System release is {release}. Skip enabling backports.')
|
||||
return
|
||||
|
||||
protocol = _get_protocol()
|
||||
if protocol == 'tor+http':
|
||||
print('Package download over Tor is enabled.')
|
||||
logging.info('Package download over Tor is enabled.')
|
||||
|
||||
if not _is_release_file_available(protocol, dist, backports=True):
|
||||
print(f'Release file for {dist}-backports is not available yet.')
|
||||
logging.info(
|
||||
f'Release file for {dist}-backports is not available yet.')
|
||||
return
|
||||
|
||||
print(f'{dist}-backports is now available. Adding to sources.')
|
||||
@ -328,12 +292,14 @@ def _add_apt_preferences():
|
||||
# Don't try to remove 50freedombox3.pref as this file is shipped with the
|
||||
# Debian package and is removed using maintainer scripts.
|
||||
|
||||
from plinth.modules.upgrades import get_current_release
|
||||
_, dist = get_current_release()
|
||||
if dist == 'sid':
|
||||
print(f'System distribution is {dist}. Skip setting apt preferences '
|
||||
'for backports.')
|
||||
logging.info(
|
||||
f'System distribution is {dist}. Skip setting apt preferences '
|
||||
'for backports.')
|
||||
else:
|
||||
print(f'Setting apt preferences for {dist}-backports.')
|
||||
logging.info(f'Setting apt preferences for {dist}-backports.')
|
||||
with open(base_path / '50freedombox4.pref', 'w',
|
||||
encoding='utf-8') as file_handle:
|
||||
file_handle.write(APT_PREFERENCES_FREEDOMBOX.format(dist))
|
||||
@ -350,17 +316,20 @@ def _is_sufficient_free_space() -> bool:
|
||||
|
||||
|
||||
def _check_dist_upgrade(test_upgrade=False) -> Tuple[bool, str]:
|
||||
"""Check for new stable release, if updates are enabled, and if there is
|
||||
"""Check if a distribution upgrade be performed.
|
||||
|
||||
Check for new stable release, if updates are enabled, and if there is
|
||||
enough free space for the dist upgrade.
|
||||
|
||||
If test_upgrade is True, also check for upgrade to testing.
|
||||
|
||||
Returns (boolean, string) indicating if the upgrade is ready, and a reason
|
||||
Return (boolean, string) indicating if the upgrade is ready, and a reason
|
||||
if not.
|
||||
"""
|
||||
if dist_upgrade_flag.exists():
|
||||
return (True, 'found-previous')
|
||||
|
||||
from plinth.modules.upgrades import get_current_release
|
||||
release, dist = get_current_release()
|
||||
if release in ['unstable', 'testing']:
|
||||
return (False, f'already-{release}')
|
||||
@ -429,7 +398,8 @@ def _check_dist_upgrade(test_upgrade=False) -> Tuple[bool, str]:
|
||||
def _take_snapshot_and_disable() -> bool:
|
||||
"""Take a snapshot if supported and enabled, then disable snapshots.
|
||||
|
||||
Return whether snapshots shall be re-enabled at the end."""
|
||||
Return whether snapshots shall be re-enabled at the end.
|
||||
"""
|
||||
if snapshot_is_supported():
|
||||
print('Taking a snapshot before dist upgrade...', flush=True)
|
||||
subprocess.run([
|
||||
@ -467,7 +437,8 @@ def _restore_snapshots_config(reenable=False):
|
||||
def _disable_searx() -> bool:
|
||||
"""If searx is enabled, disable it until we can upgrade it properly.
|
||||
|
||||
Return whether searx was originally enabled."""
|
||||
Return whether searx was originally enabled.
|
||||
"""
|
||||
searx_is_enabled = pathlib.Path(
|
||||
'/etc/uwsgi/apps-enabled/searx.ini').exists()
|
||||
if searx_is_enabled:
|
||||
@ -481,7 +452,9 @@ def _disable_searx() -> bool:
|
||||
|
||||
def _update_searx(reenable=False):
|
||||
"""If searx is installed, update search engines list.
|
||||
Re-enable if previously enabled."""
|
||||
|
||||
Re-enable if previously enabled.
|
||||
"""
|
||||
if pathlib.Path('/etc/searx/settings.yml').exists():
|
||||
print('Updating searx search engines list...', flush=True)
|
||||
subprocess.run([
|
||||
@ -567,19 +540,20 @@ def _perform_dist_upgrade():
|
||||
dist_upgrade_flag.unlink()
|
||||
|
||||
|
||||
def subcommand_setup(_):
|
||||
@privileged
|
||||
def setup():
|
||||
"""Setup apt preferences."""
|
||||
_add_apt_preferences()
|
||||
|
||||
|
||||
def subcommand_activate_backports(arguments):
|
||||
@privileged
|
||||
def activate_backports(develop: bool = False):
|
||||
"""Setup software repositories needed for FreedomBox.
|
||||
|
||||
Repositories list for now only contains the backports. If the file exists,
|
||||
assume that it contains backports.
|
||||
|
||||
"""
|
||||
_check_and_backports_sources(arguments.develop)
|
||||
_check_and_backports_sources(develop)
|
||||
|
||||
|
||||
def _start_dist_upgrade_service():
|
||||
@ -595,7 +569,8 @@ def _start_dist_upgrade_service():
|
||||
start_new_session=True)
|
||||
|
||||
|
||||
def subcommand_start_dist_upgrade(arguments):
|
||||
@privileged
|
||||
def start_dist_upgrade(test: bool = False) -> dict[str, Union[str, bool]]:
|
||||
"""Start dist upgrade process.
|
||||
|
||||
Check if a new stable release is available, and start dist-upgrade process
|
||||
@ -603,31 +578,14 @@ def subcommand_start_dist_upgrade(arguments):
|
||||
"""
|
||||
_release_held_freedombox()
|
||||
|
||||
upgrade_ready, reason = _check_dist_upgrade(arguments.test)
|
||||
upgrade_ready, reason = _check_dist_upgrade(test)
|
||||
if upgrade_ready:
|
||||
_start_dist_upgrade_service()
|
||||
|
||||
print(
|
||||
json.dumps({
|
||||
'dist_upgrade_started': upgrade_ready,
|
||||
'reason': reason,
|
||||
}))
|
||||
return {'dist_upgrade_started': upgrade_ready, 'reason': reason}
|
||||
|
||||
|
||||
def subcommand_dist_upgrade(_):
|
||||
"""Perform major distribution upgrade.
|
||||
"""
|
||||
@privileged
|
||||
def dist_upgrade():
|
||||
"""Perform major distribution upgrade."""
|
||||
_perform_dist_upgrade()
|
||||
|
||||
|
||||
def main():
|
||||
"""Parse arguments and perform all duties"""
|
||||
arguments = parse_arguments()
|
||||
|
||||
subcommand = arguments.subcommand.replace('-', '_')
|
||||
subcommand_method = globals()['subcommand_' + subcommand]
|
||||
subcommand_method(arguments)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@ -1,7 +1,6 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""
|
||||
FreedomBox app for upgrades.
|
||||
"""
|
||||
"""FreedomBox app for upgrades."""
|
||||
|
||||
import subprocess
|
||||
|
||||
from apt.cache import Cache
|
||||
@ -13,34 +12,37 @@ from django.utils.translation import gettext as _
|
||||
from django.views.generic import TemplateView
|
||||
from django.views.generic.edit import FormView
|
||||
|
||||
from plinth import __version__, actions, package
|
||||
from plinth.errors import ActionError
|
||||
from plinth import __version__, package
|
||||
from plinth.modules import first_boot, upgrades
|
||||
from plinth.views import AppView
|
||||
|
||||
from . import privileged
|
||||
from .forms import BackportsFirstbootForm, ConfigureForm, UpdateFirstbootForm
|
||||
|
||||
|
||||
class UpgradesConfigurationView(AppView):
|
||||
"""Serve configuration page."""
|
||||
|
||||
form_class = ConfigureForm
|
||||
success_url = reverse_lazy('upgrades:index')
|
||||
template_name = "upgrades_configure.html"
|
||||
app_id = 'upgrades'
|
||||
|
||||
def get_initial(self):
|
||||
"""Return the initial values for the form."""
|
||||
return {
|
||||
'auto_upgrades_enabled': upgrades.is_enabled(),
|
||||
'auto_upgrades_enabled': privileged.check_auto(),
|
||||
'dist_upgrade_enabled': upgrades.is_dist_upgrade_enabled()
|
||||
}
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
"""Add additional context data for template."""
|
||||
context = super().get_context_data(*args, **kwargs)
|
||||
context['can_activate_backports'] = upgrades.can_activate_backports()
|
||||
context['is_backports_requested'] = upgrades.is_backports_requested()
|
||||
context['is_busy'] = (_is_updating()
|
||||
or package.is_package_manager_busy())
|
||||
context['log'] = get_log()
|
||||
context['log'] = privileged.get_log()
|
||||
context['refresh_page_sec'] = 3 if context['is_busy'] else None
|
||||
context['version'] = __version__
|
||||
context['new_version'] = is_newer_version_available()
|
||||
@ -58,10 +60,10 @@ class UpgradesConfigurationView(AppView):
|
||||
|
||||
try:
|
||||
if new_status['auto_upgrades_enabled']:
|
||||
upgrades.enable()
|
||||
privileged.enable_auto()
|
||||
else:
|
||||
upgrades.disable()
|
||||
except ActionError as exception:
|
||||
privileged.disable_auto()
|
||||
except Exception as exception:
|
||||
error = exception.args[2]
|
||||
messages.error(
|
||||
self.request,
|
||||
@ -89,14 +91,14 @@ class UpgradesConfigurationView(AppView):
|
||||
|
||||
|
||||
def is_newer_version_available():
|
||||
"""Returns whether a newer Freedombox version is available."""
|
||||
"""Return whether a newer Freedombox version is available."""
|
||||
cache = Cache()
|
||||
freedombox = cache['freedombox']
|
||||
return not freedombox.candidate.is_installed
|
||||
|
||||
|
||||
def get_os_release():
|
||||
"""Returns the Debian release number and name."""
|
||||
"""Return the Debian release number and name."""
|
||||
output = 'Error: Cannot read PRETTY_NAME in /etc/os-release.'
|
||||
with open('/etc/os-release', 'r', encoding='utf-8') as release_file:
|
||||
for line in release_file:
|
||||
@ -107,11 +109,6 @@ def get_os_release():
|
||||
return output
|
||||
|
||||
|
||||
def get_log():
|
||||
"""Return the current log for unattended upgrades."""
|
||||
return actions.superuser_run('upgrades', ['get-log'])
|
||||
|
||||
|
||||
def _is_updating():
|
||||
"""Check if manually triggered update is running."""
|
||||
command = ['systemctl', 'is-active', 'freedombox-manual-upgrade']
|
||||
@ -124,9 +121,9 @@ def upgrade(request):
|
||||
"""Serve the upgrade page."""
|
||||
if request.method == 'POST':
|
||||
try:
|
||||
actions.superuser_run('upgrades', ['run'])
|
||||
privileged.run()
|
||||
messages.success(request, _('Upgrade process started.'))
|
||||
except ActionError:
|
||||
except Exception:
|
||||
messages.error(request, _('Starting upgrade failed.'))
|
||||
|
||||
return redirect(reverse_lazy('upgrades:index'))
|
||||
@ -144,6 +141,7 @@ def activate_backports(request):
|
||||
|
||||
class BackportsFirstbootView(FormView):
|
||||
"""View to configure backports during first boot wizard."""
|
||||
|
||||
template_name = 'backports-firstboot.html'
|
||||
form_class = BackportsFirstbootForm
|
||||
|
||||
@ -179,6 +177,7 @@ class BackportsFirstbootView(FormView):
|
||||
|
||||
class UpdateFirstbootView(FormView):
|
||||
"""View to run initial update during first boot wizard."""
|
||||
|
||||
template_name = 'update-firstboot.html'
|
||||
form_class = UpdateFirstbootForm
|
||||
|
||||
@ -197,7 +196,7 @@ class UpdateFirstbootView(FormView):
|
||||
"""Run update if selected, and mark step as done."""
|
||||
self.update = form.cleaned_data['update_now']
|
||||
if self.update:
|
||||
actions.superuser_run('upgrades', ['run'])
|
||||
privileged.run()
|
||||
|
||||
first_boot.mark_step_done('initial_update')
|
||||
return super().form_valid(form)
|
||||
@ -205,6 +204,7 @@ class UpdateFirstbootView(FormView):
|
||||
|
||||
class UpdateFirstbootProgressView(TemplateView):
|
||||
"""View to show initial update progress."""
|
||||
|
||||
template_name = 'update-firstboot-progress.html'
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user