From dc7aba3bca6b997d2efe2c360dd0c7f2d3356640 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sun, 10 Sep 2023 09:00:20 +0200 Subject: [PATCH] [PR #7200/8fa667ee backport][stable-6] CmdRunner bugfix (#7227) CmdRunner bugfix (#7200) * cmd_runner module utils: fix bug when passing absolute path not in standard search paths * improved tests * changed /usr/bin/echo to /bin/echo for the sake of alpine * fixed error messaging for last testcase * add condition to test cases, and remove macos from troubling ones * fix templating * fix templating * exclude centos 6 from testcases copying echo to tmp dir * try different way of specifying version * trying trick for old jinjas * use os.path.isabs() to determine if path is absolute * add changelog frag * Update plugins/module_utils/cmd_runner.py Co-authored-by: Felix Fontein * Update changelogs/fragments/7200-cmd-runner-abs-path.yml Co-authored-by: Felix Fontein --------- Co-authored-by: Felix Fontein (cherry picked from commit 8fa667eeb7a4e3ceca30794846b27aa289cc0028) Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> --- .../fragments/7200-cmd-runner-abs-path.yml | 2 + plugins/module_utils/cmd_runner.py | 8 +- .../targets/cmd_runner/library/cmd_echo.py | 4 +- .../targets/cmd_runner/meta/main.yml | 7 + .../targets/cmd_runner/tasks/main.yml | 1 + .../cmd_runner/tasks/test_cmd_echo.yml | 31 +++-- .../targets/cmd_runner/vars/main.yml | 122 ++++++++++++++++++ 7 files changed, 162 insertions(+), 13 deletions(-) create mode 100644 changelogs/fragments/7200-cmd-runner-abs-path.yml create mode 100644 tests/integration/targets/cmd_runner/meta/main.yml diff --git a/changelogs/fragments/7200-cmd-runner-abs-path.yml b/changelogs/fragments/7200-cmd-runner-abs-path.yml new file mode 100644 index 0000000000..78c990c139 --- /dev/null +++ b/changelogs/fragments/7200-cmd-runner-abs-path.yml @@ -0,0 +1,2 @@ +bugfixes: + - CmdRunner module utils - does not attempt to resolve path if executable is a relative or absolute path (https://github.com/ansible-collections/community.general/pull/7200). diff --git a/plugins/module_utils/cmd_runner.py b/plugins/module_utils/cmd_runner.py index ddeac941c0..1f0ec6bfb9 100644 --- a/plugins/module_utils/cmd_runner.py +++ b/plugins/module_utils/cmd_runner.py @@ -6,6 +6,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type +import os from functools import wraps from ansible.module_utils.common.collections import is_sequence @@ -199,12 +200,17 @@ class CmdRunner(object): environ_update = {} self.environ_update = environ_update - self.command[0] = module.get_bin_path(self.command[0], opt_dirs=path_prefix, required=True) + _cmd = self.command[0] + self.command[0] = _cmd if (os.path.isabs(_cmd) or '/' in _cmd) else module.get_bin_path(_cmd, opt_dirs=path_prefix, required=True) for mod_param_name, spec in iteritems(module.argument_spec): if mod_param_name not in self.arg_formats: self.arg_formats[mod_param_name] = _Format.as_default_type(spec.get('type', 'str'), mod_param_name) + @property + def binary(self): + return self.command[0] + def __call__(self, args_order=None, output_process=None, ignore_value_none=True, check_mode_skip=False, check_mode_return=None, **kwargs): if output_process is None: output_process = _process_as_is diff --git a/tests/integration/targets/cmd_runner/library/cmd_echo.py b/tests/integration/targets/cmd_runner/library/cmd_echo.py index 47af478cd2..ec0beb98e7 100644 --- a/tests/integration/targets/cmd_runner/library/cmd_echo.py +++ b/tests/integration/targets/cmd_runner/library/cmd_echo.py @@ -21,6 +21,8 @@ from ansible_collections.community.general.plugins.module_utils.cmd_runner impor def main(): module = AnsibleModule( argument_spec=dict( + cmd=dict(type="str", default="echo"), + path_prefix=dict(type="str"), arg_formats=dict(type="dict", default={}), arg_order=dict(type="raw", required=True), arg_values=dict(type="dict", default={}), @@ -41,7 +43,7 @@ def main(): arg_formats[arg] = func(*args) - runner = CmdRunner(module, ['echo', '--'], arg_formats=arg_formats) + runner = CmdRunner(module, [module.params["cmd"], '--'], arg_formats=arg_formats, path_prefix=module.params["path_prefix"]) with runner.context(p['arg_order'], check_mode_skip=p['check_mode_skip']) as ctx: result = ctx.run(**p['arg_values']) diff --git a/tests/integration/targets/cmd_runner/meta/main.yml b/tests/integration/targets/cmd_runner/meta/main.yml new file mode 100644 index 0000000000..982de6eb03 --- /dev/null +++ b/tests/integration/targets/cmd_runner/meta/main.yml @@ -0,0 +1,7 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +dependencies: + - setup_remote_tmp_dir diff --git a/tests/integration/targets/cmd_runner/tasks/main.yml b/tests/integration/targets/cmd_runner/tasks/main.yml index 36ab039f0f..e955c5d3d7 100644 --- a/tests/integration/targets/cmd_runner/tasks/main.yml +++ b/tests/integration/targets/cmd_runner/tasks/main.yml @@ -6,3 +6,4 @@ ansible.builtin.include_tasks: file: test_cmd_echo.yml loop: "{{ cmd_echo_tests }}" + when: item.condition | default(true) | bool diff --git a/tests/integration/targets/cmd_runner/tasks/test_cmd_echo.yml b/tests/integration/targets/cmd_runner/tasks/test_cmd_echo.yml index 1c2caf2b5a..dd56e9e762 100644 --- a/tests/integration/targets/cmd_runner/tasks/test_cmd_echo.yml +++ b/tests/integration/targets/cmd_runner/tasks/test_cmd_echo.yml @@ -3,17 +3,26 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later -- name: test cmd_echo [{{ item.name }}] - cmd_echo: - arg_formats: "{{ item.arg_formats|default(omit) }}" - arg_order: "{{ item.arg_order }}" - arg_values: "{{ item.arg_values|default(omit) }}" - check_mode_skip: "{{ item.check_mode_skip|default(omit) }}" - aa: "{{ item.aa|default(omit) }}" - register: test_result - check_mode: "{{ item.check_mode|default(omit) }}" - ignore_errors: "{{ item.expect_error|default(omit) }}" +- name: create copy of /bin/echo ({{ item.name }}) + ansible.builtin.copy: + src: /bin/echo + dest: "{{ item.copy_to }}/echo" + mode: "755" + when: item.copy_to is defined -- name: check results [{{ item.name }}] +- name: test cmd_echo module ({{ item.name }}) + cmd_echo: + cmd: "{{ item.cmd | default(omit) }}" + path_prefix: "{{ item.path_prefix | default(omit) }}" + arg_formats: "{{ item.arg_formats | default(omit) }}" + arg_order: "{{ item.arg_order }}" + arg_values: "{{ item.arg_values | default(omit) }}" + check_mode_skip: "{{ item.check_mode_skip | default(omit) }}" + aa: "{{ item.aa | default(omit) }}" + register: test_result + check_mode: "{{ item.check_mode | default(omit) }}" + ignore_errors: "{{ item.expect_error | default(omit) }}" + +- name: check results ({{ item.name }}) assert: that: "{{ item.assertions }}" diff --git a/tests/integration/targets/cmd_runner/vars/main.yml b/tests/integration/targets/cmd_runner/vars/main.yml index a1d74934fd..f9a7153381 100644 --- a/tests/integration/targets/cmd_runner/vars/main.yml +++ b/tests/integration/targets/cmd_runner/vars/main.yml @@ -138,3 +138,125 @@ cmd_echo_tests: - test_result.rc == 0 - test_result.out == "-- --answer=11 --tt-arg potatoes\n" - test_result.err == "" + + - name: use cmd echo + cmd: echo + arg_formats: + aa: + func: as_opt_eq_val + args: [--answer] + tt: + func: as_opt_val + args: [--tt-arg] + arg_order: 'aa tt' + arg_values: + tt: potatoes + aa: 11 + assertions: + - test_result.rc == 0 + - test_result.out == "-- --answer=11 --tt-arg potatoes\n" + - test_result.err == "" + + - name: use cmd /bin/echo + cmd: /bin/echo + arg_formats: + aa: + func: as_opt_eq_val + args: [--answer] + tt: + func: as_opt_val + args: [--tt-arg] + arg_order: 'aa tt' + arg_values: + tt: potatoes + aa: 11 + assertions: + - test_result.rc == 0 + - test_result.out == "-- --answer=11 --tt-arg potatoes\n" + - test_result.err == "" + + # this will not be in the regular set of paths get_bin_path() searches + - name: use cmd {{ remote_tmp_dir }}/echo + condition: > + {{ + ansible_distribution != "MacOSX" and + not (ansible_distribution == "CentOS" and ansible_distribution_major_version is version('7.0', '<')) + }} + copy_to: "{{ remote_tmp_dir }}" + cmd: "{{ remote_tmp_dir }}/echo" + arg_formats: + aa: + func: as_opt_eq_val + args: [--answer] + tt: + func: as_opt_val + args: [--tt-arg] + arg_order: 'aa tt' + arg_values: + tt: potatoes + aa: 11 + assertions: + - test_result.rc == 0 + - test_result.out == "-- --answer=11 --tt-arg potatoes\n" + - test_result.err == "" + + - name: use cmd echo with path_prefix {{ remote_tmp_dir }} + cmd: echo + condition: > + {{ + ansible_distribution != "MacOSX" and + not (ansible_distribution == "CentOS" and ansible_distribution_major_version is version('7.0', '<')) + }} + copy_to: "{{ remote_tmp_dir }}" + path_prefix: "{{ remote_tmp_dir }}" + arg_formats: + aa: + func: as_opt_eq_val + args: [--answer] + tt: + func: as_opt_val + args: [--tt-arg] + arg_order: 'aa tt' + arg_values: + tt: potatoes + aa: 11 + assertions: + - test_result.rc == 0 + - test_result.out == "-- --answer=11 --tt-arg potatoes\n" + - test_result.err == "" + + - name: use cmd never-existed + cmd: never-existed + arg_formats: + aa: + func: as_opt_eq_val + args: [--answer] + tt: + func: as_opt_val + args: [--tt-arg] + arg_order: 'aa tt' + arg_values: + tt: potatoes + aa: 11 + expect_error: true + assertions: + - > + "Failed to find required executable" in test_result.msg + + - name: use cmd /usr/bin/never-existed + cmd: /usr/bin/never-existed + arg_formats: + aa: + func: as_opt_eq_val + args: [--answer] + tt: + func: as_opt_val + args: [--tt-arg] + arg_order: 'aa tt' + arg_values: + tt: potatoes + aa: 11 + expect_error: true + assertions: + - > + "No such file or directory" in test_result.msg