diff --git a/changelogs/fragments/10484-kernel_blacklist-handle-file-exceptions-gracefully.yml b/changelogs/fragments/10484-kernel_blacklist-handle-file-exceptions-gracefully.yml new file mode 100644 index 0000000000..1562d2671d --- /dev/null +++ b/changelogs/fragments/10484-kernel_blacklist-handle-file-exceptions-gracefully.yml @@ -0,0 +1,3 @@ +bugfixes: + - kernel_blacklist - handle file exceptions gracefully + (https://github.com/ansible-collections/community.general/issues/10482, https://github.com/ansible-collections/community.general/pull/10484). \ No newline at end of file diff --git a/plugins/modules/kernel_blacklist.py b/plugins/modules/kernel_blacklist.py index e1cf3fddb5..504f831954 100644 --- a/plugins/modules/kernel_blacklist.py +++ b/plugins/modules/kernel_blacklist.py @@ -69,15 +69,23 @@ class Blacklist(StateModuleHelper): def __init_module__(self): self.pattern = re.compile(r'^blacklist\s+{0}$'.format(re.escape(self.vars.name))) self.vars.filename = self.vars.blacklist_file - self.vars.set('file_exists', os.path.exists(self.vars.filename), output=False, change=True) - if not self.vars.file_exists: - with open(self.vars.filename, 'a'): - pass - self.vars.file_exists = True - self.vars.set('lines', [], change=True, diff=True) - else: - with open(self.vars.filename) as fd: - self.vars.set('lines', [x.rstrip() for x in fd.readlines()], change=True, diff=True) + try: + dirpath = os.path.dirname(self.vars.filename) + if not os.path.isdir(dirpath): + self.module.fail_json(msg="The directory {!r} does not exist.".format(dirpath)) + self.vars.set('file_exists', os.path.exists(self.vars.filename), output=False, change=True) + if not self.vars.file_exists: + with open(self.vars.filename, 'a'): + pass + self.vars.file_exists = True + self.vars.set('lines', [], change=True, diff=True) + else: + with open(self.vars.filename) as fd: + self.vars.set('lines', [x.rstrip() for x in fd.readlines()], change=True, diff=True) + except (OSError, IOError) as e: + self.module.fail_json(msg="Error accessing or creating blacklist file {!r}: {}".format(self.vars.filename, e)) + + self.vars.set('file_exists', True, output=False, change=True) self.vars.set('is_blacklisted', self._is_module_blocked(), change=True) def _is_module_blocked(self): @@ -104,8 +112,11 @@ class Blacklist(StateModuleHelper): def __quit_module__(self): if self.has_changed() and not self.module.check_mode: bkp = self.module.backup_local(self.vars.filename) - with open(self.vars.filename, "w") as fd: - fd.writelines(["{0}\n".format(x) for x in self.vars.lines]) + try: + with open(self.vars.filename, "w") as fd: + fd.writelines(["{0}\n".format(x) for x in self.vars.lines]) + except (OSError, IOError) as e: + self.module.fail_json(msg="Failed to write to blacklist file {!r}: {}".format(self.vars.filename, e)) self.module.add_cleanup_file(bkp) diff --git a/tests/integration/targets/kernel_blacklist/tasks/main.yml b/tests/integration/targets/kernel_blacklist/tasks/main.yml index aecc9b68d5..7ef0617f50 100644 --- a/tests/integration/targets/kernel_blacklist/tasks/main.yml +++ b/tests/integration/targets/kernel_blacklist/tasks/main.yml @@ -153,3 +153,52 @@ - cls_rsvp loop_control: loop_var: line_item + +- name: Resolve UID of nobody + ansible.builtin.command: id -u nobody + register: nobody_uid + ignore_errors: true + +- name: Create file owned by nobody + ansible.builtin.copy: + dest: /tmp/test-blacklist-perm.conf + content: "" + owner: "{{ nobody_uid.stdout | int }}" + mode: '0600' + become: true + when: nobody_uid.rc == 0 + +- name: Run kernel_blacklist with unreadable file + community.general.kernel_blacklist: + name: lp + state: present + blacklist_file: /tmp/test-blacklist-perm.conf + ignore_errors: true + register: result_perm + when: nobody_uid.rc == 0 + +- name: Assert module fails gracefully with permission error + ansible.builtin.assert: + that: + - result_perm.failed + - "result_perm.msg is match(\"Failed to write to blacklist file '/tmp/test-blacklist-perm.conf': \\\\[Errno 13\\\\] Permission denied: '.*'\")" + when: nobody_uid.rc == 0 + +- name: Ensure non-existing path + ansible.builtin.file: + path: /tmp/nonexistent-dir + state: absent + +- name: Run kernel_blacklist with nonexistent path + community.general.kernel_blacklist: + name: lp + state: present + blacklist_file: /tmp/nonexistent-dir/blacklist.conf + ignore_errors: true + register: result_missing + +- name: Assert module fails gracefully with missing directory error + ansible.builtin.assert: + that: + - result_missing.failed + - result_missing.msg == "The directory '/tmp/nonexistent-dir' does not exist."