setup: Add option to handle configuration prompts during install

This is optional and does not affect normal installations. However, when
performing configuration migration in FreedomBox (due to unattended-upgrades
refusing it), it is useful as a part of strategy to read configuration, force
install new configuration files and apply configuration again. This option can
be used on such cases.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
Sunil Mohan Adapa 2019-02-11 14:02:06 -08:00 committed by James Valleroy
parent 0f5a52f8c4
commit 920d083301
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
3 changed files with 44 additions and 13 deletions

View File

@ -41,6 +41,9 @@ def parse_arguments():
subparser.add_argument(
'--skip-recommends', action='store_true',
help='whether to skip installing recommended packages')
subparser.add_argument(
'--force-configuration', choices=['new', 'old'],
help='force old/new configuration files during install')
subparser.add_argument(
'module', help='name of module for which package is being installed')
subparser.add_argument('packages', nargs='+',
@ -85,11 +88,19 @@ def subcommand_install(arguments):
print('Access check failed:', exception, file=sys.stderr)
sys.exit(99)
extra_arguments = []
if arguments.skip_recommends:
_run_apt_command(
['install', '--no-install-recommends'] + arguments.packages)
else:
_run_apt_command(['install'] + arguments.packages)
extra_arguments.append('--no-install-recommends')
if arguments.force_configuration == 'old':
extra_arguments += [
'-o', 'Dpkg::Options::=--force-confdef', '-o',
'Dpkg::Options::=--force-confold'
]
elif arguments.force_configuration == 'new':
extra_arguments += ['-o', 'Dpkg::Options::=--force-confnew']
_run_apt_command(['install'] + extra_arguments + arguments.packages)
def _assert_managed_packages(module, packages):

View File

@ -68,23 +68,42 @@ class Transaction(object):
self.percentage = 0
self.stderr = None
def install(self, skip_recommends=False):
def install(self, skip_recommends=False, force_configuration=None):
"""Run an apt-get transaction to install given packages.
FreedomBox Service (Plinth) needs to be running as root when calling
this. Currently, this is meant to be only during first time setup when
--setup is argument is passed.
If force_configuration is set to 'new', dpkg options will be enabled to
make it force overwrite (without prompts) new configuration in place of
old configuration (with a backup). This is useful when writing
migration scripts in FreedomBox to handle the upgrades when
unattended-upgrades refuse to upgrade a package due to configuration
prompts.
If force_configuration is set to 'old', dpkg options will be enabled to
make it keep the old configuration (without prompts). This is useful
when the Debian packages introduce new configuration with just
cosmetics (such as updates to comments) and keeping the old
configuration has same semantics.
If force_configuration is None, no special options are passed to
apt/dpkg for configuration file behavior.
"""
try:
self._run_apt_command(['update'])
extra_arguments = []
if skip_recommends:
self._run_apt_command(
['install', '--skip-recommends', self.module_name] +
self.package_names)
else:
self._run_apt_command(['install', self.module_name] +
self.package_names)
extra_arguments.append('--skip-recommends')
if force_configuration is not None:
extra_arguments.append(
'--force-configuration={}'.format(force_configuration))
self._run_apt_command(['install'] + extra_arguments +
[self.module_name] + self.package_names)
except subprocess.CalledProcessError as exception:
logger.exception('Error installing package: %s', exception)
raise

View File

@ -101,7 +101,8 @@ class Helper(object):
self.is_finished = True
self.current_operation = None
def install(self, package_names, skip_recommends=False):
def install(self, package_names, skip_recommends=False,
force_configuration=None):
"""Install a set of packages marking progress."""
if self.allow_install is False:
# Raise error if packages are not already installed.
@ -120,7 +121,7 @@ class Helper(object):
'step': 'install',
'transaction': transaction,
}
transaction.install(skip_recommends)
transaction.install(skip_recommends, force_configuration)
def call(self, step, method, *args, **kwargs):
"""Call an arbitrary method during setup and note down its stage."""