Ensure exit_json returns failed = False

This is required for modules that may return a non-zero `rc` value for a
successful run, similar to #24865 for Windows fixing **win_chocolatey**.

We also disable the dependency on `rc` value only, even if `failed` was
set.

Adapted unit and integration tests to the new scheme.
Updated raw, shell, script, expect to take `rc` into account.
pull/25181/head
Dag Wieers 8 years ago committed by Toshio Kuratomi
parent 1f78715848
commit 0e160d5c7e

@ -570,10 +570,13 @@ class TaskExecutor:
vars_copy.update(result['ansible_facts']) vars_copy.update(result['ansible_facts'])
vars_copy.update({'ansible_facts': result['ansible_facts']}) vars_copy.update({'ansible_facts': result['ansible_facts']})
# set the failed property if the result has a non-zero rc. This will be # set the failed property if it was missing.
# overridden below if the failed_when property is set if 'failed' not in result:
if result.get('rc', 0) != 0: result['failed'] = False
result['failed'] = True
# set the changed property if it was missing.
if 'changed' not in result:
result['changed'] = False
# if we didn't skip this task, use the helpers to evaluate the changed/ # if we didn't skip this task, use the helpers to evaluate the changed/
# failed_when properties # failed_when properties

@ -2023,6 +2023,9 @@ class AnsibleModule(object):
assert 'msg' in kwargs, "implementation error -- msg to explain the error is required" assert 'msg' in kwargs, "implementation error -- msg to explain the error is required"
kwargs['failed'] = True kwargs['failed'] = True
if not 'changed' in kwargs:
kwargs['changed'] = False
self.do_cleanup_files() self.do_cleanup_files()
self._return_formatted(kwargs) self._return_formatted(kwargs)
sys.exit(1) sys.exit(1)

@ -62,6 +62,10 @@ Function Exit-Json($obj)
$obj = @{ } $obj = @{ }
} }
if (-not $obj.ContainsKey('changed')) {
Set-Attr $obj "changed" $false
}
echo $obj | ConvertTo-Json -Compress -Depth 99 echo $obj | ConvertTo-Json -Compress -Depth 99
Exit Exit
} }
@ -88,6 +92,10 @@ Function Fail-Json($obj, $message = $null)
Set-Attr $obj "msg" $message Set-Attr $obj "msg" $message
Set-Attr $obj "failed" $true Set-Attr $obj "failed" $true
if (-not $obj.ContainsKey('changed')) {
Set-Attr $obj "changed" $false
}
echo $obj | ConvertTo-Json -Compress -Depth 99 echo $obj | ConvertTo-Json -Compress -Depth 99
Exit 1 Exit 1
} }

@ -204,7 +204,7 @@ def main():
if err is None: if err is None:
err = b('') err = b('')
module.exit_json( result = dict(
cmd = args, cmd = args,
stdout = out.rstrip(b("\r\n")), stdout = out.rstrip(b("\r\n")),
stderr = err.rstrip(b("\r\n")), stderr = err.rstrip(b("\r\n")),
@ -216,5 +216,11 @@ def main():
warnings = warnings warnings = warnings
) )
if rc != 0:
module.fail_json(msg='non-zero return code', **result)
module.exit_json(**result)
if __name__ == '__main__': if __name__ == '__main__':
main() main()

@ -221,7 +221,7 @@ def main():
if out is None: if out is None:
out = '' out = ''
ret = dict( result = dict(
cmd=args, cmd=args,
stdout=out.rstrip('\r\n'), stdout=out.rstrip('\r\n'),
rc=rc, rc=rc,
@ -231,11 +231,12 @@ def main():
changed=True, changed=True,
) )
if rc is not None: if rc is None:
module.exit_json(**ret) module.fail_json(msg='command exceeded timeout', **result)
else: elif rc != 0:
ret['msg'] = 'command exceeded timeout' module.fail_json(msg='non-zero return code', **result)
module.fail_json(**ret)
module.exit_json(**result)
if __name__ == '__main__': if __name__ == '__main__':

@ -42,4 +42,8 @@ class ActionModule(ActionBase):
result['changed'] = True result['changed'] = True
if 'rc' in result and result['rc'] != 0:
result['failed'] = True
result['msg'] = 'non-zero return code'
return result return result

@ -93,4 +93,8 @@ class ActionModule(ActionBase):
result['changed'] = True result['changed'] = True
if 'rc' in result and result['rc'] != 0:
result['failed'] = True
result['msg'] = 'non-zero return code'
return result return result

@ -111,7 +111,6 @@
- name: assert new EIP was assigned - name: assert new EIP was assigned
assert: assert:
that: that:
- '"failed" not in result'
- '"public_ip" in result' - '"public_ip" in result'
@ -128,10 +127,6 @@
instance_id={{ instance_id }} instance_id={{ instance_id }}
register: result register: result
- name: assert success disassociate EIP associated with instance
assert:
that:
- '"failed" not in result'
# eip allocated:1 assigned:0 # eip allocated:1 assigned:0
@ -152,7 +147,6 @@
- name: assert new EIP was assigned - name: assert new EIP was assigned
assert: assert:
that: that:
- '"failed" not in result'
- '"public_ip" in result' - '"public_ip" in result'
@ -176,11 +170,6 @@
ec2_secret_key={{ ec2_secret_key }} ec2_secret_key={{ ec2_secret_key }}
register: result register: result
- name: assert EIP deactivated
assert:
that:
- '"failed" not in result'
# eip allocated:0 assigned:0 # eip allocated:0 assigned:0
@ -205,7 +194,6 @@
- name: assert provision EIP with instance_id - name: assert provision EIP with instance_id
assert: assert:
that: that:
- '"failed" not in result'
- '"public_ip" in result' - '"public_ip" in result'
@ -245,7 +233,6 @@
- name: assert VPC EIP creation - name: assert VPC EIP creation
assert: assert:
that: that:
- '"failed" not in result'
- '"public_ip" in result' - '"public_ip" in result'
@ -268,7 +255,6 @@
#- name: assert new VPC EIP was assigned #- name: assert new VPC EIP was assigned
# assert: # assert:
# that: # that:
# - '"failed" not in result'
# - '"public_ip" in result' # - '"public_ip" in result'
# #
# #
@ -307,7 +293,6 @@
- name: assert environment variable EC2_REGION - name: assert environment variable EC2_REGION
assert: assert:
that: that:
- '"failed" not in result'
- '"public_ip" in result' - '"public_ip" in result'

@ -60,7 +60,6 @@
- assert: - assert:
that: that:
- 'info.changed' - 'info.changed'
- '"failed" not in info'
- 'info.elb.status == "created"' - 'info.elb.status == "created"'
- '"us-east-1c" in info.elb.zones' - '"us-east-1c" in info.elb.zones'
- '"us-east-1d" in info.elb.zones' - '"us-east-1d" in info.elb.zones'
@ -124,7 +123,6 @@
- assert: - assert:
that: that:
- '"failed" not in info'
- 'info.elb.status == "ok"' - 'info.elb.status == "ok"'
- 'info.changed' - 'info.changed'
- 'info.elb.zones[0] == "us-east-1b"' - 'info.elb.zones[0] == "us-east-1b"'
@ -154,7 +152,6 @@
- assert: - assert:
that: that:
- '"failed" not in info'
- 'info.changed' - 'info.changed'
- 'info.elb.status == "ok"' - 'info.elb.status == "ok"'
- '"us-east-1b" in info.elb.zones' - '"us-east-1b" in info.elb.zones'
@ -187,7 +184,6 @@
- assert: - assert:
that: that:
- '"failed" not in info'
- 'info.elb.status == "ok"' - 'info.elb.status == "ok"'
- 'info.changed' - 'info.changed'
- '[80, 81, "HTTP", "HTTP"] in info.elb.listeners' - '[80, 81, "HTTP", "HTTP"] in info.elb.listeners'
@ -220,7 +216,6 @@
- assert: - assert:
that: that:
- '"failed" not in info'
- 'info.elb.status == "ok"' - 'info.elb.status == "ok"'
- 'info.changed' - 'info.changed'
- '[80, 81, "HTTP", "HTTP"] in info.elb.listeners' - '[80, 81, "HTTP", "HTTP"] in info.elb.listeners'

@ -188,11 +188,6 @@
state=absent state=absent
register: result register: result
- name: assert state=absent
assert:
that:
- '"failed" not in result'
# ============================================================ # ============================================================
- name: test state=present (expected changed=true) - name: test state=present (expected changed=true)
ec2_group: ec2_group:

@ -157,11 +157,6 @@
state=absent state=absent
register: result register: result
- name: assert state=absent with key_material
assert:
that:
- '"failed" not in result'
# ============================================================ # ============================================================
- name: test state=present without key_material - name: test state=present without key_material
ec2_key: ec2_key:
@ -177,7 +172,6 @@
assert: assert:
that: that:
- 'result.changed' - 'result.changed'
- '"failed" not in result'
- '"key" in result' - '"key" in result'
- '"name" in result.key' - '"name" in result.key'
- '"fingerprint" in result.key' - '"fingerprint" in result.key'
@ -200,7 +194,6 @@
assert: assert:
that: that:
- 'result.changed' - 'result.changed'
- '"failed" not in result'
- '"key" in result' - '"key" in result'
- 'result.key == None' - 'result.key == None'
@ -220,7 +213,6 @@
- name: assert state=present with key_material - name: assert state=present with key_material
assert: assert:
that: that:
- '"failed" not in result'
- 'result.changed == True' - 'result.changed == True'
- '"key" in result' - '"key" in result'
- '"name" in result.key' - '"name" in result.key'
@ -246,7 +238,6 @@
assert: assert:
that: that:
- 'result.changed' - 'result.changed'
- '"failed" not in result'
- '"key" in result' - '"key" in result'
- 'result.key == None' - 'result.key == None'
@ -324,7 +315,6 @@
assert: assert:
that: that:
- 'result.changed' - 'result.changed'
- '"failed" not in result'
- '"key" in result' - '"key" in result'
- 'result.key == None' - 'result.key == None'
@ -345,6 +335,5 @@
assert: assert:
that: that:
- 'not result.changed' - 'not result.changed'
- '"failed" not in result'
- '"key" in result' - '"key" in result'
- 'result.key == None' - 'result.key == None'

@ -23,7 +23,7 @@
- assert: - assert:
that: that:
- "'failed' not in result" - "'failed' in result and not result.failed"
- name: command rc 0 failed_when_result False - name: command rc 0 failed_when_result False
shell: exit 0 shell: exit 0

@ -95,7 +95,7 @@
- name: check fetch directory result - name: check fetch directory result
assert: assert:
that: that:
- "failed_fetch_dir['failed']" - "failed_fetch_dir|failed"
- "fetch_dir.msg" - "fetch_dir.msg"
- name: create symlink to a file that we can fetch - name: create symlink to a file that we can fetch

