diff --git a/actions/packages b/actions/packages index 4f65d6882..b0072cbf3 100755 --- a/actions/packages +++ b/actions/packages @@ -172,35 +172,49 @@ def subcommand_filter_conffile_packages(arguments): apt_pkg.init() # Read configuration that will be used later. packages = set(arguments.packages) - status_hashes = _get_conffile_hashes_from_status_file(packages) + status_hashes, current_versions = _get_conffile_hashes_from_status_file( + packages) mismatched_hashes = _filter_matching_package_hashes(status_hashes) downloaded_files = _download_packages(packages) - new_package_hashes = _get_conffile_hashes_from_downloaded_files( + new_package_hashes, new_versions = _get_conffile_hashes_from_downloaded_files( packages, downloaded_files, status_hashes, mismatched_hashes) - conffile_packages = [ - package for package in packages if _is_conffile_prompt_pending( + packages_info = {} + for package in packages: + modified_conffiles = _get_modified_conffiles( status_hashes[package], mismatched_hashes[package], new_package_hashes[package]) - ] - print(json.dumps(conffile_packages)) + if not modified_conffiles: + continue + + package_info = { + 'current_version': current_versions[package], + 'new_version': new_versions[package], + 'modified_conffiles': modified_conffiles + } + packages_info[package] = package_info + + print(json.dumps(packages_info)) -def _is_conffile_prompt_pending(status_hashes, mismatched_hashes, - new_package_hashes): - """Return whether upgrading the package requires a conffile prompt.""" +def _get_modified_conffiles(status_hashes, mismatched_hashes, + new_package_hashes): + """Return list of conffiles that will cause prompts for a package.""" + modified_conffiles = [] for conffile, hash_value in mismatched_hashes.items(): if conffile not in new_package_hashes: # Modified configuration file not present new package continue - if status_hashes[conffile] != new_package_hashes[conffile]: + if status_hashes[conffile] == new_package_hashes[conffile]: # Configuration file is same in both versions of package. Conffile # prompt is not triggered even if the file is modified on disk. - return True + continue + + modified_conffiles.append(conffile) for conffile, hash_value in new_package_hashes.items(): if conffile in status_hashes: @@ -218,9 +232,9 @@ def _is_conffile_prompt_pending(status_hashes, mismatched_hashes, # # If existing file is a directory, unattended-upgrades allows it, # we still treat it as a conffile prompt. This should be okay. - return True + modified_conffiles.append(conffile) - return False + return modified_conffiles def _get_conffile_hashes_from_status_file(packages): @@ -231,6 +245,7 @@ def _get_conffile_hashes_from_status_file(packages): """ package_hashes = defaultdict(dict) + package_versions = defaultdict(lambda: None) status_file = apt_pkg.config.find('Dir::State::status') with apt_pkg.TagFile(status_file) as tag_file: @@ -241,8 +256,9 @@ def _get_conffile_hashes_from_status_file(packages): hashes = _parse_conffiles_value(section.get('Conffiles', '')) package_hashes[package] = hashes + package_versions[package] = section.get('Version') - return package_hashes + return package_hashes, package_versions def _parse_conffiles_value(value): @@ -335,16 +351,19 @@ def _get_conffile_hashes_from_downloaded_files( packages, downloaded_files, status_hashes, mismatched_hashes): """Retrieve the conffile hashes from downloaded .deb files.""" new_hashes = defaultdict(dict) + new_versions = defaultdict(lambda: None) for downloaded_file in downloaded_files: try: - package_name, hashes = _get_conffile_hashes_from_downloaded_file( + package_name, hashes, new_version = \ + _get_conffile_hashes_from_downloaded_file( packages, downloaded_file, status_hashes, mismatched_hashes) except (LookupError, apt_pkg.Error, ValueError): continue new_hashes[package_name] = hashes + new_versions[package_name] = new_version - return new_hashes + return new_hashes, new_versions def _get_conffile_hashes_from_downloaded_file( @@ -359,6 +378,8 @@ def _get_conffile_hashes_from_downloaded_file( if package_name not in packages: raise ValueError + new_version = section['Version'] + conffiles = deb_file.control.extractdata('conffiles') conffiles = conffiles.decode().strip().split() @@ -376,7 +397,7 @@ def _get_conffile_hashes_from_downloaded_file( md5sum = apt_pkg.md5sum(conffile_data) hashes[conffile] = md5sum - return package_name, hashes + return package_name, hashes, new_version def main(): diff --git a/plinth/modules/bind/__init__.py b/plinth/modules/bind/__init__.py index b29b76839..b5425701e 100644 --- a/plinth/modules/bind/__init__.py +++ b/plinth/modules/bind/__init__.py @@ -104,7 +104,7 @@ def setup(helper, old_version=None): helper.call('post', actions.superuser_run, 'bind', ['setup']) -def force_upgrade(helper): +def force_upgrade(helper, _packages): """Force upgrade the managed packages to resolve conffile prompt.""" helper.install(managed_packages, force_configuration='old') diff --git a/plinth/package.py b/plinth/package.py index a50a87092..a278a57dd 100644 --- a/plinth/package.py +++ b/plinth/package.py @@ -186,8 +186,13 @@ def refresh_package_lists(): transaction.refresh_package_lists() -def filter_conffile_prompts(packages): - """Return a filtered list of packages that require conffile prompts.""" +def filter_conffile_prompt_packages(packages): + """Return a filtered info on packages that require conffile prompts. + + Information for each package includes: current_version, new_version and + list of modified_conffiles. + + """ response = actions.superuser_run( 'packages', ['filter-conffile-packages', '--packages'] + list(packages)) diff --git a/plinth/setup.py b/plinth/setup.py index 5646e59d8..e1a2d2b37 100644 --- a/plinth/setup.py +++ b/plinth/setup.py @@ -501,17 +501,13 @@ class ForceUpgrader(): apps = self._get_list_of_apps_to_force_upgrade() logger.info('Apps needing conffile upgrades: %s', - ', '.join([app.name for app in apps])) + ', '.join([str(app.name) for app in apps])) need_retry = False - for app in apps: + for app, packages in apps.items(): try: logger.info('Force upgrading app: %s', app.name) - # XXX: Try to send information about package versions being - # upgraded and the configuration files that have lead to the - # force upgrade. This can be picked up from the 'packages - # filter-conffile-packages' that does all the relevant work. - app.force_upgrade(app.setup_helper) + app.force_upgrade(app.setup_helper, packages) logger.info('Successfully force upgraded app: %s', app.name) except Exception as exception: logger.exception('Error running force upgrade: %s', exception) @@ -525,7 +521,7 @@ class ForceUpgrader(): """Return a list of app modules on which to run force upgrade.""" packages = self._get_list_of_upgradable_packages() if not packages: # No packages to upgrade - return [] + return {} package_names = [package.name for package in packages] logger.info('Packages available for upgrade: %s', @@ -534,15 +530,17 @@ class ForceUpgrader(): managed_packages, package_apps_map = self._filter_managed_packages( package_names) if not managed_packages: - return [] + return {} - conffile_packages = package.filter_conffile_prompts(managed_packages) + conffile_packages = package.filter_conffile_prompt_packages( + managed_packages) logger.info('Packages that need conffile upgrades: %s', conffile_packages) - apps = set() + apps = defaultdict(list) for package_name in conffile_packages: - apps.update(package_apps_map[package_name]) + for app in package_apps_map[package_name]: + apps[app].append(conffile_packages[package_name]) return apps