Hello, group, I encountered a strange problem with variables when running a role. The role has some defaults and the variables are used throughout the plays. At some point in the tasks/mains playbook, there are included other tasks from the same directory. Those also include tasks from root directory. Everything seems fine but when the plays AFTER the included are executed then the variables are no longer valid (seems like they are taken from another role’s). None of the included tasks changes the variables in question. Seems like a bug? Or am I missing something.
Actually the problem shows up even when the included tasks do not include any other ones.
I’m not understanding the problem, can you post an acual example and also what versions you are using?
I'm not understanding the problem, can you post an acual example and also what versions you are using?
There are several roles with practically the same structures:
- role0
- defaults
- main.yml
- tasks
- main.yml
- task1.yml
- task2.yml
- etc.yml
- role1
- defaults
- main.yml
- tasks
- main.yml
- task1.yml
- task2.yml
- etc.yml
- role2
- defaults
- main.yml
- tasks
- main.yml
- task1.yml
- task2.yml
- etc.yml
and so on. There are other directories but "vars" are empty in all of them. Each role's "main" task uses variables defined in respective "defaults" but also includes other tasks from the same role and tasks directory. While default values are all fine as long as they are used before the first
- include?: task1.yml
they get their values overwritten with some defaults from another role (sic!) once finished processing the included task file. Now that looks to me like a bug but I might be missing something.
A real example:
# Prerequisities / system dependencies
- name: install python http library
apt: name={{item}} state=present
with_items:
- python-httplib2
# Configuring PHP
- include: php.yml
# Configuring database
- include: database.yml
# Configuring network
- include: network.yml
# Configuring httpd
- include: httpd.yml
# Getting and preparing / installing the code
- name: clone repository
git: repo={{repository}} dest={{destination}} accept_hostkey=yes
When I place git above the included tasks then {{destination}} is correct. When I place it below any of the included tasks, then the destination gets wrong value of a default from a completely different role...
Do you by any chance have some of those vars also defined in the group_vars/all file? In that case they overide the ones in the roles default file causing unexpected results for people unaware of this fact.
No, that would actually explain - Definitely there is no "destination" for example in any group_vars/, host_vars/, etc. In group_vars/all, there is only a vaulted set of "sensitive" variables like passwords, keys, etc.
Strange, because I'm sure every time I call a tasks file from another role I have to explicitly include that role's defaults|vars file via vars_files to be able to use its variables. So you are saying you are seeing this happen without this linking. Do you mind showing us the whole main playbook?
FYI: I - kind of - worked the problem around by moving the variables from "defaults" to "vars" but this causes other inconveniences when I actually want to overwrite them.
Example playbook ({{destination}} value changes after the first -include:
Actually I stay within one role. The problem is that when I include other tasks from the same role, the role's defaults change into values from another role, which defaults have variables named the same as in the current one.
Just out of curiosity, what happens when you supply destination via extra_vars and leave everything as it was in defaults file?
Also if you can put
- debug: var=destination
before and after every include call would be very helpful to see where the var actually gets changed.
Just realized that my previous message might not be clear enough, what I mean is add:
–extra-vars ‘{“destination”:“some_value_here”}’
to the ansible-playbook command and move back all your vars in their respective defaults files as they were before.
I /think/ I know where the problem comes from: I encountered it with group vars defined in the inventory file. I eventually moved both the role variables and group_vars into inventory file, trying to establish one place where the variables are defined. Now:
My inventory file looks like this (IPs are dummy):
[group0]
234.123.41.44
[group0:vars]
var0=00
var1=01
[group1]
234.123.41.45
[group1:vars]
var0=10
var1=11
Now, in this configuration, variables have their values intact. But when I refer with both groups to the same host, like:
[group0]
234.123.41.44
[group0:vars]
var0=00
var1=01
[group1]
234.123.41.44
[group1:vars]
var0=10
var1=11
which I guess is rather typical when deploying to development environment, then the group0 variables get overwritten!
Since my configuration with "defaults" has been applied to similar inventory, my best guess is that this caused the problem there too. Now - what to do in order to have the variables keep their values for different groups, even if the host happens to be the same? And shouldn't it be the case anyway?
Well of course they get overwritten in the same play since all variables are global during the play execution, otherwise if you call group1 after group0 what will happen is group1 will get the variables from group0, which is not desirable, right?
What you need to do is rethink your playbooks and inventory structure, think of using multiple roles instead of single one with many tasks and keep the global variables names unique if you don’t want them overwritten.
Well of course they get overwritten in the *same* play since all variables are *global* during the play execution,
Well, until yesterday I wouldn't say "of course". If I used host variables, then I would understand this as being "of course". I thought that group variables are applied to the group's host(s) upon processing. In the sense that if I don't process the other group, the variables would remain intact. But then, after writing the previous post, I dumped the 'hostvars' and only then realised what's going on.
otherwise if you call group1 after group0 what will happen is group1 will get the variables from group0, which is not desirable, right?
Frankly - I would expect/desire that when I call group0, I get variables defined for group0, when I call group1, I get the values as defined for group1. Even if they apply to the same host in the end. And if I wanted them within the host scope, I would use host_vars rather then group_vars. I understand that this is somewhat more difficult to implement, because a kind of "group context" or "group scope" would need to be introduced to the Ansible's inner workings. From what I saw in the variables dump, nothing like that's been done so far. Seems like during play execution, everything's thrown into one basket (becomes global as you mentioned). This is surely easier to implement but in this case I find the group_vars concept of noticeably lower value and also susceptible to errors, unless one understands that they are actually kind of "global host_vars".
What you need to do is rethink your playbooks and inventory structure, think of using multiple roles instead of single one with many tasks
The setup is quite complex with many roles and even those roles have number of tasks, many of them being very similar, which is why tried to extract the repetitive tasks from within the roles and place those in the upper level, where it would be accessible for all roles. You know, the good, old DRY stuff.. "Of course" I used the same variable names in all the roles as they do the same thing and the shared, included tasks use them this way. Things - kind of - worked until I started including tasks, which is probably where it traverses the directory structure and overwrites the values.
and keep the global variables names unique if you don't want them overwritten.
Actually - as I wrote above - I didn't expect them to be global. I wanted to keep them within the scope of role/group. Both approaches fail due to variables being overwritten. I spent several days on this already and eventually ended up generating different role/group variable naming (prefixes). Then I assign them (set_fact:) to the required variables right before including the tasks from upper level. Reworked half of the setup this way - works so far but I don't find it an elegant way. I've got this feeling like I am writing a program with tens of thousands lines of code and am allowed to use only global variables everywhere...