diff --git a/.azure-pipelines/azure-pipelines.yml b/.azure-pipelines/azure-pipelines.yml index 3390c34feb8..4fbd7456ee3 100644 --- a/.azure-pipelines/azure-pipelines.yml +++ b/.azure-pipelines/azure-pipelines.yml @@ -79,10 +79,10 @@ stages: targets: - name: macOS 13.2 test: macos/13.2 - - name: RHEL 9.2 py39 - test: rhel/9.2@3.9 - - name: RHEL 9.2 py311 - test: rhel/9.2@3.11 + - name: RHEL 9.3 py39 + test: rhel/9.3@3.9 + - name: RHEL 9.3 py311 + test: rhel/9.3@3.11 - name: FreeBSD 13.2 test: freebsd/13.2 groups: @@ -93,8 +93,8 @@ stages: targets: - name: macOS 13.2 test: macos/13.2 - - name: RHEL 9.2 - test: rhel/9.2 + - name: RHEL 9.3 + test: rhel/9.3 - name: FreeBSD 13.2 test: freebsd/13.2 groups: @@ -108,8 +108,8 @@ stages: test: alpine/3.18 - name: Fedora 38 test: fedora/38 - - name: RHEL 9.2 - test: rhel/9.2 + - name: RHEL 9.3 + test: rhel/9.3 - name: Ubuntu 22.04 test: ubuntu/22.04 groups: diff --git a/changelogs/fragments/81532-fix-nested-flush_handlers.yml b/changelogs/fragments/81532-fix-nested-flush_handlers.yml new file mode 100644 index 00000000000..e43aa2e1df7 --- /dev/null +++ b/changelogs/fragments/81532-fix-nested-flush_handlers.yml @@ -0,0 +1,2 @@ +bugfixes: + - flush_handlers - properly handle a handler failure in a nested block when ``force_handlers`` is set (http://github.com/ansible/ansible/issues/81532) diff --git a/changelogs/fragments/ansible-test-rhel-9.3.yml b/changelogs/fragments/ansible-test-rhel-9.3.yml new file mode 100644 index 00000000000..b8d1a3f3b49 --- /dev/null +++ b/changelogs/fragments/ansible-test-rhel-9.3.yml @@ -0,0 +1,2 @@ +minor_changes: + - ansible-test - Add support for RHEL 9.3 remotes. diff --git a/changelogs/fragments/copy_keep_suffix_temp.yml b/changelogs/fragments/copy_keep_suffix_temp.yml new file mode 100644 index 00000000000..d6dc36dc54e --- /dev/null +++ b/changelogs/fragments/copy_keep_suffix_temp.yml @@ -0,0 +1,4 @@ +bugfixes: + - copy action now ensures that tempfiles use the same suffix as destination, to allow for ``validate`` to work with utilities that check extensions. + - copy action now also generates temprary files as hidden ('.' prefixed) to avoid accidental pickup by running services that glob by extension. + - template action will also inherit the behavior from copy (as it uses it internally). diff --git a/lib/ansible/executor/play_iterator.py b/lib/ansible/executor/play_iterator.py index 7acbe4172cb..58eebfaea57 100644 --- a/lib/ansible/executor/play_iterator.py +++ b/lib/ansible/executor/play_iterator.py @@ -50,7 +50,7 @@ class FailedStates(IntFlag): TASKS = 2 RESCUE = 4 ALWAYS = 8 - HANDLERS = 16 + HANDLERS = 16 # NOTE not in use anymore class HostState: @@ -429,22 +429,18 @@ class PlayIterator: state.update_handlers = False state.cur_handlers_task = 0 - if state.fail_state & FailedStates.HANDLERS == FailedStates.HANDLERS: - state.update_handlers = True - state.run_state = IteratingStates.COMPLETE - else: - while True: - try: - task = state.handlers[state.cur_handlers_task] - except IndexError: - task = None - state.run_state = state.pre_flushing_run_state - state.update_handlers = True + while True: + try: + task = state.handlers[state.cur_handlers_task] + except IndexError: + task = None + state.run_state = state.pre_flushing_run_state + state.update_handlers = True + break + else: + state.cur_handlers_task += 1 + if task.is_host_notified(host): break - else: - state.cur_handlers_task += 1 - if task.is_host_notified(host): - break elif state.run_state == IteratingStates.COMPLETE: return (state, None) @@ -485,20 +481,16 @@ class PlayIterator: else: state.fail_state |= FailedStates.ALWAYS state.run_state = IteratingStates.COMPLETE - elif state.run_state == IteratingStates.HANDLERS: - state.fail_state |= FailedStates.HANDLERS - state.update_handlers = True - if state._blocks[state.cur_block].rescue: - state.run_state = IteratingStates.RESCUE - elif state._blocks[state.cur_block].always: - state.run_state = IteratingStates.ALWAYS - else: - state.run_state = IteratingStates.COMPLETE return state def mark_host_failed(self, host): s = self.get_host_state(host) display.debug("marking host %s failed, current state: %s" % (host, s)) + if s.run_state == IteratingStates.HANDLERS: + # we are failing `meta: flush_handlers`, so just reset the state to whatever + # it was before and let `_set_failed_state` figure out the next state + s.run_state = s.pre_flushing_run_state + s.update_handlers = True s = self._set_failed_state(s) display.debug("^ failed state is now: %s" % s) self.set_state_for_host(host.name, s) @@ -514,8 +506,6 @@ class PlayIterator: return True elif state.run_state == IteratingStates.ALWAYS and self._check_failed_state(state.always_child_state): return True - elif state.run_state == IteratingStates.HANDLERS and state.fail_state & FailedStates.HANDLERS == FailedStates.HANDLERS: - return True elif state.fail_state != FailedStates.NONE: if state.run_state == IteratingStates.RESCUE and state.fail_state & FailedStates.RESCUE == 0: return False diff --git a/lib/ansible/module_utils/common/validation.py b/lib/ansible/module_utils/common/validation.py index 7b2bc38b2e0..69721e47f18 100644 --- a/lib/ansible/module_utils/common/validation.py +++ b/lib/ansible/module_utils/common/validation.py @@ -542,7 +542,7 @@ def check_type_raw(value): def check_type_bytes(value): """Convert a human-readable string value to bytes - Raises :class:`TypeError` if unable to covert the value + Raises :class:`TypeError` if unable to convert the value """ try: return human_to_bytes(value) @@ -555,7 +555,7 @@ def check_type_bits(value): Example: ``check_type_bits('1Mb')`` returns integer 1048576. - Raises :class:`TypeError` if unable to covert the value. + Raises :class:`TypeError` if unable to convert the value. """ try: return human_to_bytes(value, isbits=True) @@ -567,7 +567,7 @@ def check_type_jsonarg(value): """Return a jsonified string. Sometimes the controller turns a json string into a dict/list so transform it back into json here - Raises :class:`TypeError` if unable to covert the value + Raises :class:`TypeError` if unable to convert the value """ if isinstance(value, (text_type, binary_type)): diff --git a/lib/ansible/plugins/action/copy.py b/lib/ansible/plugins/action/copy.py index 47bcd7a13e3..b0b1dc643f9 100644 --- a/lib/ansible/plugins/action/copy.py +++ b/lib/ansible/plugins/action/copy.py @@ -292,7 +292,12 @@ class ActionModule(ActionBase): return result # Define a remote directory that we will copy the file to. - tmp_src = self._connection._shell.join_path(self._connection._shell.tmpdir, 'source') + tmp_src = self._connection._shell.join_path(self._connection._shell.tmpdir, '.source') + + # ensure we keep suffix for validate + suffix = os.path.splitext(dest_file)[1] + if suffix: + tmp_src += suffix remote_path = None @@ -385,7 +390,7 @@ class ActionModule(ActionBase): def _create_content_tempfile(self, content): ''' Create a tempfile containing defined content ''' - fd, content_tempfile = tempfile.mkstemp(dir=C.DEFAULT_LOCAL_TMP) + fd, content_tempfile = tempfile.mkstemp(dir=C.DEFAULT_LOCAL_TMP, prefix='.') f = os.fdopen(fd, 'wb') content = to_bytes(content) try: diff --git a/lib/ansible/plugins/filter/union.yml b/lib/ansible/plugins/filter/union.yml index 7ef656de00b..d5e5c7ae31d 100644 --- a/lib/ansible/plugins/filter/union.yml +++ b/lib/ansible/plugins/filter/union.yml @@ -29,7 +29,7 @@ EXAMPLES: | # list1: [1, 2, 5, 1, 3, 4, 10] # list2: [1, 2, 3, 4, 5, 11, 99] {{ list1 | union(list2) }} - # => [1, 2, 5, 1, 3, 4, 10, 11, 99] + # => [1, 2, 5, 3, 4, 10, 11, 99] RETURN: _value: description: A unique list of all the elements from both lists. diff --git a/test/integration/targets/callback_default/callback_default.out.result_format_yaml_lossy_verbose.stdout b/test/integration/targets/callback_default/callback_default.out.result_format_yaml_lossy_verbose.stdout index ed455756ba8..51f025162f8 100644 --- a/test/integration/targets/callback_default/callback_default.out.result_format_yaml_lossy_verbose.stdout +++ b/test/integration/targets/callback_default/callback_default.out.result_format_yaml_lossy_verbose.stdout @@ -166,7 +166,7 @@ changed: [testhost] => mode: '0644' owner: root size: 3 - src: .../source + src: .../.source.txt state: file uid: 0 diff --git a/test/integration/targets/callback_default/callback_default.out.result_format_yaml_verbose.stdout b/test/integration/targets/callback_default/callback_default.out.result_format_yaml_verbose.stdout index 3a121a5f9f1..8fd5b4f0e87 100644 --- a/test/integration/targets/callback_default/callback_default.out.result_format_yaml_verbose.stdout +++ b/test/integration/targets/callback_default/callback_default.out.result_format_yaml_verbose.stdout @@ -173,7 +173,7 @@ changed: [testhost] => mode: '0644' owner: root size: 3 - src: .../source + src: .../.source.txt state: file uid: 0 diff --git a/test/integration/targets/callback_default/runme.sh b/test/integration/targets/callback_default/runme.sh index a815132a5c2..eab50bfb328 100755 --- a/test/integration/targets/callback_default/runme.sh +++ b/test/integration/targets/callback_default/runme.sh @@ -34,7 +34,7 @@ run_test() { sed -i -e 's/^Using .*//g' "${OUTFILE}.${testname}.stdout" sed -i -e 's/[0-9]:[0-9]\{2\}:[0-9]\{2\}\.[0-9]\{6\}/0:00:00.000000/g' "${OUTFILE}.${testname}.stdout" sed -i -e 's/[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\} [0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}\.[0-9]\{6\}/0000-00-00 00:00:00.000000/g' "${OUTFILE}.${testname}.stdout" - sed -i -e 's#: .*/source$#: .../source#g' "${OUTFILE}.${testname}.stdout" + sed -i -e 's#: .*/\.source\.txt$#: .../.source.txt#g' "${OUTFILE}.${testname}.stdout" sed -i -e '/secontext:/d' "${OUTFILE}.${testname}.stdout" sed -i -e 's/group: wheel/group: root/g' "${OUTFILE}.${testname}.stdout" diff --git a/test/integration/targets/handlers/nested_flush_handlers_failure_force.yml b/test/integration/targets/handlers/nested_flush_handlers_failure_force.yml new file mode 100644 index 00000000000..7380923e99a --- /dev/null +++ b/test/integration/targets/handlers/nested_flush_handlers_failure_force.yml @@ -0,0 +1,19 @@ +- hosts: A,B + gather_facts: false + force_handlers: true + tasks: + - block: + - command: echo + notify: h + + - meta: flush_handlers + rescue: + - debug: + msg: flush_handlers_rescued + always: + - debug: + msg: flush_handlers_always + handlers: + - name: h + fail: + when: inventory_hostname == "A" diff --git a/test/integration/targets/handlers/runme.sh b/test/integration/targets/handlers/runme.sh index e26fdd7a1be..e24dad60289 100755 --- a/test/integration/targets/handlers/runme.sh +++ b/test/integration/targets/handlers/runme.sh @@ -206,3 +206,7 @@ ansible-playbook force_handlers_blocks_81533-1.yml -i inventory.handlers "$@" 2> ansible-playbook force_handlers_blocks_81533-2.yml -i inventory.handlers "$@" 2>&1 | tee out.txt [ "$(grep out.txt -ce 'hosts_left')" = "1" ] + +ansible-playbook nested_flush_handlers_failure_force.yml -i inventory.handlers "$@" 2>&1 | tee out.txt +[ "$(grep out.txt -ce 'flush_handlers_rescued')" = "1" ] +[ "$(grep out.txt -ce 'flush_handlers_always')" = "2" ] diff --git a/test/lib/ansible_test/_data/completion/remote.txt b/test/lib/ansible_test/_data/completion/remote.txt index d30f5ebb64e..106c195bacf 100644 --- a/test/lib/ansible_test/_data/completion/remote.txt +++ b/test/lib/ansible_test/_data/completion/remote.txt @@ -7,6 +7,7 @@ freebsd python_dir=/usr/local/bin become=su_sudo provider=aws arch=x86_64 macos/13.2 python=3.11 python_dir=/usr/local/bin become=sudo provider=parallels arch=x86_64 macos python_dir=/usr/local/bin become=sudo provider=parallels arch=x86_64 rhel/9.2 python=3.9,3.11 become=sudo provider=aws arch=x86_64 +rhel/9.3 python=3.9,3.11 become=sudo provider=aws arch=x86_64 rhel become=sudo provider=aws arch=x86_64 ubuntu/22.04 python=3.10 become=sudo provider=aws arch=x86_64 ubuntu become=sudo provider=aws arch=x86_64