Repository: ansible/ansible

Issue:
win_chocolatey not respecting expected exit codes such as 3010
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and master branch are affected too.
-->

##### ISSUE TYPE
 - Bug Report


##### COMPONENT NAME
- win_chocolatey

##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
```
ansible 2.3.0.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides
  python version = 2.7.5 (default, Aug  2 2016, 04:20:16) [GCC 4.8.5 20150623 (Red Hat 4.8.5-4)]

```

##### CONFIGURATION



##### OS / ENVIRONMENT
- Running from RHEL 7
- Managing Windows Server 2016 OS on a HP Blade (ProLiant BL460c Gen9)

##### SUMMARY
The chocolatey package returns 3010 which is expected "Reboot required". When ansible invokes win_chocolatey it returns failed even though this return code is expected. I can confirm this was not the behavior seen when using ansible  2.2.1.0. 

The win_chocolatey module specifies 3010 as a successexitcode on line 57 and the logic of the module seems to run without issue but only after the module has finished it is interpreted by ansible as a failure.
https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/windows/win_chocolatey.ps1 

##### STEPS TO REPRODUCE
To Reproduce this on demmand I have had to create a chocolatey package that emulates a package that requires reboot. I called this Return3010. This is the code for the chocolateyinstall.ps1 script:

```powershell
#Emulate a return of 3010 from an installer
$env:ChocolateyExitCode = 3010
```

No that I can reproduce the event I run:

<!--- Paste example playbooks or commands between quotes below -->
```bash
ansible hostname -i hosts -m win_chocolatey -a 'name="Return3010" state="present"'
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
I consistantly get this output when running on ansible 2.2.1.0
```
hostname | SUCCESS => {
    "changed": true
}

```

##### ACTUAL RESULTS
With -vvv
```

META: ran handlers
Using module file /usr/lib/python2.7/site-packages/ansible/modules/windows/win_chocolatey.ps1
<hostname> ESTABLISH WINRM CONNECTION FOR USER: None on PORT 5986 TO hostname
EXEC (via pipeline wrapper)
hostname | FAILED! => {
    "changed": true,
    "failed": true,
    "rc": 3010
}
```



Discussion:
cc @smadam813 @nitzmahone
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Thanks for reporting. Looking into this, I discovered that if a module did not explicitly returned "failed" (which Exit-Json did not do) it falls back on the rc value. Which I guess is asking for issues, like this one.

So we are adding an explicit `failed = $false` to ensure that the rc value is not considered for the evaluation.

The fix is part of #24867 

Resolving patch:
diff --git a/lib/ansible/executor/task_result.py b/lib/ansible/executor/task_result.py
--- a/lib/ansible/executor/task_result.py
+++ b/lib/ansible/executor/task_result.py
@@ -67,7 +67,7 @@ def is_failed(self):
            'results' in self._result and True in [True for x in self._result['results'] if 'failed_when_result' in x]:
             return self._check_key('failed_when_result')
         else:
-            return self._check_key('failed') or self._result.get('rc', 0) != 0
+            return self._check_key('failed')
 
     def is_unreachable(self):
         return self._check_key('unreachable')


Test patch:
diff --git a/test/units/executor/test_task_result.py b/test/units/executor/test_task_result.py
--- a/test/units/executor/test_task_result.py
+++ b/test/units/executor/test_task_result.py
@@ -125,11 +125,11 @@ def test_task_result_is_failed(self):
         tr = TaskResult(mock_host, mock_task, dict())
         self.assertFalse(tr.is_failed())
 
-        # test failed result with rc values
+        # test failed result with rc values (should not matter)
         tr = TaskResult(mock_host, mock_task, dict(rc=0))
         self.assertFalse(tr.is_failed())
         tr = TaskResult(mock_host, mock_task, dict(rc=1))
-        self.assertTrue(tr.is_failed())
+        self.assertFalse(tr.is_failed())
 
         # test with failed in result
         tr = TaskResult(mock_host, mock_task, dict(failed=True))