summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortchernomax <maxime.deroucy@gmail.com>2018-11-19 15:45:48 +0100
committerJohn R Barker <john@johnrbarker.com>2018-11-19 14:45:48 +0000
commit943edb33196ca4713e9e8037721c7757740bd8e2 (patch)
treea2b5a04a07c9d7528fe5b5eef021fe3a85049670
parent50808ffa8fa8a2c6561035cd9c755309ffe4ea0e (diff)
downloadansible-943edb33196ca4713e9e8037721c7757740bd8e2.tar.gz
apt: add policy_rc_d option (#47191)
-rw-r--r--lib/ansible/modules/packaging/os/apt.py110
-rw-r--r--test/integration/targets/apt/tasks/apt.yml59
2 files changed, 164 insertions, 5 deletions
diff --git a/lib/ansible/modules/packaging/os/apt.py b/lib/ansible/modules/packaging/os/apt.py
index 8f05ba2a3e..42332077c6 100644
--- a/lib/ansible/modules/packaging/os/apt.py
+++ b/lib/ansible/modules/packaging/os/apt.py
@@ -109,6 +109,15 @@ options:
type: bool
default: 'no'
version_added: "2.4"
+ policy_rc_d:
+ description:
+ - Force the exit code of /usr/sbin/policy-rc.d.
+ - For example, if I(policy_rc_d=101) the installed package will not trigger a service start.
+ - If /usr/sbin/policy-rc.d already exist, it is backed up and restored after the package installation.
+ - If C(null), the /usr/sbin/policy-rc.d isn't created/changed.
+ type: int
+ default: null
+ version_added: "2.8"
only_upgrade:
description:
- Only upgrade a package if it is already installed.
@@ -261,8 +270,10 @@ import datetime
import fnmatch
import itertools
import os
+import shutil
import re
import sys
+import tempfile
import time
from ansible.module_utils.basic import AnsibleModule
@@ -309,6 +320,83 @@ else:
PYTHON_APT = 'python3-apt'
+class PolicyRcD(object):
+ """
+ This class is a context manager for the /usr/sbin/policy-rc.d file.
+ It allow the user to prevent dpkg to start the corresponding service when installing
+ a package.
+ https://people.debian.org/~hmh/invokerc.d-policyrc.d-specification.txt
+ """
+
+ def __init__(self, module):
+ # we need the module for later use (eg. fail_json)
+ self.m = module
+
+ # if policy_rc_d is null then we don't need to modify policy-rc.d
+ if self.m.params['policy_rc_d'] is None:
+ return
+
+ # if the /usr/sbin/policy-rc.d already exist
+ # we will back it up during package installation
+ # then restore it
+ if os.path.exists('/usr/sbin/policy-rc.d'):
+ self.backup_dir = tempfile.mkdtemp(prefix="ansible")
+ else:
+ self.backup_dir = None
+
+ def __enter__(self):
+ """
+ This method will be call when we enter the context, before we call `apt-get …`
+ """
+
+ # if policy_rc_d is null then we don't need to modify policy-rc.d
+ if self.m.params['policy_rc_d'] is None:
+ return
+
+ # if the /usr/sbin/policy-rc.d already exist we back it up
+ if self.backup_dir:
+ try:
+ shutil.move('/usr/sbin/policy-rc.d', self.backup_dir)
+ except:
+ self.m.fail_json(msg="Fail to move /usr/sbin/policy-rc.d to %s" % self.backup_dir)
+
+ # we write /usr/sbin/policy-rc.d so it always exit with code policy_rc_d
+ try:
+ with open('/usr/sbin/policy-rc.d', 'w') as policy_rc_d:
+ policy_rc_d.write('#!/bin/sh\nexit %d\n' % self.m.params['policy_rc_d'])
+
+ os.chmod('/usr/sbin/policy-rc.d', 0o0755)
+ except:
+ self.m.fail_json(msg="Failed to create or chmod /usr/sbin/policy-rc.d")
+
+ def __exit__(self, type, value, traceback):
+ """
+ This method will be call when we enter the context, before we call `apt-get …`
+ """
+
+ # if policy_rc_d is null then we don't need to modify policy-rc.d
+ if self.m.params['policy_rc_d'] is None:
+ return
+
+ if self.backup_dir:
+ # if /usr/sbin/policy-rc.d already exists before the call to __enter__
+ # we restore it (from the backup done in __enter__)
+ try:
+ shutil.move(os.path.join(self.backup_dir, 'policy-rc.d'),
+ '/usr/sbin/policy-rc.d')
+ os.rmdir(self.tmpdir_name)
+ except:
+ self.m.fail_json(msg="Fail to move back %s to /usr/sbin/policy-rc.d"
+ % os.path.join(self.backup_dir, 'policy-rc.d'))
+ else:
+ # if they wheren't any /usr/sbin/policy-rc.d file before the call to __enter__
+ # we just remove the file
+ try:
+ os.remove('/usr/sbin/policy-rc.d')
+ except:
+ self.m.fail_json(msg="Fail to remove /usr/sbin/policy-rc.d (after package manipulation)")
+
+
def package_split(pkgspec):
parts = pkgspec.split('=', 1)
version = None
@@ -585,7 +673,9 @@ def install(m, pkgspec, cache, upgrade=False, default_release=None,
if allow_unauthenticated:
cmd += " --allow-unauthenticated"
- rc, out, err = m.run_command(cmd)
+ with PolicyRcD(m):
+ rc, out, err = m.run_command(cmd)
+
if m._diff:
diff = parse_diff(out)
else:
@@ -674,7 +764,10 @@ def install_deb(m, debs, cache, force, install_recommends, allow_unauthenticated
options += " --force-all"
cmd = "dpkg %s -i %s" % (options, " ".join(pkgs_to_install))
- rc, out, err = m.run_command(cmd)
+
+ with PolicyRcD(m):
+ rc, out, err = m.run_command(cmd)
+
if "stdout" in retvals:
stdout = retvals["stdout"] + out
else:
@@ -734,7 +827,9 @@ def remove(m, pkgspec, cache, purge=False, force=False,
cmd = "%s -q -y %s %s %s %s %s remove %s" % (APT_GET_CMD, dpkg_options, purge, force_yes, autoremove, check_arg, packages)
- rc, out, err = m.run_command(cmd)
+ with PolicyRcD(m):
+ rc, out, err = m.run_command(cmd)
+
if m._diff:
diff = parse_diff(out)
else:
@@ -767,7 +862,9 @@ def cleanup(m, purge=False, force=False, operation=None,
cmd = "%s -y %s %s %s %s %s" % (APT_GET_CMD, dpkg_options, purge, force_yes, operation, check_arg)
- rc, out, err = m.run_command(cmd)
+ with PolicyRcD(m):
+ rc, out, err = m.run_command(cmd)
+
if m._diff:
diff = parse_diff(out)
else:
@@ -840,7 +937,9 @@ def upgrade(m, mode="yes", force=False, default_release=None,
if default_release:
cmd += " -t '%s'" % (default_release,)
- rc, out, err = m.run_command(cmd, prompt_regex=prompt_regex)
+ with PolicyRcD(m):
+ rc, out, err = m.run_command(cmd, prompt_regex=prompt_regex)
+
if m._diff:
diff = parse_diff(out)
else:
@@ -917,6 +1016,7 @@ def main():
dpkg_options=dict(type='str', default=DPKG_OPTIONS),
autoremove=dict(type='bool', default=False),
autoclean=dict(type='bool', default=False),
+ policy_rc_d=dict(type='int', default=None),
only_upgrade=dict(type='bool', default=False),
force_apt_get=dict(type='bool', default=False),
allow_unauthenticated=dict(type='bool', default=False, aliases=['allow-unauthenticated']),
diff --git a/test/integration/targets/apt/tasks/apt.yml b/test/integration/targets/apt/tasks/apt.yml
index bd8f225a54..b0131682dd 100644
--- a/test/integration/targets/apt/tasks/apt.yml
+++ b/test/integration/targets/apt/tasks/apt.yml
@@ -258,3 +258,62 @@
assert:
that:
- apt_result is not changed
+
+# check policy_rc_d parameter
+
+- name: Install unscd but forbid service start
+ apt:
+ name: unscd
+ policy_rc_d: 101
+
+- name: Stop unscd service
+ service:
+ name: unscd
+ state: stopped
+ register: service_unscd_stop
+
+- name: unscd service shouldn't have been stopped by previous task
+ assert:
+ that: service_unscd_stop is not changed
+
+- name: Uninstall unscd
+ apt:
+ name: unscd
+ policy_rc_d: 101
+
+- name: Create incorrect /usr/sbin/policy-rc.d
+ copy:
+ dest: /usr/sbin/policy-rc.d
+ content: apt integration test
+ mode: 0755
+
+- name: Install unscd but forbid service start
+ apt:
+ name: unscd
+ policy_rc_d: 101
+
+- name: Stop unscd service
+ service:
+ name: unscd
+ state: stopped
+ register: service_unscd_stop
+
+- name: unscd service shouldn't have been stopped by previous task
+ assert:
+ that: service_unscd_stop is not changed
+
+- name: Create incorrect /usr/sbin/policy-rc.d
+ copy:
+ dest: /usr/sbin/policy-rc.d
+ content: apt integration test
+ mode: 0755
+ register: policy_rc_d
+
+- name: Check if /usr/sbin/policy-rc.d was correctly backed-up during unscd install
+ assert:
+ that: policy_rc_d is not changed
+
+- name: Delete /usr/sbin/policy-rc.d
+ file:
+ path: /usr/sbin/policy-rc.d
+ state: absent