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 role dependencies are not passing variables as expected #68922

Closed
jamesfreeman959 opened this issue Apr 13, 2020 · 9 comments
Closed
Labels
affects_2.9 This issue/PR affects Ansible v2.9 docs This issue/PR relates to or includes documentation. support:core This issue/PR relates to code supported by the Ansible Engineering Team.

Comments

@jamesfreeman959
Copy link

jamesfreeman959 commented Apr 13, 2020

SUMMARY

Ansible role dependencies are not passing variables as expected

ISSUE TYPE
  • Bug Report
  • Documentation Report
COMPONENT NAME

Role dependencies

ANSIBLE VERSION
ansible 2.9.6
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.5 (default, Aug  7 2019, 00:51:29) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]
CONFIGURATION

OS / ENVIRONMENT

CentOS 7.7.1908 64 bit running on libvirt

STEPS TO REPRODUCE

Create the car, wheel, tire and brake roles as described here: https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html

Run a playbook calling the roles

For example, a role named car depends on a role named wheel as follows:

---
dependencies:
  - role: wheel
    vars:
      n: 1
  - role: wheel
    vars:
      n: 2
  - role: wheel
    vars:
      n: 3
  - role: wheel
    vars:
      n: 4
And the wheel role depends on two roles: tire and brake. The meta/main.yml for wheel would then contain the following:

---
dependencies:
  - role: tire
  - role: brake
And the meta/main.yml for tire and brake would contain the following:

---
allow_duplicates: true
EXPECTED RESULTS

The documentation states:

The resulting order of execution would be as follows:

tire(n=1)
brake(n=1)
wheel(n=1)
tire(n=2)
brake(n=2)
wheel(n=2)
...
car

ACTUAL RESULTS

I am seeing:

tire(n=4)
brake(n=4)
wheel(n=4)
tire(n=4)
brake(n=4)
wheel(n=4)
...
car

$ ansible-playbook site.yml
[WARNING]: provided hosts list is empty, only localhost is available. Note that
the implicit localhost does not match 'all'

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

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [tire : debug] ************************************************************
ok: [localhost] => {
    "n": 4
}

TASK [brake : debug] ***********************************************************
ok: [localhost] => {
    "n": 4
}

TASK [tire : debug] ************************************************************
ok: [localhost] => {
    "n": 4
}

TASK [brake : debug] ***********************************************************
ok: [localhost] => {
    "n": 4
}

TASK [tire : debug] ************************************************************
ok: [localhost] => {
    "n": 4
}

TASK [brake : debug] ***********************************************************
ok: [localhost] => {
    "n": 4
}

TASK [tire : debug] ************************************************************
ok: [localhost] => {
    "n": 4
}

TASK [brake : debug] ***********************************************************
ok: [localhost] => {
    "n": 4
}

PLAY RECAP *********************************************************************
localhost                  : ok=9    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
@ansibot
Copy link
Contributor

ansibot commented Apr 13, 2020

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.9 This issue/PR affects Ansible v2.9 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 Apr 13, 2020
@sivel
Copy link
Member

sivel commented Apr 13, 2020

The problem here is a misunderstanding about how role variables are scoped as well as how roles are processed. Role variables are scoped at the play level, and roles are all parsed and merged into the play at playbook parsing time. As a result, the last defined variable from vars overwrites earlier values.

This can be disabled through the config with https://docs.ansible.com/ansible/latest/reference_appendices/config.html#default-private-role-vars

Otherwise, you can switch from role vars to role params:

  - role: wheel
    n: 1
  - role: wheel
    n: 2
  - role: wheel
    n: 3
  - role: wheel
    n: 4

Role params are scoped to the role, and not the play.

I won't close this, as the documentation is incorrect, and needs to be updated accordingly.

@sivel sivel added docs This issue/PR relates to or includes documentation. and removed bug This issue/PR relates to a bug. labels Apr 13, 2020
@liuyangc3
Copy link

liuyangc3 commented Apr 14, 2020

another question does the role dependencies behavior is the same as import_role or include_role, or it's own logic? @sivel

@jamesfreeman959
Copy link
Author

jamesfreeman959 commented Apr 14, 2020

Thanks so much for the quick reply - this is really useful to know and glad that the documentation can be updated as a result of this. One query however - I just tested with ANSIBLE_PRIVATE_ROLE_VARS set to true, and the output now returns:

# ANSIBLE_PRIVATE_ROLE_VARS=true ansible-playbook site.yml
[WARNING]: provided hosts list is empty, only localhost is available. Note that
the implicit localhost does not match 'all'

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

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [tire : debug] ************************************************************
ok: [localhost] => {
    "n": "VARIABLE IS NOT DEFINED!"
}

TASK [brake : debug] ***********************************************************
ok: [localhost] => {
    "n": "VARIABLE IS NOT DEFINED!"
}

TASK [tire : debug] ************************************************************
ok: [localhost] => {
    "n": "VARIABLE IS NOT DEFINED!"
}

TASK [brake : debug] ***********************************************************
ok: [localhost] => {
    "n": "VARIABLE IS NOT DEFINED!"
}

TASK [tire : debug] ************************************************************
ok: [localhost] => {
    "n": "VARIABLE IS NOT DEFINED!"
}

TASK [brake : debug] ***********************************************************
ok: [localhost] => {
    "n": "VARIABLE IS NOT DEFINED!"
}

TASK [tire : debug] ************************************************************
ok: [localhost] => {
    "n": "VARIABLE IS NOT DEFINED!"
}

TASK [brake : debug] ***********************************************************
ok: [localhost] => {
    "n": "VARIABLE IS NOT DEFINED!"
}

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

Surely this is not as intended either?

I did test with parameters and this works as you suggested, so just curious about the above parameter setting.

@sivel
Copy link
Member

sivel commented Apr 14, 2020

@liuyangc3 the roles: header is equivalent to using import_role

@sivel
Copy link
Member

sivel commented Apr 14, 2020

ANSIBLE_PRIVATE_ROLE_VARS=true restricts the vars to only the associated role, they are not accessible from role dependencies.

The other option is switching to use include_role in tasks/main.yml instead of dependencies:

- include_role:
    name: wheel
  vars:
    n: 1

- include_role:
    name: wheel
  vars:
    n: 2

- include_role:
    name: wheel
  vars:
    n: 3

- include_role:
    name: wheel
  vars:
    n: 4

@jamesfreeman959
Copy link
Author

Awesome - thanks @sivel - your answers have really helped me. Looking forward to seeing the updated documentation page - if you want me to review or help with anything please do let me know.

@bcoca
Copy link
Member

bcoca commented Apr 23, 2020

possibly fixed by #69040

@bcoca bcoca removed the needs_triage Needs a first human triage before being processed. label Apr 23, 2020
@scottselberg
Copy link

This was helpful. I setup a role to get secrets from HashiCorp vault. In some situations it was being inserted via include_role and other times via a role dependency. Sometimes I was getting the behavior I expected and sometime my variables were just set to the value of the last call. Using the role param syntax vs role vars has cleaned up the issue.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
affects_2.9 This issue/PR affects Ansible v2.9 docs This issue/PR relates to or includes documentation. support:core This issue/PR relates to code supported by the Ansible Engineering Team.
Projects
None yet
Development

No branches or pull requests

6 participants