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

variables defined with 'vars:' keyword below 'role:' have unexpected values when used in imported/included roles #50278

Closed
pilou- opened this issue Dec 23, 2018 · 17 comments
Assignees
Labels
affects_2.4 This issue/PR affects Ansible v2.4 bug This issue/PR relates to a bug. docs This issue/PR relates to or includes documentation. has_pr This issue has an associated PR. support:core This issue/PR relates to code supported by the Ansible Engineering Team.

Comments

@pilou-
Copy link
Contributor

pilou- commented Dec 23, 2018

SUMMARY

Variables defined with vars: keyword below role: have unexpected values when used in imported/included role.

I am not sure:

  • if this is a bug
  • or a poorly documented feature because:
    1. Are variables defined with vars keyword below role and role params the same thing ?
    2. 2.6 documentation switched from role params to role vars, implying that variables defined with vars keyword below role are role params (but behaviors differ, see ADDITIONAL INFO section)
    3. all vars keywords at role/task level share the same definition
ISSUE TYPE
  • Bug Report
COMPONENT NAME

role

ANSIBLE VERSION
since 2.4
CONFIGURATION

OS / ENVIRONMENT
STEPS TO REPRODUCE
  • playbook:
$ cat play.yml
---
- hosts: all
  gather_facts: false
  roles:
    - role: testrole
      vars:
        myvar: "{{ myvalue }}"
    - role: testrole
      vars:
        myvar: "www.{{ myvalue }}"
  • inventory all group_vars:
$ cat group_vars/all.yml
---
myvalue: blah
  • roles:
