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

Ansible 2.x: variables were not passed to role #13617

Closed
ThomasSteinbach opened this issue Dec 21, 2015 · 14 comments
Closed

Ansible 2.x: variables were not passed to role #13617

ThomasSteinbach opened this issue Dec 21, 2015 · 14 comments
Labels
bug This issue/PR relates to a bug.
Milestone

Comments

@ThomasSteinbach
Copy link
Contributor

I recognized a somewhat strange behavior (bug), it took me some hours to figure out, what's wrong. Let me explain it by the following example. We have three roles, each role comprising one file as following:

/rolespath/role1/tasks/main.yml


---
- name: print variable
  debug: msg="{{ role1_variable }}"

/rolespath/role2/meta/main.yml


---
dependencies:
  - role: role1
    role1_variable: "{{ role2_variable }}"

/rolespath/role3/meta/main.yml


---
dependencies:
  - role: role2
    role2_variable: "from role3"

What is working

Now, when I call the role2 within a playbook, everything works as expected. The playbook...

- name: calling role2 works
  hosts: localhost
  gather_facts: no
  roles:
    - role: role2
      role2_variable: "from playbook"

... would lead to the following correct output:

PLAY [calling role 2 works] ***********************************

TASK [role1 : print variable] *********************************
ok: [localhost] => {
    "changed": false, 
    "msg": "from playbook"
}

What does not work

But when I call the role3, the playbook...

- name: calling role3 fails
  hosts: localhost
  gather_facts: no
  roles:
    - role: role3

... would lead to the following error output:

PLAY [calling role3 fails] ***********************************

TASK [role1 : print variable] ********************************
fatal: [localhost]: FAILED! => {"failed": true, "msg": "ERROR! ERROR! 'role2_variable' is undefined"}

Further description

It doesn't matter If you set the role2_variable within the third roles meta/main.yml directly, as shown in the example, or introduce a role3_variable. Even when you introduce a further role4 which calls role3 and setting the role3_variable (and even further roles in this scheme), the error remains that 'role2_variable' is undefined.

@Yannig
Copy link
Contributor

Yannig commented Dec 21, 2015

Looking after this issue, the following patch is fixing the current issue:

diff --git a/lib/ansible/vars/__init__.py b/lib/ansible/vars/__init__.py
index 1184ec5..65dfed3 100644
--- a/lib/ansible/vars/__init__.py
+++ b/lib/ansible/vars/__init__.py
@@ -308,7 +308,7 @@ class VariableManager:

             if not C.DEFAULT_PRIVATE_ROLE_VARS:
                 for role in play.get_roles():
-                    all_vars = combine_vars(all_vars, role.get_vars(include_params=False))
+                    all_vars = combine_vars(all_vars, role.get_vars(include_params=True))

         if task:
             if task._role:

Looking after the commit behind this change, I found a22f7b8 which fix the following issue: #12460

@Yannig
Copy link
Contributor

Yannig commented Dec 21, 2015

@jimi-c: what do you think about?

@ThomasSteinbach
Copy link
Contributor Author

Thank you @Yannig for your investigation. Your 'fix' would make my day as long as there is not the trick, satisfying both issues.

@Yannig
Copy link
Contributor

Yannig commented Dec 21, 2015

@ThomasSteinbach the problem is that, using this change, the previous issue raise again. So not very satisfying.

@ThomasSteinbach
Copy link
Contributor Author

Yes, what I meant is that it is just a temporary fix for me until there is a fix 'satisfying both issues' :)

@bcoca bcoca added this to the v2 milestone Dec 21, 2015
@jimi-c
Copy link
Member

jimi-c commented Dec 21, 2015

@ThomasSteinbach you could also try running with ANSIBLE_PRIVATE_ROLE_VARS=1 set in your environment (this is a new 2.0 only option), which may resolve this, as the given task will still see all of its variables from its parent role (and dependencies, I believe).

@Yannig
Copy link
Contributor

Yannig commented Dec 21, 2015

@jimi-c: using this variable does not change Ansible behaviour.

You can retrieve a copy at the following location: https://github.com/Yannig/ansible-issue-low-speed/tree/master/roles-issue

@jimi-c
Copy link
Member

jimi-c commented Dec 21, 2015

Yep, I'm looking into it to see if there's a way to fix it without breaking the previous issue.

@Yannig
Copy link
Contributor

Yannig commented Dec 21, 2015

@jimi-c The following patch is working with both issue:

diff --git a/lib/ansible/vars/__init__.py b/lib/ansible/vars/__init__.py
index 1184ec5..2a85489 100644
--- a/lib/ansible/vars/__init__.py
+++ b/lib/ansible/vars/__init__.py
@@ -308,6 +308,8 @@ class VariableManager:

             if not C.DEFAULT_PRIVATE_ROLE_VARS:
                 for role in play.get_roles():
+                    for dep in role.get_all_dependencies():
+                        all_vars = combine_vars(all_vars, dep._role_params)
                     all_vars = combine_vars(all_vars, role.get_vars(include_params=False))

         if task:

@Yannig
Copy link
Contributor

Yannig commented Dec 21, 2015

The bad thing is about accessing a private field.

@Yannig
Copy link
Contributor

Yannig commented Dec 21, 2015

A much cleaner version:

diff --git a/lib/ansible/playbook/role/__init__.py b/lib/ansible/playbook/role/__init__.py
index f308954..ce82573 100644
--- a/lib/ansible/playbook/role/__init__.py
+++ b/lib/ansible/playbook/role/__init__.py
@@ -265,6 +265,12 @@ class Role(Base, Become, Conditional, Taggable):
                 inherited_vars = combine_vars(inherited_vars, parent._role_params)
         return inherited_vars

+    def get_role_params(self):
+        params = {}
+        for dep in self.get_all_dependencies():
+            params = combine_vars(params, dep._role_params)
+        return params
+
     def get_vars(self, dep_chain=[], include_params=True):
         all_vars = self.get_inherited_vars(dep_chain, include_params=include_params)

diff --git a/lib/ansible/vars/__init__.py b/lib/ansible/vars/__init__.py
index 1184ec5..699333a 100644
--- a/lib/ansible/vars/__init__.py
+++ b/lib/ansible/vars/__init__.py
@@ -308,6 +308,7 @@ class VariableManager:

             if not C.DEFAULT_PRIVATE_ROLE_VARS:
                 for role in play.get_roles():
+                    all_vars = combine_vars(all_vars, role.get_role_params())
                     all_vars = combine_vars(all_vars, role.get_vars(include_params=False))

         if task:

@jimi-c jimi-c closed this as completed in a467490 Dec 21, 2015
@jimi-c
Copy link
Member

jimi-c commented Dec 21, 2015

@Yannig very nice, confirmed that does the trick so merged that in (giving you credit of course). Waiting for the full set of integration tests to run, and if they pass I'll merge this into stable-2.0 as well.

Thanks!

bcoca pushed a commit to bcoca/ansible that referenced this issue Dec 21, 2015
@Yannig
Copy link
Contributor

Yannig commented Dec 21, 2015

Thanks :)

@ThomasSteinbach
Copy link
Contributor Author

Thans too - works like a charm.

@ansibot ansibot added bug This issue/PR relates to a bug. and removed bug_report labels Mar 7, 2018
@ansible ansible locked and limited conversation to collaborators Apr 25, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug This issue/PR relates to a bug.
Projects
None yet
Development

No branches or pull requests

5 participants