diff --git a/changelogs/fragments/fix-handlers-callback.yml b/changelogs/fragments/fix-handlers-callback.yml new file mode 100644 index 00000000000..b590c208755 --- /dev/null +++ b/changelogs/fragments/fix-handlers-callback.yml @@ -0,0 +1,2 @@ +bugfixes: + - "handlers - fix ``v2_playbook_on_notify`` callback not being called when notifying handlers" diff --git a/lib/ansible/plugins/strategy/__init__.py b/lib/ansible/plugins/strategy/__init__.py index 7b808900032..1ace575bd5a 100644 --- a/lib/ansible/plugins/strategy/__init__.py +++ b/lib/ansible/plugins/strategy/__init__.py @@ -947,7 +947,7 @@ class StrategyBase: # actually notify proper handlers based on all notifications up to this point for notification in list(host_state.handler_notifications): for handler in self.search_handlers_by_notification(notification, iterator): - if not handler.notify_host(target_host): + if handler.notify_host(target_host): # NOTE even with notifications deduplicated this can still happen in case of handlers being # notified multiple times using different names, like role name or fqcn self._tqm.send_callback('v2_playbook_on_notify', handler, target_host) diff --git a/test/integration/targets/ansible-playbook-callbacks/aliases b/test/integration/targets/ansible-playbook-callbacks/aliases new file mode 100644 index 00000000000..d88a7fa6140 --- /dev/null +++ b/test/integration/targets/ansible-playbook-callbacks/aliases @@ -0,0 +1,4 @@ +shippable/posix/group3 +context/controller +needs/target/setup_remote_tmp_dir +needs/target/support-callback_plugins diff --git a/test/integration/targets/ansible-playbook-callbacks/all-callbacks.yml b/test/integration/targets/ansible-playbook-callbacks/all-callbacks.yml new file mode 100644 index 00000000000..8ea3a571897 --- /dev/null +++ b/test/integration/targets/ansible-playbook-callbacks/all-callbacks.yml @@ -0,0 +1,123 @@ +- hosts: localhost + gather_facts: false + vars_prompt: + name: vars_prompt_var + default: hamsandwich + handlers: + - name: handler1 + debug: + msg: handler1 + + - debug: + msg: listen1 + listen: + - listen1 + roles: + - setup_remote_tmp_dir + tasks: + - name: ok + debug: + msg: ok + + - name: changed + debug: + msg: changed + changed_when: true + + - name: skipped + debug: + msg: skipped + when: false + + - name: failed + debug: + msg: failed + failed_when: true + ignore_errors: true + + - name: unreachable + ping: + delegate_to: example.org + ignore_unreachable: true + vars: + ansible_timeout: 1 + + - name: loop + debug: + ignore_errors: true + changed_when: '{{ item.changed }}' + failed_when: '{{ item.failed }}' + when: '{{ item.when }}' + loop: + # ok + - changed: false + failed: false + when: true + # changed + - changed: true + failed: false + when: true + # failed + - changed: false + failed: true + when: true + # skipped + - changed: false + failed: false + when: false + + - name: notify handler1 + debug: + msg: notify handler1 + changed_when: true + notify: + - handler1 + + - name: notify listen1 + debug: + msg: notify listen1 + changed_when: true + notify: + - listen1 + + - name: retry ok + debug: + register: result + until: result.attempts == 2 + retries: 1 + delay: 0 + + - name: retry failed + debug: + register: result + until: result.attempts == 3 + retries: 1 + delay: 0 + ignore_errors: true + + - name: async poll ok + command: sleep 2 + async: 3 + poll: 1 + + - name: async poll failed + shell: sleep 2; false + async: 3 + poll: 1 + ignore_errors: true + + - include_tasks: include_me.yml + + - name: diff + copy: + content: diff + dest: '{{ remote_tmp_dir.path }}/diff.txt' + diff: true + +- hosts: i_dont_exist + +- hosts: localhost + gather_facts: false + max_fail_percentage: 0 + tasks: + - fail: diff --git a/test/integration/targets/ansible-playbook-callbacks/callbacks_list.expected b/test/integration/targets/ansible-playbook-callbacks/callbacks_list.expected new file mode 100644 index 00000000000..39e5cafa0e8 --- /dev/null +++ b/test/integration/targets/ansible-playbook-callbacks/callbacks_list.expected @@ -0,0 +1,22 @@ + 1 __init__ +83 v2_on_any + 4 v2_playbook_on_handler_task_start + 2 v2_playbook_on_include + 3 v2_playbook_on_notify + 1 v2_playbook_on_play_start + 1 v2_playbook_on_start + 1 v2_playbook_on_stats +17 v2_playbook_on_task_start + 1 v2_playbook_on_vars_prompt + 1 v2_runner_item_on_failed + 2 v2_runner_item_on_ok + 1 v2_runner_item_on_skipped + 1 v2_runner_on_async_failed + 1 v2_runner_on_async_ok + 2 v2_runner_on_async_poll + 5 v2_runner_on_failed +15 v2_runner_on_ok + 1 v2_runner_on_skipped +21 v2_runner_on_start + 1 v2_runner_on_unreachable + 2 v2_runner_retry diff --git a/test/integration/targets/ansible-playbook-callbacks/runme.sh b/test/integration/targets/ansible-playbook-callbacks/runme.sh new file mode 100755 index 00000000000..73c8a3d0799 --- /dev/null +++ b/test/integration/targets/ansible-playbook-callbacks/runme.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -eux + +export ANSIBLE_CALLBACK_PLUGINS=../support-callback_plugins/callback_plugins +export ANSIBLE_ROLES_PATH=../ +export ANSIBLE_STDOUT_CALLBACK=callback_debug + +ansible-playbook all-callbacks.yml 2>/dev/null | sort | uniq -c | tee callbacks_list.out + +diff -w callbacks_list.out callbacks_list.expected diff --git a/test/integration/targets/ansible/aliases b/test/integration/targets/ansible/aliases index 8278ec8bcc7..c7f2050a3c0 100644 --- a/test/integration/targets/ansible/aliases +++ b/test/integration/targets/ansible/aliases @@ -1,2 +1,3 @@ shippable/posix/group3 context/controller +needs/target/support-callback_plugins diff --git a/test/integration/targets/ansible/runme.sh b/test/integration/targets/ansible/runme.sh index e9e72a9fec6..55e9fea7635 100755 --- a/test/integration/targets/ansible/runme.sh +++ b/test/integration/targets/ansible/runme.sh @@ -34,7 +34,7 @@ ansible localhost -m debug -a var=playbook_dir --playbook-dir=/doesnotexist/tmp env -u ANSIBLE_PLAYBOOK_DIR ANSIBLE_CONFIG=./playbookdir_cfg.ini ansible localhost -m debug -a var=playbook_dir | grep '"playbook_dir": "/doesnotexist/tmp"' # test adhoc callback triggers -ANSIBLE_STDOUT_CALLBACK=callback_debug ANSIBLE_LOAD_CALLBACK_PLUGINS=1 ansible --playbook-dir . testhost -i ../../inventory -m ping | grep -E '^v2_' | diff -u adhoc-callback.stdout - +ANSIBLE_CALLBACK_PLUGINS=../support-callback_plugins/callback_plugins ANSIBLE_STDOUT_CALLBACK=callback_debug ANSIBLE_LOAD_CALLBACK_PLUGINS=1 ansible --playbook-dir . testhost -i ../../inventory -m ping | grep -E '^v2_' | diff -u adhoc-callback.stdout - # CB_WANTS_IMPLICIT isn't anything in Ansible itself. # Our test cb plugin just accepts it. It lets us avoid copypasting the whole diff --git a/test/integration/targets/support-callback_plugins/aliases b/test/integration/targets/support-callback_plugins/aliases new file mode 100644 index 00000000000..136c05e0d02 --- /dev/null +++ b/test/integration/targets/support-callback_plugins/aliases @@ -0,0 +1 @@ +hidden diff --git a/test/integration/targets/ansible/callback_plugins/callback_debug.py b/test/integration/targets/support-callback_plugins/callback_plugins/callback_debug.py similarity index 100% rename from test/integration/targets/ansible/callback_plugins/callback_debug.py rename to test/integration/targets/support-callback_plugins/callback_plugins/callback_debug.py