Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Block with rescue stops next plays and end playbook successfully on some errors #73657

Closed
lucasbasquerotto opened this issue Feb 19, 2021 · 6 comments · Fixed by #73722
Closed
Labels
affects_2.10 This issue/PR affects Ansible v2.10 bug This issue/PR relates to a bug. python3 support:core This issue/PR relates to code supported by the Ansible Engineering Team.

Comments

@lucasbasquerotto
Copy link

lucasbasquerotto commented Feb 19, 2021

SUMMARY

Depending on the error that occurs when a block with rescue catches it, the play execution stops, and the playbook ends successfuly, but the next plays don't run. I was able to simulate it using include_tasks, but the task inside use a module with a wrong name (that can be provided by a user, so not something that I have control, because it might not be me running the play, the user may include and run custom tasks).

This is not a problem if the module with wrong name is in the main playbook file, because the playbook fails at compilation time, but when lazily loaded through an include_tasks, the task fails, the rescue (wrapping the include_tasks) catches the error successfully, but the next plays don't run, and the playbook ends successfuly.

ISSUE TYPE
  • Bug Report
COMPONENT NAME

block

ANSIBLE VERSION
ansible 2.10.5
  config file = None
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.6/dist-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 3.6.9 (default, Oct  8 2020, 12:12:24) [GCC 8.4.0]
CONFIGURATION

(ansible-config dump --only-changed outputs nothing)

OS / ENVIRONMENT

Ubuntu 18.04 (docker container)

STEPS TO REPRODUCE

hosts.yml:

[main]
localhost ansible_connection=local

playbook.yml:

- name: Test 01
  hosts: main
  tasks:
    - block:
        - include_tasks: "test.yml"
      rescue:
        - debug:
            msg: "rescued"

    - debug:
        msg: "test 01"

- name: Test 02
  hosts: main
  tasks:
    - debug:
        msg: "test 02"

    - fail:
        msg: "end here"

Works:

test.yml (gives an error, but works as expected):

- ansible.builtin.file:
    param: "value"

Outputs:

PLAY [Test 01] ************************************************************************************************************************

TASK [include_tasks] ******************************************************************************************************************
included: /main/dev/repos/cloud/test.yml for localhost

TASK [ansible.builtin.file] ***********************************************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": "Unsupported parameters for (ansible.builtin.file) module: param Supported parameters include: _diff_peek, _original_basename, access_time, access_time_format, attributes, follow, force, group, mode, modification_time, modification_time_format, owner, path, recurse, selevel, serole, setype, seuser, src, state, unsafe_writes"}

TASK [debug] **************************************************************************************************************************
ok: [localhost] => {
    "msg": "rescued"
}

TASK [debug] **************************************************************************************************************************
ok: [localhost] => {
    "msg": "test 01"
}

PLAY [Test 02] ************************************************************************************************************************

TASK [debug] **************************************************************************************************************************
ok: [localhost] => {
    "msg": "test 02"
}

TASK [fail] ***************************************************************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": "end here"}

PLAY RECAP ****************************************************************************************************************************
localhost                  : ok=4    changed=0    unreachable=0    failed=1    skipped=0    rescued=1    ignored=0   

Bug:

test.yml (gives an error, stops the next plays, ends the playbook successfully, as explained above):

- wrong.collection.module:
    param: "value"

Outputs:

PLAY [Test 01] ************************************************************************************************************************

TASK [include_tasks] ******************************************************************************************************************
fatal: [localhost]: FAILED! => {"reason": "couldn't resolve module/action 'wrong.collection.module'. This often indicates a misspelling, missing collection, or incorrect module path.\n\nThe error appears to be in '/main/dev/repos/cloud/test.yml': line 1, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- wrong.collection.module:\n  ^ here\n"}

TASK [debug] **************************************************************************************************************************
ok: [localhost] => {
    "msg": "rescued"
}

TASK [debug] **************************************************************************************************************************
ok: [localhost] => {
    "msg": "test 01"
}

PLAY RECAP ****************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   
EXPECTED RESULTS

The same as in the case that works.

ACTUAL RESULTS

Stop the next plays, even after being rescued, and end the playbook successfully (if I run ansible-playbook playbook.yml && echo "success" || echo "error" it gives an error (due to the fail task at the end) in the 1st case (that works), but success in the 2nd, and don't run the 2nd play).

@ansibot
Copy link
Contributor

ansibot commented Feb 19, 2021

Files identified in the description:

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

click here for bot help

@ansibot ansibot added affects_2.10 This issue/PR affects Ansible v2.10 bug This issue/PR relates to a bug. needs_triage Needs a first human triage before being processed. python3 support:core This issue/PR relates to code supported by the Ansible Engineering Team. labels Feb 19, 2021
@samdoran samdoran removed the needs_triage Needs a first human triage before being processed. label Feb 23, 2021
@samdoran
Copy link
Contributor

An incorrect module name is a syntax error. Rescue does not apply to syntax errors, but we'll look into a but further just to make sure this isn't a bug.

@lucasbasquerotto
Copy link
Author

lucasbasquerotto commented Feb 23, 2021

I don't mind too much the playbook stopping if there is a syntax error (although it's not what I want too), but there are some aspects that makes it clearly a bug:

  1. The playbook is not stopped at compile time if is called in an include task (so a rescue outside it should catch). It fails only after it loads the task, which is expected, but then it should fail only the task, if there is a rescue task outside.
  2. The rescue outside the task actually catches the error and the play continues executing (like demonstrated above). Only the next plays don't run.
  3. The ansible-playbook command ends successfully, even tough the plays after the one that gave the error weren't executed.

