Add a 'finished' test for async jobs (#38325)
This provides a more convenient way for testing (async) jobs. When used with a non-async job it will report a warning so the user is aware that he may be doing something incorrect. Since the 'finished' result value is an integer (!), the test is turning this in a proper boolean.
This commit is contained in:
parent
5ecfb0b793
commit
939de473c6
2 changed files with 56 additions and 9 deletions
|
@ -26,11 +26,17 @@ from distutils.version import LooseVersion, StrictVersion
|
||||||
|
|
||||||
from ansible import errors
|
from ansible import errors
|
||||||
|
|
||||||
|
try:
|
||||||
|
from __main__ import display
|
||||||
|
except ImportError:
|
||||||
|
from ansible.utils.display import Display
|
||||||
|
display = Display()
|
||||||
|
|
||||||
|
|
||||||
def failed(result):
|
def failed(result):
|
||||||
''' Test if task result yields failed '''
|
''' Test if task result yields failed '''
|
||||||
if not isinstance(result, MutableMapping):
|
if not isinstance(result, MutableMapping):
|
||||||
raise errors.AnsibleFilterError("The failed test expects a dictionary")
|
raise errors.AnsibleFilterError("The 'failed' test expects a dictionary")
|
||||||
return result.get('failed', False)
|
return result.get('failed', False)
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,7 +48,7 @@ def success(result):
|
||||||
def changed(result):
|
def changed(result):
|
||||||
''' Test if task result yields changed '''
|
''' Test if task result yields changed '''
|
||||||
if not isinstance(result, MutableMapping):
|
if not isinstance(result, MutableMapping):
|
||||||
raise errors.AnsibleFilterError("The changed test expects a dictionary")
|
raise errors.AnsibleFilterError("The 'changed' test expects a dictionary")
|
||||||
if 'changed' not in result:
|
if 'changed' not in result:
|
||||||
changed = False
|
changed = False
|
||||||
if (
|
if (
|
||||||
|
@ -62,10 +68,24 @@ def changed(result):
|
||||||
def skipped(result):
|
def skipped(result):
|
||||||
''' Test if task result yields skipped '''
|
''' Test if task result yields skipped '''
|
||||||
if not isinstance(result, MutableMapping):
|
if not isinstance(result, MutableMapping):
|
||||||
raise errors.AnsibleFilterError("The skipped test expects a dictionary")
|
raise errors.AnsibleFilterError("The 'skipped' test expects a dictionary")
|
||||||
return result.get('skipped', False)
|
return result.get('skipped', False)
|
||||||
|
|
||||||
|
|
||||||
|
def finished(result):
|
||||||
|
''' Test if async task has finished '''
|
||||||
|
if not isinstance(result, MutableMapping):
|
||||||
|
raise errors.AnsibleFilterError("The 'finished' test expects a dictionary")
|
||||||
|
if 'finished' in result:
|
||||||
|
# For async tasks return status
|
||||||
|
# NOTE: The value of finished it 0 or 1, not False or True :-/
|
||||||
|
return result.get('finished', 0) == 1
|
||||||
|
else:
|
||||||
|
# For non-async tasks warn user, but return as finished
|
||||||
|
display.warning("The 'finished' test expects an async task, but a non-async task was tested")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def regex(value='', pattern='', ignorecase=False, multiline=False, match_type='search'):
|
def regex(value='', pattern='', ignorecase=False, multiline=False, match_type='search'):
|
||||||
''' Expose `re` as a boolean filter using the `search` method by default.
|
''' Expose `re` as a boolean filter using the `search` method by default.
|
||||||
This is likely only useful for `search` and `match` which already
|
This is likely only useful for `search` and `match` which already
|
||||||
|
@ -139,6 +159,9 @@ class TestModule(object):
|
||||||
'skipped': skipped,
|
'skipped': skipped,
|
||||||
'skip': skipped,
|
'skip': skipped,
|
||||||
|
|
||||||
|
# async testing
|
||||||
|
'finished': finished,
|
||||||
|
|
||||||
# regex
|
# regex
|
||||||
'match': match,
|
'match': match,
|
||||||
'search': search,
|
'search': search,
|
||||||
|
|
|
@ -33,13 +33,14 @@
|
||||||
- "'cmd' in async_result"
|
- "'cmd' in async_result"
|
||||||
- "'delta' in async_result"
|
- "'delta' in async_result"
|
||||||
- "'end' in async_result"
|
- "'end' in async_result"
|
||||||
- "'finished' in async_result or async_result.finished == 1"
|
|
||||||
- "'rc' in async_result"
|
- "'rc' in async_result"
|
||||||
- "'start' in async_result"
|
- "'start' in async_result"
|
||||||
- "'stderr' in async_result"
|
- "'stderr' in async_result"
|
||||||
- "'stdout' in async_result"
|
- "'stdout' in async_result"
|
||||||
- "'stdout_lines' in async_result"
|
- "'stdout_lines' in async_result"
|
||||||
- "async_result.rc == 0"
|
- async_result.rc == 0
|
||||||
|
- async_result.finished == 1
|
||||||
|
- async_result is finished
|
||||||
|
|
||||||
- name: test async without polling
|
- name: test async without polling
|
||||||
command: sleep 5
|
command: sleep 5
|
||||||
|
@ -54,7 +55,8 @@
|
||||||
that:
|
that:
|
||||||
- "'ansible_job_id' in async_result"
|
- "'ansible_job_id' in async_result"
|
||||||
- "'started' in async_result"
|
- "'started' in async_result"
|
||||||
- "'finished' not in async_result or async_result.finished == 0"
|
- async_result.finished == 0
|
||||||
|
- async_result is not finished
|
||||||
|
|
||||||
- name: test skipped task handling
|
- name: test skipped task handling
|
||||||
command: /bin/true
|
command: /bin/true
|
||||||
|
@ -79,7 +81,7 @@
|
||||||
- name: 'check on task started as a "fire-and-forget"'
|
- name: 'check on task started as a "fire-and-forget"'
|
||||||
async_status: jid={{ fnf_task.ansible_job_id }}
|
async_status: jid={{ fnf_task.ansible_job_id }}
|
||||||
register: fnf_result
|
register: fnf_result
|
||||||
until: fnf_result.finished
|
until: fnf_result is finished
|
||||||
retries: 10
|
retries: 10
|
||||||
delay: 1
|
delay: 1
|
||||||
|
|
||||||
|
@ -87,6 +89,7 @@
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- fnf_result.finished
|
- fnf_result.finished
|
||||||
|
- fnf_result is finished
|
||||||
|
|
||||||
- name: test graceful module failure
|
- name: test graceful module failure
|
||||||
async_test:
|
async_test:
|
||||||
|
@ -101,7 +104,8 @@
|
||||||
that:
|
that:
|
||||||
- async_result.ansible_job_id is match('\d+\.\d+')
|
- async_result.ansible_job_id is match('\d+\.\d+')
|
||||||
- async_result.finished == 1
|
- async_result.finished == 1
|
||||||
- async_result is changed == false
|
- async_result is finished
|
||||||
|
- async_result is not changed
|
||||||
- async_result is failed
|
- async_result is failed
|
||||||
- async_result.msg == 'failed gracefully'
|
- async_result.msg == 'failed gracefully'
|
||||||
|
|
||||||
|
@ -118,8 +122,11 @@
|
||||||
that:
|
that:
|
||||||
- async_result.ansible_job_id is match('\d+\.\d+')
|
- async_result.ansible_job_id is match('\d+\.\d+')
|
||||||
- async_result.finished == 1
|
- async_result.finished == 1
|
||||||
|
- async_result is finished
|
||||||
- async_result.changed == false
|
- async_result.changed == false
|
||||||
- async_result is failed == true
|
- async_result is not changed
|
||||||
|
- async_result.failed == true
|
||||||
|
- async_result is failed
|
||||||
- async_result.stderr is search('failing via exception', multiline=True)
|
- async_result.stderr is search('failing via exception', multiline=True)
|
||||||
|
|
||||||
- name: test leading junk before JSON
|
- name: test leading junk before JSON
|
||||||
|
@ -134,7 +141,9 @@
|
||||||
that:
|
that:
|
||||||
- async_result.ansible_job_id is match('\d+\.\d+')
|
- async_result.ansible_job_id is match('\d+\.\d+')
|
||||||
- async_result.finished == 1
|
- async_result.finished == 1
|
||||||
|
- async_result is finished
|
||||||
- async_result.changed == true
|
- async_result.changed == true
|
||||||
|
- async_result is changed
|
||||||
- async_result is successful
|
- async_result is successful
|
||||||
|
|
||||||
- name: test trailing junk after JSON
|
- name: test trailing junk after JSON
|
||||||
|
@ -149,6 +158,21 @@
|
||||||
that:
|
that:
|
||||||
- async_result.ansible_job_id is match('\d+\.\d+')
|
- async_result.ansible_job_id is match('\d+\.\d+')
|
||||||
- async_result.finished == 1
|
- async_result.finished == 1
|
||||||
|
- async_result is finished
|
||||||
- async_result.changed == true
|
- async_result.changed == true
|
||||||
|
- async_result is changed
|
||||||
- async_result is successful
|
- async_result is successful
|
||||||
- async_result.warnings[0] is search('trailing junk after module output')
|
- async_result.warnings[0] is search('trailing junk after module output')
|
||||||
|
|
||||||
|
# NOTE: This should report a warning that cannot be tested
|
||||||
|
- name: test async properties on non-async task
|
||||||
|
command: sleep 1
|
||||||
|
register: non_async_result
|
||||||
|
|
||||||
|
- name: validate response
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- non_async_result is successful
|
||||||
|
- non_async_result is changed
|
||||||
|
- non_async_result is finished
|
||||||
|
- "'ansible_job_id' not in non_async_result"
|
||||||
|
|
Loading…
Reference in a new issue