Repository: ansible/ansible

Issue:
Role default variables are not available in static task imports inside a role

##### SUMMARY

Role default variables cannot be used as arguments to  `import_tasks` inside a role. Role defaults are available for conditionals, task arguments, and `include_tasks`.

##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
`import_role`

##### ANSIBLE VERSION
<!--- Paste, BELOW THIS COMMENT, verbatim output from "ansible --version" between quotes below -->
```
ansible 2.5.2
  config file = /Users/sdoran/.ansible.cfg
  configured module search path = ['/Users/sdoran/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /Users/sdoran/.pyenv/versions/3.6.4/lib/python3.6/site-packages/ansible
  executable location = /Users/sdoran/.pyenv/versions/3.6.4/bin/ansible
  python version = 3.6.4 (default, Mar 19 2018, 12:20:18) [GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)]
```

##### CONFIGURATION
```
CACHE_PLUGIN(/Users/sdoran/.ansible.cfg) = jsonfile
CACHE_PLUGIN_CONNECTION(/Users/sdoran/.ansible.cfg) = ~/.cache/facts
DEFAULT_BECOME(/Users/sdoran/.ansible.cfg) = False
DEFAULT_BECOME_ASK_PASS(/Users/sdoran/.ansible.cfg) = False
DEFAULT_BECOME_METHOD(/Users/sdoran/.ansible.cfg) = sudo
DEFAULT_BECOME_USER(/Users/sdoran/.ansible.cfg) = root
DEFAULT_FORKS(/Users/sdoran/.ansible.cfg) = 50
DEFAULT_GATHERING(/Users/sdoran/.ansible.cfg) = smart
DEFAULT_HOST_LIST(/Users/sdoran/.ansible.cfg) = ['/Users/sdoran/Projects/inventory']
DEFAULT_MANAGED_STR(/Users/sdoran/.ansible.cfg) = Ansible managed. Any local changes will be overwritten.
DEFAULT_REMOTE_USER(/Users/sdoran/.ansible.cfg) = sdoran
DEFAULT_ROLES_PATH(env: ANSIBLE_ROLES_PATH) = ['/Users', '/Users/sdoran/Projects/roles', '/Users/sdoran/Projects/Ansible/roles']
DEFAULT_VAULT_PASSWORD_FILE(env: ANSIBLE_VAULT_PASSWORD_FILE) = /Users/sdoran/bin/getvaultkey.sh
HOST_KEY_CHECKING(/Users/sdoran/.ansible.cfg) = False
PERSISTENT_COMMAND_TIMEOUT(/Users/sdoran/.ansible.cfg) = 900
RETRY_FILES_ENABLED(/Users/sdoran/.ansible.cfg) = False
```

##### OS / ENVIRONMENT
```
ProductName:	Mac OS X
ProductVersion:	10.13.4
BuildVersion:	17E202
```

##### STEPS TO REPRODUCE
Run `play.yml` in this [example repository](https://github.com/samdoran/role-default-static-import-reproducer)

##### EXPECTED RESULTS

The playbook will run the role using values defined inside `defaults/main.yml` inside the role if the variable is not set by any other means. This will include `tasks1.yml` by default

```
PLAY [localhost] ***************************************************************

TASK [example_role : debug] ****************************************************
ok: [localhost] => {
    "msg": "In tasks1.yml"
}

PLAY RECAP *********************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0
```

##### ACTUAL RESULTS
 The role defaults are not available inside the role for `import_tasks` and an error message is thrown.

The variable must be set in group/host_vars or extra vars in order to work. Using the `default()` filter on the `import_tasks` task also does not work and reports the same error.

```
ERROR! Error when evaluating variable in include name: {{ tasks_file_name }}.yml.

When using static includes, ensure that any variables used in their names are defined in vars/vars_files
or extra-vars passed in from the command line. Static includes cannot use variables from inventory
sources like group or host vars.
```



Discussion:
Files identified in the description:
* [lib/ansible/modules/utilities/logic/import_role.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/utilities/logic/import_role.py)

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 --->

Resolving patch:
diff --git a/lib/ansible/playbook/role/__init__.py b/lib/ansible/playbook/role/__init__.py
--- a/lib/ansible/playbook/role/__init__.py
+++ b/lib/ansible/playbook/role/__init__.py
@@ -197,6 +197,19 @@ def _load_role_data(self, role_include, parent_role=None):
                 if os.path.isdir(plugin_path):
                     obj.add_directory(plugin_path)
 
+        # vars and default vars are regular dictionaries
+        self._role_vars = self._load_role_yaml('vars', main=self._from_files.get('vars'), allow_dir=True)
+        if self._role_vars is None:
+            self._role_vars = dict()
+        elif not isinstance(self._role_vars, dict):
+            raise AnsibleParserError("The vars/main.yml file for role '%s' must contain a dictionary of variables" % self._role_name)
+
+        self._default_vars = self._load_role_yaml('defaults', main=self._from_files.get('defaults'), allow_dir=True)
+        if self._default_vars is None:
+            self._default_vars = dict()
+        elif not isinstance(self._default_vars, dict):
+            raise AnsibleParserError("The defaults/main.yml file for role '%s' must contain a dictionary of variables" % self._role_name)
+
         # load the role's other files, if they exist
         metadata = self._load_role_yaml('meta')
         if metadata:
@@ -222,19 +235,6 @@ def _load_role_data(self, role_include, parent_role=None):
                 raise AnsibleParserError("The handlers/main.yml file for role '%s' must contain a list of tasks" % self._role_name,
                                          obj=handler_data, orig_exc=e)
 
-        # vars and default vars are regular dictionaries
-        self._role_vars = self._load_role_yaml('vars', main=self._from_files.get('vars'), allow_dir=True)
-        if self._role_vars is None:
-            self._role_vars = dict()
-        elif not isinstance(self._role_vars, dict):
-            raise AnsibleParserError("The vars/main.yml file for role '%s' must contain a dictionary of variables" % self._role_name)
-
-        self._default_vars = self._load_role_yaml('defaults', main=self._from_files.get('defaults'), allow_dir=True)
-        if self._default_vars is None:
-            self._default_vars = dict()
-        elif not isinstance(self._default_vars, dict):
-            raise AnsibleParserError("The defaults/main.yml file for role '%s' must contain a dictionary of variables" % self._role_name)
-
     def _load_role_yaml(self, subdir, main=None, allow_dir=False):
         file_path = os.path.join(self._role_path, subdir)
         if self._loader.path_exists(file_path) and self._loader.is_directory(file_path):


Test patch: