Repository: ansible/ansible

Issue:
ANSIBLE_STRATEGY=debug and ignore_errors: true does start debugger
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
code
##### ANSIBLE VERSION
<!--- Paste, BELOW THIS COMMENT, verbatim output from "ansible --version" between quotes below -->
```
2.4.2.0
```

##### CONFIGURATION
```
ANSIBLE_STRATEGY=debug 
```

##### OS / ENVIRONMENT
N/A

##### SUMMARY

`ANSIBLE_STRATEGY=debug` is a very useful Ansible feature that starts the interactive debugger when a task fails. 

This works well unless you encounter tasks that are expected to return failures and where the playbook writer added ``ignore_errors: true`` in order to continue.

Usually such a task looks like this (ignore possible alternative use of ansible module):

```yaml
- shell: cat foo | grep bar
  ignore_errors: true
  register: result
```

There is one workaround that works that I wrote about on https://medium.com/@sbarnea/why-ansible-ignore-errors-is-evil-500fb6e81229 that looks like:

```yaml
- shell: cat foo | grep bar
  failed_when: false
  register: result
```

Now there are few issues which the usage of the workaround:
* Use of ``ignore_errors: true`` is a **wild spread** usage pattern, where number of occurrences in single projects could easily be over 100s. 
* failed_when does change the the result to be a success which means that user can no longer rely on `result | success` filters to check the result. They have to switch to classic things like `result.rc == 0`
* most people do not even know about Ansible debugger which means that will never know about its side-effect of triggering a brakepoint. 
* CI systems never use a debug strategy so the debugger behaviour cannot be exposed by normal CI gates

##### EXPECTED RESULTS
When a task have `ignore_errors: true` it should not trigger the debugger, even if the final task execution would mark it as a failure.

If desired, this new behaviour could be enabled by some config option.


Discussion:
Files identified in the description:
None

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
This is already resolved in v2.5.  The debugger functionality was completely rewritten for the 2.5 release.

```
TASK [shell] *********************************************************************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"changed": true, "cmd": "cat foo | grep bar", "delta": "0:00:00.007750", "end": "2018-05-08 13:01:31.690302", "msg": "non-zero return code", "rc": 1, "start": "2018-05-08 13:01:31.682552", "stderr": "cat: foo: No such file or directory", "stderr_lines": ["cat: foo: No such file or directory"], "stdout": "", "stdout_lines": []}
...ignoring
[localhost] TASK: command (debug)> c
```

For more information on the debugger in 2.5, see http://docs.ansible.com/ansible/latest/user_guide/playbooks_debugger.html

If you have further questions please stop by IRC or the mailing list:

   * IRC: #ansible on irc.freenode.net
   * mailing list: https://groups.google.com/forum/#!forum/ansible-project 
Oops, I just re-read, you are advocating that `ignore_errors: True` should *NOT* invoke the debugger.

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
@@ -7,6 +7,7 @@
 
 from copy import deepcopy
 
+from ansible import constants as C
 from ansible.parsing.dataloader import DataLoader
 from ansible.vars.clean import strip_internal_keys
 
@@ -66,16 +67,17 @@ def is_unreachable(self):
 
     def needs_debugger(self, globally_enabled=False):
         _debugger = self._task_fields.get('debugger')
+        _ignore_errors = C.TASK_DEBUGGER_IGNORE_ERRORS and self._task_fields.get('ignore_errors')
 
         ret = False
-        if globally_enabled and (self.is_failed() or self.is_unreachable()):
+        if globally_enabled and ((self.is_failed() and not _ignore_errors) or self.is_unreachable()):
             ret = True
 
         if _debugger in ('always',):
             ret = True
         elif _debugger in ('never',):
             ret = False
-        elif _debugger in ('on_failed',) and self.is_failed():
+        elif _debugger in ('on_failed',) and self.is_failed() and not _ignore_errors:
             ret = True
         elif _debugger in ('on_unreachable',) and self.is_unreachable():
             ret = True


Test patch: