apt - add fail_on_autoremove option to avoid unintended package removals (#70056)
* Ensure not to remove existing packages while installing apt packages. * Make all lines shorter than 160 characters * Allow removing packages only when upgrading. * Add integration tests
This commit is contained in:
parent
b05e00e99a
commit
4997063b4a
|
@ -0,0 +1,2 @@
|
||||||
|
bugfixes:
|
||||||
|
- apt - add ``fail_on_autoremove`` param to apt module to avoid unintended package removals (https://github.com/ansible/ansible/issues/63231)
|
|
@ -135,6 +135,14 @@ options:
|
||||||
type: bool
|
type: bool
|
||||||
default: 'no'
|
default: 'no'
|
||||||
version_added: "2.1"
|
version_added: "2.1"
|
||||||
|
fail_on_autoremove:
|
||||||
|
description:
|
||||||
|
- 'Corresponds to the C(--no-remove) option for C(apt).'
|
||||||
|
- 'If C(yes), it is ensured that no packages will be removed or the task will fail.'
|
||||||
|
- 'C(fail_on_autoremove) is only supported with state except C(absent)'
|
||||||
|
type: bool
|
||||||
|
default: 'no'
|
||||||
|
version_added: "2.11"
|
||||||
force_apt_get:
|
force_apt_get:
|
||||||
description:
|
description:
|
||||||
- Force usage of apt-get instead of aptitude
|
- Force usage of apt-get instead of aptitude
|
||||||
|
@ -194,6 +202,12 @@ EXAMPLES = '''
|
||||||
default_release: squeeze-backports
|
default_release: squeeze-backports
|
||||||
update_cache: yes
|
update_cache: yes
|
||||||
|
|
||||||
|
- name: Install zfsutils-linux with ensuring conflicted packages (e.g. zfs-fuse) will not be removed.
|
||||||
|
apt:
|
||||||
|
name: zfsutils-linux
|
||||||
|
state: latest
|
||||||
|
fail_on_autoremove: yes
|
||||||
|
|
||||||
- name: Install latest version of "openjdk-6-jdk" ignoring "install-recommends"
|
- name: Install latest version of "openjdk-6-jdk" ignoring "install-recommends"
|
||||||
apt:
|
apt:
|
||||||
name: openjdk-6-jdk
|
name: openjdk-6-jdk
|
||||||
|
@ -613,7 +627,7 @@ def mark_installed_manually(m, packages):
|
||||||
def install(m, pkgspec, cache, upgrade=False, default_release=None,
|
def install(m, pkgspec, cache, upgrade=False, default_release=None,
|
||||||
install_recommends=None, force=False,
|
install_recommends=None, force=False,
|
||||||
dpkg_options=expand_dpkg_options(DPKG_OPTIONS),
|
dpkg_options=expand_dpkg_options(DPKG_OPTIONS),
|
||||||
build_dep=False, fixed=False, autoremove=False, only_upgrade=False,
|
build_dep=False, fixed=False, autoremove=False, fail_on_autoremove=False, only_upgrade=False,
|
||||||
allow_unauthenticated=False):
|
allow_unauthenticated=False):
|
||||||
pkg_list = []
|
pkg_list = []
|
||||||
packages = ""
|
packages = ""
|
||||||
|
@ -656,6 +670,11 @@ def install(m, pkgspec, cache, upgrade=False, default_release=None,
|
||||||
else:
|
else:
|
||||||
autoremove = ''
|
autoremove = ''
|
||||||
|
|
||||||
|
if fail_on_autoremove:
|
||||||
|
fail_on_autoremove = '--no-remove'
|
||||||
|
else:
|
||||||
|
fail_on_autoremove = ''
|
||||||
|
|
||||||
if only_upgrade:
|
if only_upgrade:
|
||||||
only_upgrade = '--only-upgrade'
|
only_upgrade = '--only-upgrade'
|
||||||
else:
|
else:
|
||||||
|
@ -667,9 +686,10 @@ def install(m, pkgspec, cache, upgrade=False, default_release=None,
|
||||||
fixed = ''
|
fixed = ''
|
||||||
|
|
||||||
if build_dep:
|
if build_dep:
|
||||||
cmd = "%s -y %s %s %s %s %s build-dep %s" % (APT_GET_CMD, dpkg_options, only_upgrade, fixed, force_yes, check_arg, packages)
|
cmd = "%s -y %s %s %s %s %s %s build-dep %s" % (APT_GET_CMD, dpkg_options, only_upgrade, fixed, force_yes, fail_on_autoremove, check_arg, packages)
|
||||||
else:
|
else:
|
||||||
cmd = "%s -y %s %s %s %s %s %s install %s" % (APT_GET_CMD, dpkg_options, only_upgrade, fixed, force_yes, autoremove, check_arg, packages)
|
cmd = "%s -y %s %s %s %s %s %s %s install %s" % \
|
||||||
|
(APT_GET_CMD, dpkg_options, only_upgrade, fixed, force_yes, autoremove, fail_on_autoremove, check_arg, packages)
|
||||||
|
|
||||||
if default_release:
|
if default_release:
|
||||||
cmd += " -t '%s'" % (default_release,)
|
cmd += " -t '%s'" % (default_release,)
|
||||||
|
@ -719,7 +739,7 @@ def get_field_of_deb(m, deb_file, field="Version"):
|
||||||
return to_native(stdout).strip('\n')
|
return to_native(stdout).strip('\n')
|
||||||
|
|
||||||
|
|
||||||
def install_deb(m, debs, cache, force, install_recommends, allow_unauthenticated, dpkg_options):
|
def install_deb(m, debs, cache, force, fail_on_autoremove, install_recommends, allow_unauthenticated, dpkg_options):
|
||||||
changed = False
|
changed = False
|
||||||
deps_to_install = []
|
deps_to_install = []
|
||||||
pkgs_to_install = []
|
pkgs_to_install = []
|
||||||
|
@ -761,6 +781,7 @@ def install_deb(m, debs, cache, force, install_recommends, allow_unauthenticated
|
||||||
if deps_to_install:
|
if deps_to_install:
|
||||||
(success, retvals) = install(m=m, pkgspec=deps_to_install, cache=cache,
|
(success, retvals) = install(m=m, pkgspec=deps_to_install, cache=cache,
|
||||||
install_recommends=install_recommends,
|
install_recommends=install_recommends,
|
||||||
|
fail_on_autoremove=fail_on_autoremove,
|
||||||
allow_unauthenticated=allow_unauthenticated,
|
allow_unauthenticated=allow_unauthenticated,
|
||||||
dpkg_options=expand_dpkg_options(dpkg_options))
|
dpkg_options=expand_dpkg_options(dpkg_options))
|
||||||
if not success:
|
if not success:
|
||||||
|
@ -890,7 +911,7 @@ def cleanup(m, purge=False, force=False, operation=None,
|
||||||
|
|
||||||
def upgrade(m, mode="yes", force=False, default_release=None,
|
def upgrade(m, mode="yes", force=False, default_release=None,
|
||||||
use_apt_get=False,
|
use_apt_get=False,
|
||||||
dpkg_options=expand_dpkg_options(DPKG_OPTIONS), autoremove=False,
|
dpkg_options=expand_dpkg_options(DPKG_OPTIONS), autoremove=False, fail_on_autoremove=False,
|
||||||
allow_unauthenticated=False,
|
allow_unauthenticated=False,
|
||||||
):
|
):
|
||||||
|
|
||||||
|
@ -932,6 +953,11 @@ def upgrade(m, mode="yes", force=False, default_release=None,
|
||||||
else:
|
else:
|
||||||
force_yes = ''
|
force_yes = ''
|
||||||
|
|
||||||
|
if fail_on_autoremove:
|
||||||
|
fail_on_autoremove = '--no-remove'
|
||||||
|
else:
|
||||||
|
fail_on_autoremove = ''
|
||||||
|
|
||||||
allow_unauthenticated = '--allow-unauthenticated' if allow_unauthenticated else ''
|
allow_unauthenticated = '--allow-unauthenticated' if allow_unauthenticated else ''
|
||||||
|
|
||||||
if apt_cmd is None:
|
if apt_cmd is None:
|
||||||
|
@ -942,8 +968,7 @@ def upgrade(m, mode="yes", force=False, default_release=None,
|
||||||
"to have APTITUDE in path or use 'force_apt_get=True'")
|
"to have APTITUDE in path or use 'force_apt_get=True'")
|
||||||
apt_cmd_path = m.get_bin_path(apt_cmd, required=True)
|
apt_cmd_path = m.get_bin_path(apt_cmd, required=True)
|
||||||
|
|
||||||
cmd = '%s -y %s %s %s %s %s' % (apt_cmd_path, dpkg_options, force_yes, allow_unauthenticated,
|
cmd = '%s -y %s %s %s %s %s %s' % (apt_cmd_path, dpkg_options, force_yes, fail_on_autoremove, allow_unauthenticated, check_arg, upgrade_command)
|
||||||
check_arg, upgrade_command)
|
|
||||||
|
|
||||||
if default_release:
|
if default_release:
|
||||||
cmd += " -t '%s'" % (default_release,)
|
cmd += " -t '%s'" % (default_release,)
|
||||||
|
@ -1029,6 +1054,7 @@ def main():
|
||||||
dpkg_options=dict(type='str', default=DPKG_OPTIONS),
|
dpkg_options=dict(type='str', default=DPKG_OPTIONS),
|
||||||
autoremove=dict(type='bool', default=False),
|
autoremove=dict(type='bool', default=False),
|
||||||
autoclean=dict(type='bool', default=False),
|
autoclean=dict(type='bool', default=False),
|
||||||
|
fail_on_autoremove=dict(type='bool', default=False),
|
||||||
policy_rc_d=dict(type='int', default=None),
|
policy_rc_d=dict(type='int', default=None),
|
||||||
only_upgrade=dict(type='bool', default=False),
|
only_upgrade=dict(type='bool', default=False),
|
||||||
force_apt_get=dict(type='bool', default=False),
|
force_apt_get=dict(type='bool', default=False),
|
||||||
|
@ -1084,6 +1110,7 @@ def main():
|
||||||
allow_unauthenticated = p['allow_unauthenticated']
|
allow_unauthenticated = p['allow_unauthenticated']
|
||||||
dpkg_options = expand_dpkg_options(p['dpkg_options'])
|
dpkg_options = expand_dpkg_options(p['dpkg_options'])
|
||||||
autoremove = p['autoremove']
|
autoremove = p['autoremove']
|
||||||
|
fail_on_autoremove = p['fail_on_autoremove']
|
||||||
autoclean = p['autoclean']
|
autoclean = p['autoclean']
|
||||||
|
|
||||||
# Get the cache object
|
# Get the cache object
|
||||||
|
@ -1145,7 +1172,7 @@ def main():
|
||||||
force_yes = p['force']
|
force_yes = p['force']
|
||||||
|
|
||||||
if p['upgrade']:
|
if p['upgrade']:
|
||||||
upgrade(module, p['upgrade'], force_yes, p['default_release'], use_apt_get, dpkg_options, autoremove, allow_unauthenticated)
|
upgrade(module, p['upgrade'], force_yes, p['default_release'], use_apt_get, dpkg_options, autoremove, fail_on_autoremove, allow_unauthenticated)
|
||||||
|
|
||||||
if p['deb']:
|
if p['deb']:
|
||||||
if p['state'] != 'present':
|
if p['state'] != 'present':
|
||||||
|
@ -1155,7 +1182,7 @@ def main():
|
||||||
install_deb(module, p['deb'], cache,
|
install_deb(module, p['deb'], cache,
|
||||||
install_recommends=install_recommends,
|
install_recommends=install_recommends,
|
||||||
allow_unauthenticated=allow_unauthenticated,
|
allow_unauthenticated=allow_unauthenticated,
|
||||||
force=force_yes, dpkg_options=p['dpkg_options'])
|
force=force_yes, fail_on_autoremove=fail_on_autoremove, dpkg_options=p['dpkg_options'])
|
||||||
|
|
||||||
unfiltered_packages = p['package'] or ()
|
unfiltered_packages = p['package'] or ()
|
||||||
packages = [package.strip() for package in unfiltered_packages if package != '*']
|
packages = [package.strip() for package in unfiltered_packages if package != '*']
|
||||||
|
@ -1165,7 +1192,7 @@ def main():
|
||||||
if latest and all_installed:
|
if latest and all_installed:
|
||||||
if packages:
|
if packages:
|
||||||
module.fail_json(msg='unable to install additional packages when upgrading all installed packages')
|
module.fail_json(msg='unable to install additional packages when upgrading all installed packages')
|
||||||
upgrade(module, 'yes', force_yes, p['default_release'], use_apt_get, dpkg_options, autoremove, allow_unauthenticated)
|
upgrade(module, 'yes', force_yes, p['default_release'], use_apt_get, dpkg_options, autoremove, fail_on_autoremove, allow_unauthenticated)
|
||||||
|
|
||||||
if packages:
|
if packages:
|
||||||
for package in packages:
|
for package in packages:
|
||||||
|
@ -1203,6 +1230,7 @@ def main():
|
||||||
build_dep=state_builddep,
|
build_dep=state_builddep,
|
||||||
fixed=state_fixed,
|
fixed=state_fixed,
|
||||||
autoremove=autoremove,
|
autoremove=autoremove,
|
||||||
|
fail_on_autoremove=fail_on_autoremove,
|
||||||
only_upgrade=p['only_upgrade'],
|
only_upgrade=p['only_upgrade'],
|
||||||
allow_unauthenticated=allow_unauthenticated
|
allow_unauthenticated=allow_unauthenticated
|
||||||
)
|
)
|
||||||
|
|
|
@ -179,6 +179,44 @@
|
||||||
- name: uninstall hello with apt
|
- name: uninstall hello with apt
|
||||||
apt: pkg=hello state=absent purge=yes
|
apt: pkg=hello state=absent purge=yes
|
||||||
|
|
||||||
|
# INSTALL WITHOUT REMOVALS
|
||||||
|
- name: Install hello, that conflicts with hello-traditional
|
||||||
|
apt:
|
||||||
|
pkg: hello
|
||||||
|
state: present
|
||||||
|
update_cache: no
|
||||||
|
|
||||||
|
- name: check hello
|
||||||
|
shell: dpkg-query -l hello
|
||||||
|
register: dpkg_result
|
||||||
|
|
||||||
|
- name: verify installation of hello
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- "apt_result.changed"
|
||||||
|
- "dpkg_result.rc == 0"
|
||||||
|
|
||||||
|
- name: Try installing hello-traditional, that conflicts with hello
|
||||||
|
apt:
|
||||||
|
pkg: hello-traditional
|
||||||
|
state: present
|
||||||
|
fail_on_autoremove: yes
|
||||||
|
ignore_errors: yes
|
||||||
|
register: apt_result
|
||||||
|
|
||||||
|
- name: verify failure of installing hello-traditional, because it is required to remove hello to install.
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- apt_result is failed
|
||||||
|
- '"Packages need to be removed but remove is disabled." in apt_result.msg'
|
||||||
|
|
||||||
|
- name: uninstall hello with apt
|
||||||
|
apt:
|
||||||
|
pkg: hello
|
||||||
|
state: absent
|
||||||
|
purge: yes
|
||||||
|
update_cache: no
|
||||||
|
|
||||||
- name: install deb file
|
- name: install deb file
|
||||||
apt: deb="/var/cache/apt/archives/hello_{{ hello_version.stdout }}_{{ hello_architecture.stdout }}.deb"
|
apt: deb="/var/cache/apt/archives/hello_{{ hello_version.stdout }}_{{ hello_architecture.stdout }}.deb"
|
||||||
register: apt_initial
|
register: apt_initial
|
||||||
|
|
Loading…
Reference in a new issue