@ -111,7 +111,7 @@
- name: Assert that the file was not downloaded - name: Assert that the file was not downloaded
assert: assert:
that: that:
- "result.failed == true" - "result|failed"
- "'Failed to validate the SSL certificate' in result.msg" - "'Failed to validate the SSL certificate' in result.msg"
- "stat_result.stat.exists == false" - "stat_result.stat.exists == false"
@ -150,14 +150,13 @@
assert: assert:
that: that:
- 'data_result.rc == 0' - 'data_result.rc == 0'
- '"failed" not in get_url_result'
when: "{{ python_has_ssl_context }}" when: "{{ python_has_ssl_context }}"
# If the client doesn't support SNI then get_url should have failed with a certificate mismatch # If the client doesn't support SNI then get_url should have failed with a certificate mismatch
- name: Assert that hostname verification failed because SNI is not supported on this version of python - name: Assert that hostname verification failed because SNI is not supported on this version of python
assert: assert:
that: that:
- 'get_url_result["failed"]' - 'get_url_result|failed'
when: "{{ not python_has_ssl_context }}" when: "{{ not python_has_ssl_context }}"
# These tests are just side effects of how the site is hosted. It's not # These tests are just side effects of how the site is hosted. It's not
@ -178,14 +177,14 @@
assert: assert:
that: that:
- 'data_result.rc == 0' - 'data_result.rc == 0'
- '"failed" not in get_url_result' - 'not get_url_result|failed'
when: "{{ python_has_ssl_context }}" when: "{{ python_has_ssl_context }}"
# If the client doesn't support SNI then get_url should have failed with a certificate mismatch # If the client doesn't support SNI then get_url should have failed with a certificate mismatch
- name: Assert that hostname verification failed because SNI is not supported on this version of python - name: Assert that hostname verification failed because SNI is not supported on this version of python
assert: assert:
that: that:
- 'get_url_result["failed"]' - 'get_url_result|failed'
when: "{{ not python_has_ssl_context }}" when: "{{ not python_has_ssl_context }}"
# End hacky SNI test section # End hacky SNI test section