roles/
├── testrole
│   └── tasks
│       └── main.yml
└── testrole2
    └── tasks
        └── main.yml
  • testrole main.yml (note that using import_role here doesn't change anything):
$ cat roles/testrole/tasks/main.yml 
- name: include testrole2 ({{ myvar }})
  include_role:
    name: testrole2
  • testrole2 main.yml:
$ cat roles/testrole2/tasks/main.yml 
- name: "test task ({{ myvar }})"
  debug:
    msg: '{{ myvar }}'
EXPECTED RESULTS
ansible-playbook -i localhost, play.yml 

PLAY [all] ***

TASK [include testrole2 (blah)] ***

TASK [testrole2 : test task (blah)] ***
ok: [localhost] => {
    "msg": "blah"
}

TASK [include testrole2 (www.blah)] ***

TASK [testrole2 : test task (www.blah)] ***
ok: [localhost] => {
    "msg": "www.blah"
}
ACTUAL RESULTS
$ ansible-playbook -i localhost, play.yml 

PLAY [all] ***

TASK [include testrole2 (blah)] ***

TASK [testrole2 : test task (www.blah)] ***
ok: [localhost] => {
    "msg": "www.blah"
}

TASK [include testrole2 (www.blah)] ***

TASK [testrole2 : test task (www.blah)] ***
ok: [localhost] => {
    "msg": "www.blah"
}
ADDITIONAL INFORMATION

Note that output is the same that the one expected when using these playbooks:

- hosts: all
  gather_facts: false
  roles:
    - role: testrole
      myvar: "{{ myvalue }}"
    - role: testrole
      myvar: "www.{{ myvalue }}"
- hosts: all
  gather_facts: false
  tasks:
    - import_role:
        name: testrole
      vars: myvar: "{{ myvalue }}"
    - import_role:
        name: testrole
      vars:
        myvar: "www.{{ myvalue }}"
@ansibot
Copy link
Contributor

ansibot commented Dec 23, 2018

Files identified in the description:

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

@ansibot ansibot added affects_2.4 This issue/PR affects Ansible v2.4 bug This issue/PR relates to a bug. needs_triage Needs a first human triage before being processed. support:core This issue/PR relates to code supported by the Ansible Engineering Team. labels Dec 23, 2018
@sivel
Copy link
Member

sivel commented Jan 2, 2019

This is the expected behavior when using vars on a role under roles. vars is effectively treated the same as vars/main.yml and due to get_vars behavior when collecting vars for a play, these vars are overwritten with the vars defined on the last defined role.

As such, vars and role params are not the same, as one is effectively scoped to the role (role params) whereas vars is effectively scoped to the play.

You can set role vars as private to change this behavior: https://docs.ansible.com/ansible/latest/reference_appendices/config.html#default-private-role-vars

@pilou-
Copy link
Contributor Author

pilou- commented Jan 2, 2019

@sivel then should not the output result be

$ ansible-playbook -i localhost, play.yml 

PLAY [all] ***

TASK [include testrole2 (www.blah)] ***

TASK [testrole2 : test task (www.blah)] ***
ok: [localhost] => {
    "msg": "www.blah"
}

TASK [include testrole2 (www.blah)] ***

TASK [testrole2 : test task (www.blah)] ***
ok: [localhost] => {
    "msg": "www.blah"
}

?

In order to highlight the difference, here is the diff with the actual result:

 PLAY [all] ***
 
-TASK [include testrole2 (blah)] ***
+TASK [include testrole2 (www.blah)] ***
 
 TASK [testrole2 : test task (www.blah)] ***
 ok: [localhost] => {

@sivel
Copy link
Member

sivel commented Jan 2, 2019

then should not the output result be

Probably. It's probably some nuance that I don't see right now in regards to templating out the task name for callbacks.

@sivel
Copy link
Member

sivel commented Jan 3, 2019

This is happening due to the following code:

if task._role:
all_vars = combine_vars(all_vars, task._role.get_vars(task.get_dep_chain(), include_params=False))

When templating the name for callbacks in ansible/plugins/strategy/linear.py we call get_vars with the task which has task._role which overwrites 'myvar': 'www.{{ myvalue }}' with 'myvar': '{{ myvalue }}'. In this context it is the most specific value. Later when include_role for testrole2 happens, it is no longer the most specific.

@samdoran samdoran removed the needs_triage Needs a first human triage before being processed. label Jan 8, 2019
@samccann
Copy link
Contributor

We will need to document whatever the resolution to this issue is.

@pilou-
Copy link
Contributor Author

pilou- commented Apr 23, 2019

It was mentioned during an ansible-doc meeting:

15:56:13 <bcoca> actually i believe that is a bug
15:56:29 <bcoca> if set in vars: it should not persist across role invocations
15:56:45 <bcoca> so i dont think we need to document, but fix the issue
15:58:09 <Pilou> you mean that "role vars" should be the same than "role params" ?
16:01:20 <bcoca> no
16:01:30 <bcoca> role params would bleed, vars: should not
16:01:43 * bcoca really wants to kill role params ...
16:02:53 <samccann> so they are two different things, but perhaps the docs don't make that clear?
16:04:31 <bcoca> i dont think we've ever correctly documented role params

@acozine acozine added the docs This issue/PR relates to or includes documentation. label Apr 23, 2019
@acozine acozine self-assigned this Apr 26, 2019
@pcahyna
Copy link
Contributor

pcahyna commented Feb 5, 2020

As such, vars and role params are not the same, as one is effectively scoped to the role (role params) whereas vars is effectively scoped to the play.

What are actually role params? I thought that role params are just the vars: block supplied to the roles under roles keyword or import_role tasks. Is there another type of role params?

@pilou-
Copy link
Contributor Author

pilou- commented Feb 5, 2020

here is an example of role parameter:

- hosts: all
  roles:
    - role: testrole
      a_role_param: "{{ myvalue }}"

here is an example of role variable:

- hosts: all
  roles:
    - role: testrole
      vars:
        a_role_variable: "{{ myvalue }}"

Both expressions role params and role vars are listed on the variable precedence list.

@pcahyna
Copy link
Contributor

pcahyna commented Feb 5, 2020

yes, they are listed there, but their meaning is AFAICT not explained, so I always thought that role vars refer to the content of vars/main.yml and role params to vars: passed in role. The semantic difference between

  roles:
    - role: testrole
      a_role_param: "{{ myvalue }}"

and

  roles:
    - role: testrole
      vars:
        a_role_variable: "{{ myvalue }}"

is not explained, so even if one is aware of the former (I don't remember seeing it in the docs) one would assume that it is a syntactic sugar for the latter. Confusingly, the latter is called a parameter, not role variable:

Parameterized roles are useful.

If you are using a role and want to override a default, pass it as a parameter to the role like so:

roles:
   - role: apache
     vars:
        http_port: 8080

( source: https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#examples-of-where-to-set-a-variable )

@pcahyna
Copy link
Contributor

pcahyna commented Feb 5, 2020

@pilou- Anyway, thanks for the explanation.

@pcahyna
Copy link
Contributor

pcahyna commented Feb 5, 2020

I also see above that

16:01:30 <bcoca> role params would bleed, vars: should not

and

As such, vars and role params are not the same, as one is effectively scoped to the role (role params) whereas vars is effectively scoped to the play.

which seem to have opposite views of the semantics (if "bleed" means "leak out to the whole play").

@SimonHeimberg
Copy link
Contributor

SimonHeimberg commented Oct 28, 2020

Lets update the doc for this properly and soon.

  • Explain what is vars/main.yml,
    what is vars to import_role (task),
    what is vars to role (in playbook),
    what is role parameter passed directly to role (in playbook).
  • Write in documentation how it currently works (and include warnings about planned changes)
    or write how it should work and warn about current bugs.

Lets update this doc:

There is a lot of confusion about this:

@bcoca
Copy link
Member

bcoca commented Oct 30, 2020

@pcahyna they 'bleed' in the context that they get mixed and/or overridden by actual role parameters, not in the scope of other variables nor play objects. for example:

roles:
  - role: blah
    name: other
    vars: name  

since name is an alias for role the intention of it being a variable or trying to specify the role becomes unclear and can even cause a conflict. Same with vars or any other variable name you want to use that overlaps with actual keywords, though now we warn when we see those.

@bcoca
Copy link
Member

bcoca commented Oct 30, 2020

@SimonHeimberg vars should be the same across all playbook objects, something that is inherit to it and the contained play objects. This sadly is not always how it works, specially with roles, this is an attempt to fix that #69040

@pilou-
Copy link
Contributor Author

pilou- commented Oct 31, 2020

Should not the current behavior - unchanged since several versions - be documented ?

SimonHeimberg added a commit to SimonHeimberg/ansible that referenced this issue Dec 2, 2020
##### SUMMARY
Documentation playbooks_reuse_roles is more explicit about where role variables of a role are accessible.

##### ISSUE TYPE
- Docs Pull Request

+label: docsite_pr

improves ansible#50278
SimonHeimberg added a commit to SimonHeimberg/ansible that referenced this issue Dec 2, 2020
Clear usage of terms. Role parameters exist as well (see ansible#50278), but the example uses variables. So use the term variable.
SimonHeimberg added a commit to SimonHeimberg/ansible that referenced this issue Dec 3, 2020
Use terms clearly. Role parameters exist as well (see ansible#50278), but the example uses variables.
So use the term variable.
@ansibot ansibot added the has_pr This issue has an associated PR. label Jan 24, 2021
@s-hertel
Copy link
Contributor

s-hertel commented Mar 2, 2022

This was a long-standing bug with vars inheritance. This was fixed by #75034, so now all three example playbooks do the same thing.

Role params and role variables are not equivalent/just syntactic sugar though. There's a draft PR to document the differences here: #73939.

.. [5] Tasks in each role see their own role's vars if specified, or those from the role's dependencies. Tasks defined outside of a role see the last role's vars.
.. [6] Tasks in each role see their own role's params if specified, or those from the role's dependencies. Role params are not exposed outside of the role.
.. [7] Role params are not specified within vars: they are top level params specified as part of the role definition

@s-hertel s-hertel closed this as completed Mar 2, 2022
@ansible ansible locked and limited conversation to collaborators Mar 11, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
affects_2.4 This issue/PR affects Ansible v2.4 bug This issue/PR relates to a bug. docs This issue/PR relates to or includes documentation. has_pr This issue has an associated PR. support:core This issue/PR relates to code supported by the Ansible Engineering Team.
Projects
None yet
Development

No branches or pull requests

10 participants