Parse async response in async action. (#16534)

* Parse async response in async action.
* Add async test for non-JSON data before module output.
* Fix existing async unit test.

Resolves #16156

(cherry picked from commit 292785ff2b)
pull/16670/head
Matt Clay 9 years ago
parent a598f26006
commit 75fa80f73c

@ -459,15 +459,6 @@ class TaskExecutor:
vars_copy[self._task.register] = wrap_var(result.copy()) vars_copy[self._task.register] = wrap_var(result.copy())
if self._task.async > 0: if self._task.async > 0:
# the async_wrapper module returns dumped JSON via its stdout
# response, so we parse it here and replace the result
try:
if 'skipped' in result and result['skipped'] or 'failed' in result and result['failed']:
return result
result = json.loads(result.get('stdout'))
except (TypeError, ValueError) as e:
return dict(failed=True, msg=u"The async task did not return valid JSON: %s" % to_unicode(e))
if self._task.poll > 0: if self._task.poll > 0:
result = self._poll_async_result(result=result, templar=templar) result = self._poll_async_result(result=result, templar=templar)

@ -96,4 +96,11 @@ class ActionModule(ActionBase):
result['changed'] = True result['changed'] = True
if 'skipped' in result and result['skipped'] or 'failed' in result and result['failed']:
return result
# the async_wrapper module returns dumped JSON via its stdout
# response, so we parse it here and replace the result
result = self._parse_returned_data(result)
return result return result

@ -23,7 +23,7 @@ VAULT_PASSWORD_FILE = vault-password
CONSUL_RUNNING := $(shell python consul_running.py) CONSUL_RUNNING := $(shell python consul_running.py)
EUID := $(shell id -u -r) EUID := $(shell id -u -r)
all: setup test_test_infra parsing test_var_precedence unicode test_templating_settings environment test_connection non_destructive destructive includes blocks pull check_mode test_hash test_handlers test_group_by test_vault test_tags test_lookup_paths no_log test_gathering_facts all: setup test_test_infra parsing test_var_precedence unicode test_templating_settings environment test_connection non_destructive destructive includes blocks pull check_mode test_hash test_handlers test_group_by test_vault test_tags test_lookup_paths no_log test_gathering_facts test_async
test_test_infra: test_test_infra:
# ensure fail/assert work locally and can stop execution with non-zero exit code # ensure fail/assert work locally and can stop execution with non-zero exit code
@ -284,3 +284,10 @@ test_lookup_paths: setup
no_log: setup no_log: setup
# This test expects 7 loggable vars and 0 non loggable ones, if either mismatches it fails, run the ansible-playbook command to debug # This test expects 7 loggable vars and 0 non loggable ones, if either mismatches it fails, run the ansible-playbook command to debug
[ "$$(ansible-playbook no_log_local.yml -i $(INVENTORY) -e outputdir=$(TEST_DIR) -vvvvv | awk --source 'BEGIN { logme = 0; nolog = 0; } /LOG_ME/ { logme += 1;} /DO_NOT_LOG/ { nolog += 1;} END { printf "%d/%d", logme, nolog; }')" = "6/0" ] [ "$$(ansible-playbook no_log_local.yml -i $(INVENTORY) -e outputdir=$(TEST_DIR) -vvvvv | awk --source 'BEGIN { logme = 0; nolog = 0; } /LOG_ME/ { logme += 1;} /DO_NOT_LOG/ { nolog += 1;} END { printf "%d/%d", logme, nolog; }')" = "6/0" ]
test_async:
# Verify that extra data before module JSON output during async call is ignored.
LC_ALL=bogus ansible-playbook test_async.yml -i $(INVENTORY) -e outputdir=$(TEST_DIR) -v $(TEST_FLAGS)
# Verify that the warning exists by examining debug output.
ANSIBLE_DEBUG=1 LC_ALL=bogus ansible-playbook test_async.yml -i $(INVENTORY) -e outputdir=$(TEST_DIR) -v $(TEST_FLAGS) \
| grep -q 'bash: warning: setlocale: LC_ALL: cannot change locale (bogus)'

@ -0,0 +1,10 @@
- hosts: testhost3
gather_facts: false
tasks:
# make sure non-JSON data before module output is ignored
- name: async ping with invalid locale via ssh
ping:
async: 3
poll: 1
register: result
- debug: var=result

@ -26,6 +26,7 @@ from ansible.errors import AnsibleError, AnsibleParserError
from ansible.executor.task_executor import TaskExecutor from ansible.executor.task_executor import TaskExecutor
from ansible.playbook.play_context import PlayContext from ansible.playbook.play_context import PlayContext
from ansible.plugins import action_loader, lookup_loader from ansible.plugins import action_loader, lookup_loader
from ansible.parsing.yaml.objects import AnsibleUnicode
from units.mock.loader import DictDataLoader from units.mock.loader import DictDataLoader
@ -355,6 +356,7 @@ class TestTaskExecutor(unittest.TestCase):
# here: on Python 2 comparing MagicMock() > 0 returns True, and the # here: on Python 2 comparing MagicMock() > 0 returns True, and the
# other reason is that if I specify 0 here, the test fails. ;) # other reason is that if I specify 0 here, the test fails. ;)
mock_task.async = 1 mock_task.async = 1
mock_task.poll = 0
mock_play_context = MagicMock() mock_play_context = MagicMock()
mock_play_context.post_validate.return_value = None mock_play_context.post_validate.return_value = None
@ -388,11 +390,11 @@ class TestTaskExecutor(unittest.TestCase):
mock_action.run.return_value = dict(ansible_facts=dict()) mock_action.run.return_value = dict(ansible_facts=dict())
res = te._execute() res = te._execute()
mock_task.changed_when = "1 == 1" mock_task.changed_when = MagicMock(return_value=AnsibleUnicode("1 == 1"))
res = te._execute() res = te._execute()
mock_task.changed_when = None mock_task.changed_when = None
mock_task.failed_when = "1 == 1" mock_task.failed_when = MagicMock(return_value=AnsibleUnicode("1 == 1"))
res = te._execute() res = te._execute()
mock_task.failed_when = None mock_task.failed_when = None

Loading…
Cancel
Save