1

I'm looking for strategies to mitigate silent errors related to misspelled or otherwise mistyped variables being passed to Ansible roles.

So for example, I have a role with a defaults/main.yml:

foo_version: "1.0"

Within the tasks/main.yml:

- get_url:
    url: "https://foo.com/foo-{{ foo_version }}.tar.gz"
    dest: /tmp/

Now in my main playbook, if I do this, it will install the default version, 1.0:

- include_role: name=foo

If I do this, it will install version 2.0:

- include_role: name=foo
  vars:
    foo_version: "2.0"

So far so good. But if I do this by accident, it will install 1.0 without my realizing it:

- include_role: name=foo
  vars:
    fooo_version: "2.0"

The only "solution" I've thought of is to remove the defaults/main.yml, in which case it would fail in the last case because foo_version would remain undefined. But then I can't take advantage of the benefits of having defaults.

Are there other solutions? Thanks!

1

Q: " Prevent misspelled variables passed to Ansible roles ... remove the defaults/main.yml, ... But then I can't take advantage of the benefits of having defaults. Are there other solutions?

A: Yes. Tell include_role to read other file defaults_from. Fit the content of this file to your needs. For example

- include_role:
    name: foo
    defaults_from: main_special.yml
  vars:
    fooo_version: "2.0"
| improve this answer | |
  • I believe this suffers from the same problems. If I loaded defaults from main_special.yml using defaults_from, and then specified fooo_version: "2.0" expecting it to override the default software version, I'm going to silently have problems. – Ryan O. Jul 31 '19 at 1:24
0

I came up with what I think is a pretty good solution.

First, it requires the addition of this to each role:

- assert:
    that: "{{ item.key }} is defined"
    fail_msg: "Attempted to override an unknown variable: {{ item.key }}"
    quiet: yes
  loop: "{{ q('dict', override) }}"

- set_fact: { "{{ item.key }}":"{{ item.value }}" }
  loop: "{{ q('dict', override) }}"

I added those two tasks to a file, override.yml within ansible/tasks/, and then used include_tasks to dynamically include that file only when override is actually defined:

- include_tasks: "tasks/override.yml"
  when: override is defined

Second, it requires that when you override defaults with import_role that you do the following:

- include_role:
    name: foo
  vars:
    override:
      fooo_version: "2.0"

The first block of code now loops through the passed override dictionary, and verifies that each key already is defined as a variable. So in the above example, the assert would fail because fooo_version is not defined. It then loops through the override dictionary a second time and uses set_fact to set the value of each key:val pair in the dictionary.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.