@ -155,6 +155,7 @@
expected: expected:
changed: False changed: False
examined: 5 examined: 5
failed: False
files: files:
- { isarchive: True, - { isarchive: True,
attributes: Archive, attributes: Archive,
@ -237,6 +238,7 @@
expected: expected:
changed: False changed: False
examined: 11 examined: 11
failed: False
files: files:
- { isarchive: True, - { isarchive: True,
attributes: "Hidden, Archive", attributes: "Hidden, Archive",
@ -278,6 +280,7 @@
expected: expected:
changed: False changed: False
examined: 5 examined: 5
failed: False
files: files:
- { isarchive: True, - { isarchive: True,
attributes: Archive, attributes: Archive,
@ -315,6 +318,7 @@
expected: expected:
changed: False changed: False
examined: 8 examined: 8
failed: False
files: files:
- { isarchive: True, - { isarchive: True,
attributes: Archive, attributes: Archive,
@ -383,6 +387,7 @@
expected: expected:
changed: False changed: False
examined: 10 examined: 10
failed: False
files: files:
- { isarchive: True, - { isarchive: True,
attributes: Archive, attributes: Archive,
@ -465,6 +470,7 @@
expected: expected:
changed: False changed: False
examined: 2 examined: 2
failed: False
files: files:
- { isarchive: False, - { isarchive: False,
attributes: Directory, attributes: Directory,
@ -529,6 +535,7 @@
expected: expected:
changed: False changed: False
examined: 5 examined: 5
failed: False
files: files:
- { isarchive: True, - { isarchive: True,
attributes: Archive, attributes: Archive,
@ -571,6 +578,7 @@
expected: expected:
changed: False changed: False
examined: 5 examined: 5
failed: False
files: files:
- { isarchive: True, - { isarchive: True,
attributes: Archive, attributes: Archive,

@ -52,6 +52,7 @@
expected: expected:
changed: false changed: false
exists: true exists: true
failed: false
properties: properties:
binary: { raw_value: ["0x01", "0x16"], type: 'REG_BINARY', value: [1, 22] } binary: { raw_value: ["0x01", "0x16"], type: 'REG_BINARY', value: [1, 22] }
dword: { raw_value: 1, type: 'REG_DWORD', value: 1 } dword: { raw_value: 1, type: 'REG_DWORD', value: 1 }
@ -79,6 +80,7 @@
expected: expected:
changed: false changed: false
exists: true exists: true
failed: false
properties: properties:
none: { raw_value: [], type: 'REG_NONE', value: [] } none: { raw_value: [], type: 'REG_NONE', value: [] }
none1: { raw_value: ["0x00"], type: 'REG_NONE', value: [0] } none1: { raw_value: ["0x00"], type: 'REG_NONE', value: [0] }
@ -101,6 +103,7 @@
expected: expected:
changed: false changed: false
exists: true exists: true
failed: false
properties: {} properties: {}
sub_keys: [] sub_keys: []
register: expected register: expected
@ -120,6 +123,7 @@
expected: expected:
changed: false changed: false
exists: false exists: false
failed: false
- name: validate test - name: validate test
assert: assert:
@ -137,6 +141,7 @@
expected: expected:
changed: false changed: false
exists: true exists: true
failed: false
raw_value: 'test' raw_value: 'test'
type: 'REG_SZ' type: 'REG_SZ'
value: 'test' value: 'test'
@ -157,6 +162,7 @@
expected: expected:
changed: false changed: false
exists: true exists: true
failed: false
raw_value: '%windir%\dir' raw_value: '%windir%\dir'
type: 'REG_EXPAND_SZ' type: 'REG_EXPAND_SZ'
value: "{{win_dir_value.stdout_lines[0]}}\\dir" value: "{{win_dir_value.stdout_lines[0]}}\\dir"
@ -177,6 +183,7 @@
expected: expected:
changed: false changed: false
exists: true exists: true
failed: false
raw_value: ['a, b', 'c'] raw_value: ['a, b', 'c']
type: 'REG_MULTI_SZ' type: 'REG_MULTI_SZ'
value: ['a, b', 'c'] value: ['a, b', 'c']
@ -197,6 +204,7 @@
expected: expected:
changed: false changed: false
exists: true exists: true
failed: false
raw_value: ["0x01", "0x16"] raw_value: ["0x01", "0x16"]
type: 'REG_BINARY' type: 'REG_BINARY'
value: [1, 22] value: [1, 22]
@ -217,6 +225,7 @@
expected: expected:
changed: false changed: false
exists: true exists: true
failed: false
raw_value: 1 raw_value: 1
type: 'REG_DWORD' type: 'REG_DWORD'
value: 1 value: 1
@ -237,6 +246,7 @@
expected: expected:
changed: false changed: false
exists: true exists: true
failed: false
raw_value: 1 raw_value: 1
type: 'REG_QWORD' type: 'REG_QWORD'
value: 1 value: 1
@ -257,6 +267,7 @@
expected: expected:
changed: false changed: false
exists: true exists: true
failed: false
raw_value: [] raw_value: []
type: 'REG_NONE' type: 'REG_NONE'
value: [] value: []
@ -277,6 +288,7 @@
expected: expected:
changed: false changed: false
exists: true exists: true
failed: false
raw_value: ["0x00"] raw_value: ["0x00"]
type: 'REG_NONE' type: 'REG_NONE'
value: [0] value: [0]
@ -297,6 +309,7 @@
expected: expected:
changed: false changed: false
exists: false exists: false
failed: false
- name: validate test - name: validate test
assert: assert:

@ -81,7 +81,7 @@ class TestAnsibleModuleExitJson(unittest.TestCase):
else: else:
self.assertEquals(ctx.exception.code, 1) self.assertEquals(ctx.exception.code, 1)
return_val = json.loads(self.fake_stream.getvalue()) return_val = json.loads(self.fake_stream.getvalue())
self.assertEquals(return_val, dict(msg="message", failed=True, invocation=empty_invocation)) self.assertEquals(return_val, dict(msg="message", changed=False, failed=True, invocation=empty_invocation))
def test_exit_json_proper_changed(self): def test_exit_json_proper_changed(self):
with self.assertRaises(SystemExit) as ctx: with self.assertRaises(SystemExit) as ctx:
@ -143,7 +143,6 @@ class TestAnsibleModuleExitValuesRemoved(unittest.TestCase):
self.maxDiff = None self.maxDiff = None
for args, return_val, expected in self.dataset: for args, return_val, expected in self.dataset:
expected = copy.deepcopy(expected) expected = copy.deepcopy(expected)
del expected['changed']
expected['failed'] = True expected['failed'] = True
params = dict(ANSIBLE_MODULE_ARGS=args) params = dict(ANSIBLE_MODULE_ARGS=args)
params = json.dumps(params) params = json.dumps(params)

Loading…
Cancel
Save