It should either stop the current play immediately and fail (not what I want, but I would be ok with it), or rescue and continue all the next plays (which is what I want).

But it does neither one nor the other, it continues the play, don't run the next plays (stops abruptly at the end of the play), and ends the execution partially with a success status.

@bcoca
Copy link
Member

bcoca commented Feb 23, 2021

  1. It cannot stop at 'compile' time since you use includes, this creates a 'dynamic syntax issue'
  2. that might be a bug, rescue should not work on syntax issues, but it is probably working on the include build itself which is a task that 'fails' since it cannot produce valid tasks as a result, so we probably need to allow the original syntax issue to come up to be handled correctly
  3. tied to # 2

@mkrizek
Copy link
Contributor

mkrizek commented Feb 24, 2021

Since this is a parsing error the play execution should not continue and we should probably mimic the behavior of import_tasks here, minimal diff:

diff --git a/lib/ansible/plugins/strategy/__init__.py b/lib/ansible/plugins/strategy/__init__.py
index eaa7c0cb20..96910301b1 100644
--- a/lib/ansible/plugins/strategy/__init__.py
+++ b/lib/ansible/plugins/strategy/__init__.py
@@ -954,7 +954,8 @@ class StrategyBase:
             # first processed, we do so now for each host in the list
             for host in included_file._hosts:
                 self._tqm._stats.increment('ok', host.name)
-
+        except AnsibleParserError:
+            raise
         except AnsibleError as e:
             if isinstance(e, AnsibleFileNotFound):
                 reason = "Could not find or access '%s' on the Ansible Controller." % to_text(e.file_name)
diff --git a/lib/ansible/plugins/strategy/free.py b/lib/ansible/plugins/strategy/free.py
index 7e0406984d..653291b327 100644
--- a/lib/ansible/plugins/strategy/free.py
+++ b/lib/ansible/plugins/strategy/free.py
@@ -34,7 +34,7 @@ DOCUMENTATION = '''
 import time
~
 from ansible import constants as C
-from ansible.errors import AnsibleError
+from ansible.errors import AnsibleError, AnsibleParserError
 from ansible.playbook.included_file import IncludedFile
 from ansible.plugins.loader import action_loader
 from ansible.plugins.strategy import StrategyBase
@@ -255,6 +255,8 @@ class StrategyModule(StrategyBase):
                             )
                         else:
                             new_blocks = self._load_included_file(included_file, iterator=iterator)
+                    except AnsibleParserError:
+                        raise
                     except AnsibleError as e:
                         for host in included_file._hosts:
                             iterator.mark_host_failed(host)
diff --git a/lib/ansible/plugins/strategy/linear.py b/lib/ansible/plugins/strategy/linear.py
index d22f03e9f0..a345cf1f77 100644
--- a/lib/ansible/plugins/strategy/linear.py
+++ b/lib/ansible/plugins/strategy/linear.py
@@ -32,7 +32,7 @@ DOCUMENTATION = '''
 '''
~
 from ansible import constants as C
-from ansible.errors import AnsibleError, AnsibleAssertionError
+from ansible.errors import AnsibleError, AnsibleAssertionError, AnsibleParserError
 from ansible.executor.play_iterator import PlayIterator
 from ansible.module_utils.six import iteritems
 from ansible.module_utils._text import to_text
@@ -383,7 +383,8 @@ class StrategyModule(StrategyBase):
                                     else:
                                         all_blocks[host].append(noop_block)
                             display.debug("done iterating over new_blocks loaded from include file")
-
+                        except AnsibleParserError:
+                            raise
                         except AnsibleError as e:
                             for host in included_file._hosts:
                                 self._tqm._failed_hosts[host.name] = True

Then the error is fatal:

PLAY [localhost] *************************************************************************************************************************

TASK [include_tasks] *********************************************************************************************************************
ERROR! couldn't resolve module/action 'wrong.collection.module'. This often indicates a misspelling, missing collection, or incorrect module path.

The error appears to be in '/Users/mkrizek/src/ansible-issues/73657-tasks.yml': line 1, column 3, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:


- wrong.collection.module:
  ^ here

@lucasbasquerotto
Copy link
Author

About number 1, the fact that is dynamic is exactly the reason that makes me expect it to not error the entire execution. With an import task I would probably receive an error before the playbook start executing (I have not tested, but I guess it what happens).

If I have tasks at the final play that destroy any resource created at the beginning, I would expect it to run if some task included in the middle of the execution gave a syntax error (inside the task, after the resources were already created in previous steps), but had a rescue block to catch it (I would still end the play with an error at the end of everything with a fail task, after the resouces were liberated).

That said, I would still be ok if it failed immediately and returned an error (solve 2 and 3).

@Shrews Shrews removed their assignment Feb 24, 2021
mkrizek added a commit to mkrizek/ansible that referenced this issue Feb 25, 2021
mkrizek added a commit to mkrizek/ansible that referenced this issue Nov 1, 2021
bcoca pushed a commit that referenced this issue Nov 1, 2021
* Parser errors from within includes should not be rescueable
* Also fixes unit tests
Fixes #73657
@ansible ansible locked and limited conversation to collaborators Nov 22, 2021
bcoca pushed a commit to bcoca/ansible that referenced this issue Feb 7, 2022
…73722)

* Parser errors from within includes should not be rescueable
* Also fixes unit tests
Fixes ansible#73657
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
affects_2.10 This issue/PR affects Ansible v2.10 bug This issue/PR relates to a bug. python3 support:core This issue/PR relates to code supported by the Ansible Engineering Team.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants