Ansible recursive loop in role's default varia

2019-07-17 02:42发布

问题:

How can I reuse variables in other variables in Ansible (2.1.x) without causing recursive loops?

Setup

Consider this roles/<role>/defaults/main.yml file:

---
liquibase:
  version: "3.5.3"
  download_file: "liquibase-{{liquibase.version}}-bin.tar.gz"

  # I also tried this alternative with a similar result:
  # download_file: "liquibase-{{liquibase[version]}}-bin.tar.gz
...

and this roles/<role>/tasks/main.yml file:

---
- name: Liquibase | Download
  debug:
    msg: "download_file: {{liquibase.download_file}}"
...

Error

I'd expect the variable liquibase.download_file to have the value liquibase-3.5.3-bin.tar.gz but when I run a playbook using this role, I get the following error:

...
TASK [liquibase : Liquibase | Download] *******************************************
fatal: [localhost]: FAILED! => {"failed": true, "msg": "...: recursive loop detected in template string: liquibase-{{liquibase.version}}-bin.tar.gz"}
...

My Use Case

Obviously I want to download Liquibase and I want to let the role's user decide which version to use. I also want to give the possibility to completely override the download location (file, URL, etc.), e.g. for using a company's FTP server or similar.

回答1:

Referencing other dict keys in the same parent dict is not supported. See this issue.

You can only refactor your variables to make version and download_file in different variables trees, like:

liquibase_version: "3.5.3"
liquibase_download_file: "liquibase-{{liquibase_version}}-bin.tar.gz"

P.S. if this your role's defaults, separating liquibase_version into standalone variable make even more sence. This way user would have to redefine only liquibase_version and liquibase_download_file will get the changes; while in case of dict (as in your question), you can't override only one key, user whould have to set full dict with version and download_file keys.



回答2:

I found this frustrating when trying to blend the elegance of YAML structures with the flexibility of Jinja2. As it isn't supported, one way around it (which I admit is something of a kludge) is to introduce a pseudo-private section to help break the cycle. First we declare anything that might cause a dependency:

# Pseudo private KV pairs, used to work around recursion issues.
_liquibase_version: "3.5.3"

We only have to declare anything that would be declared within the dictionary and forms a dependency for another dictionary key. Next we can declare the dictionary as we wanted but use the 'private' key value pairs where necessary:

liquibase:
  version: "{{_liquibase_version}}"
  download_file: "liquibase-{{_liquibase_version}}-bin.tar.gz"

Of course, _liquibase_version is not really private, but acts as a helper so we only have to define the value once but still get the benefit of being able to refer to {{liquibase.version}} and {{liquibase.download_file}} later on.