Repository: ansible/ansible

Issue:
"always" and "rescue" clauses should not be allowed without "block"
<!---
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
<!--- Pick one below and delete the rest: -->
 - Feature Idea

##### COMPONENT NAME
<!--- Name of the module/plugin/task/feature -->
playbook

##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
```
ansible 2.3.1.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides
  python version = 2.7.6 (default, Oct 26 2016, 20:30:19) [GCC 4.8.4]
```

##### SUMMARY
<!--- Explain the problem briefly -->
I think it's no reason to use an `always` or `rescue` clauses without `block`.
However, this may happen by mistake and does not cause a parsing error.

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->

<!--- Paste example playbooks or commands between quotes below -->
```yaml
- hosts: all
  tasks:
  - block:
    fail: when=true
  - always:
    debug: msg=OK
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
```
ERROR! 'always' clause without 'block' encountered.

The error appears to have been in '/tmp/a.yml': line 5, column 5, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

    - fail: when=true
  - always:
    ^ here
```

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes below -->
```
(...)
< TASK [fail] >
 -------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

fatal: [localhost]: FAILED! => {"changed": false, "failed": true, "msg": "Failed as requested from task"}
        to retry, use: --limit @/tmp/a.retry
 ____________
< PLAY RECAP >
 ------------
(...)
```



Discussion:
I don't know if such approach is correct but seems to work:
```diff
diff --git lib/ansible/playbook/block.py lib/ansible/playbook/block.py
index c576250..6090320 100644
--- lib/ansible/playbook/block.py
+++ lib/ansible/playbook/block.py
@@ -146,20 +146,28 @@ class Block(Base, Become, Conditional, Taggable):
                 block=self,
                 role=self._role,
                 task_include=None,
                 variable_manager=self._variable_manager,
                 loader=self._loader,
                 use_handlers=self._use_handlers,
             )
         except AssertionError:
             raise AnsibleParserError("A malformed block was encountered.", obj=self._ds)
 
+    def _validate_always(self, attr, name, value):
+        if self.always and not self.block:
+            raise AnsibleParserError("'always' clause without 'block' encountered.", obj=self._ds)
+
+    def _validate_rescue(self, attr, name, value):
+        if self.rescue and not self.block:
+            raise AnsibleParserError("'rescue' clause without 'block' encountered.", obj=self._ds)
+
     def get_dep_chain(self):
         if self._dep_chain is None:
             if self._parent:
                 return self._parent.get_dep_chain()
             else:
                 return None
         else:
             return self._dep_chain[:]
 
     def copy(self, exclude_parent=False, exclude_tasks=False):
```
can you give an actual example, the one above fails with:
```
ERROR! 'fail' is not a valid attribute for a Block

The error appears to have been in '/home/bcoca/work/ansible/play.yml': line 4, column 5, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

  tasks:
  - block:
    ^ here

```
going to assume you mean this:
```
- hosts: localhost
  gather_facts: false
  tasks:
  - block:
     - fail: when=true
  - always:
     - debug: msg=OK
```
which does give the error you point out, the patch looks good to fix this, want to send it in as a pull request?
@maciejkowalczyk9ld that patch seems fine, as bcoca says send it to us in a PR whenever you're able.

Resolving patch:
diff --git a/lib/ansible/playbook/block.py b/lib/ansible/playbook/block.py
--- a/lib/ansible/playbook/block.py
+++ b/lib/ansible/playbook/block.py
@@ -161,6 +161,12 @@ def _load_always(self, attr, ds):
         except AssertionError as e:
             raise AnsibleParserError("A malformed block was encountered while loading always", obj=self._ds, orig_exc=e)
 
+    def _validate_always(self, attr, name, value):
+        if value and not self.block:
+            raise AnsibleParserError("'%s' keyword cannot be used without 'block'" % name, obj=self._ds)
+
+    _validate_rescue = _validate_always
+
     def get_dep_chain(self):
         if self._dep_chain is None:
             if self._parent:


Test patch: