upgrades: Add repair step for held packages

Package holds are only expected when apps are being installed or
uninstalled, or during distribution upgrade process. At any other
time, package holds are not expected and should be released.

Tests:

- Place a hold on one package. Run the upgrades diagnostics, which
  will have a failure. Try to repair the failure, and confirm that the
  package is no longer held.

- Repeat with two or three packages being held.

[sunil]

- When the package 'needsrestart' is outdated and another package is held,
running repair unholds the package as well as runs setup() on the upgrades app
leading to 'needsrestart' package getting upgrade.

- When only failed diagnostic is for package holds. Running repair unholds the
packages but does not rung setup().

Helps: #2347

Signed-off-by: James Valleroy <jvalleroy@mailbox.org>
Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
This commit is contained in:
James Valleroy 2024-09-06 11:42:13 -04:00 committed by Sunil Mohan Adapa
parent 5d622d89cf
commit 71500ea9df
No known key found for this signature in database
GPG Key ID: 43EA1CFF0AA7C5F2
2 changed files with 35 additions and 1 deletions

View File

@ -46,6 +46,8 @@ BACKPORTS_REQUESTED_KEY = 'upgrades_backports_requested'
DIST_UPGRADE_ENABLED_KEY = 'upgrades_dist_upgrade_enabled'
PKG_HOLD_DIAG_CHECK_ID = 'upgrades-package-holds'
logger = logging.getLogger(__name__)
@ -166,6 +168,17 @@ class UpgradesApp(app_module.App):
results.append(_diagnose_held_packages())
return results
def repair(self, failed_checks: list) -> bool:
"""Handle repair for custom diagnostic."""
remaining_checks = []
for check in failed_checks:
if check.check_id == PKG_HOLD_DIAG_CHECK_ID:
privileged.release_held_packages()
else:
remaining_checks.append(check)
return super().repair(remaining_checks)
def setup_repositories(_):
"""Setup apt repositories for backports."""
@ -307,7 +320,7 @@ def test_dist_upgrade():
def _diagnose_held_packages():
"""Check if any packages have holds."""
check = DiagnosticCheck('upgrades-package-holds',
check = DiagnosticCheck(PKG_HOLD_DIAG_CHECK_ID,
gettext_noop('Check for package holds'),
Result.NOT_DONE)
if (package.is_package_manager_busy()

View File

@ -19,6 +19,8 @@ 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
logger = logging.getLogger(__name__)
SOURCES_LIST = '/etc/apt/sources.list'
BACKPORTS_SOURCES_LIST = '/etc/apt/sources.list.d/freedombox2.list'
@ -94,6 +96,25 @@ def _release_held_freedombox():
apt_unhold_freedombox()
@privileged
def release_held_packages():
"""Release any packages that are being held."""
if is_package_manager_busy():
logger.warning('Package manager is busy, skipping releasing holds.')
return
if service_is_running('freedombox-dist-upgrade'):
logger.warning('Distribution upgrade in progress, skipping releasing '
'holds.')
return
output = subprocess.check_output(['apt-mark', 'showhold']).decode().strip()
holds = output.split('\n')
logger.info('Releasing package holds: %s', holds)
subprocess.run(['apt-mark', 'unhold', *holds], stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL, check=True)
@privileged
def run():
"""Run unattended-upgrades."""