3

I am trying to split my Ansible role variables into multiple files - as per this answer, it should be possible to create a vars/main directory, and all the .yml files in that directory should be automatically loaded.

However, this does not seem to happen in my case.

My directory structure:

vars
└── main
    ├── gce_settings.yml
    ├── vsphere_settings.yml
    └── vsphere_zone.yml

However, when I try to use a variable defined inside vsphere_settings.yml, Ansible complains that the variable is undefined: {"msg": "The task includes an option with an undefined variable. The error was: 'vsphere_user' is undefined

If I move the variable declaration into vars/main.yml, everything works as expected. But, of course, I would prefer to separate my variables into multiple files.

I was unable to find any reference to this "feature" in the official Ansible documentation, and I do not know how I could troubleshoot it. Can anyone point me in the right direction?

My ansible version: ansible 2.8.5 on Ubuntu 16.04

And before you ask: yes, I did make sure that main.yml was not present when trying to load vars/main/*.yml...

β.εηοιτ.βε
  • 33,893
  • 13
  • 69
  • 83
Bogd
  • 673
  • 9
  • 16
  • 1
    If my example below does not work for you post the details. Make it [mcve](https://stackoverflow.com/help/minimal-reproducible-example). – Vladimir Botka Oct 05 '19 at 12:47
  • Thank you, Vladimir - I have added some more details to the question, and I have also posted another example (with the steps to reproduce) in my answer. – Bogd Oct 05 '19 at 14:43

2 Answers2

2

TL;DR version: it is a bug in ansible, caused by the presence of empty .yml files in vars/main. There is a PR out for it already. See here .

The actual result (as mentioned in the comments) actually depends on the order the files are processed (by default, it looks like my Ansible processes them in alphabetical order - but this might depend on the version, or the underlying OS):

  • If the empty file is processed first, you get an error message: ERROR! failed to combine variables, expected dicts but got a 'NoneType' and a 'AnsibleMapping'
  • If the empty file is processed after other files, there is no error message, but all the variables that have been set up to this point are destroyed

More details: since I have no idea how to troubleshoot this in Ansible itself, I went to the code and started to add print statements to see what is happening.

In my case, I had some empty .yml files in the vars/main directory (files that I was planning to populate later on. Well... it looks like when the code encounters such an empty file, it destroys the entire dictionary that it has built so far.

Either I'm missing something very important, or this is a bug... Steps to reproduce:

  • create a role
  • create the vars/main directory, and populate it with some .yml files
  • add an empty .yml file (named so that it is processed after the other files)
  • try to print the variables from the first files
    ansible# tree roles 
    roles
    +-- test
        +-- tasks
        ¦   +-- main.yml
        +-- vars
            +-- main
                +-- correct_vars.yml

    4 directories, 2 files

    ansible# cat roles/test/vars/main/correct_vars.yml  myname: bogd

    ansible# ansible-playbook -i inventory.yml test.yml 
    ... 
    ok: [localhost] => {
        "myname": "bogd" } 
    ...

    ansible# echo > roles/test/vars/main/emptyfile.yml

    ansible# ansible-playbook -i inventory.yml test.yml 
    ... ok: [localhost] => {
        "myname": "VARIABLE IS NOT DEFINED!" } 
    ...

Later edit: yep, it's a bug... See here.

Bogd
  • 673
  • 9
  • 16
  • You should edit this in your question, not post an answer out of it – β.εηοιτ.βε Oct 05 '19 at 11:07
  • @β.εηοιτ.βε - Since this officially solves my problem ("it's a bug, and there is a PR out for it already"), I decided to make an answer out of it. Hopefully, this will help other people stumbling across the same issue. I will edit the answer and move the final result ("it's a bug") to the top, to make things more clear. – Bogd Oct 05 '19 at 11:11
  • This is unrelated and not a bug. What you are referring to in your question is **role vars** what your are missing is fact that all path _myrdd_ speaks about in the linked answer is actually relative to a role folder. – β.εηοιτ.βε Oct 05 '19 at 11:12
  • The linked issue refers to playbooks, indeed - but it is the same code path, and the same behavior (combine_vars nukes the dictionary when new_data is None). And, in fact, even bcoca closed a role issue ( https://github.com/ansible/ansible/issues/54915 ) as a dupe of the one I linked. – Bogd Oct 05 '19 at 11:18
  • @β.εηοιτ.βε - my apologies, I have missed the second part of your comment. Everything I tested above is relative to a role folder, of course (the answer should make that clear). But when you are saying that "it is not a bug", what are you basing that on? What am I missing? Because I have already linked two github issues that seem to say that it is a bug... – Bogd Oct 05 '19 at 11:22
  • Well because it is, or you have a bias somewhere in your testing scenario, or something that you don't explain in the question. Doing exactly the same as you give me a clear explanation from ansible: `ERROR! failed to combine variables, expected dicts but got a 'NoneType' and a 'AnsibleMapping': ` Which clearly show it is trying to merge the `no-empty.yml` and the `empty.yml` but fail to do so because the later is empty – β.εηοιτ.βε Oct 05 '19 at 12:07
  • That is because the exact result depends on the actual order in which the files are processed. If the empty one is first, you get the error you mentioned. If the empty one is second, you get exactly my results (vars get destroyed silently). The files seem to be processed in alphabetical order, so in your case the empty one gets processed first. – Bogd Oct 05 '19 at 14:38
  • Then you definitly need to edit that in you question and / or your answer, because this is the MCVE _Vladimir Botka_ is referring on your question. Without those kind of information, both this answer and your question is useless to anyone else. – β.εηοιτ.βε Oct 05 '19 at 14:45
  • Thank you - I did my best to clarify the answer. And thank you for fixing my code block! – Bogd Oct 06 '19 at 06:12
2

The example below

$ cat play.yml 
- hosts: localhost
  roles:
    - role1

$ cat roles/role1/tasks/main.yml 
- debug:
    var: var1

$ cat roles/role1/vars/main/var1.yml 
var1: test_var1

gives

"var1": "test_var1"
Vladimir Botka
  • 58,131
  • 4
  • 32
  • 63
  • Thank you - indeed, that is the way things should work. It appears that I have stumbled onto a bug caused by an empty .yml file. See my answer for the details. – Bogd Oct 05 '19 at 14:40