What's the right way to use "defaults/main.yml" in roles?

4,461 views
Skip to first unread message

Dan

unread,
Oct 9, 2013, 4:20:14 PM10/9/13
to ansible...@googlegroups.com
Hey,

I'm a bit confused about the usage of defaults. At first sight it seems like it's a good place to put default values of parameterized roles. But when I looked deeper onto how the vars in defaults work, I see that defaults aren't exactly vars. They are placeholders that are being rendered at runtime.

Here is something that will show what I mean.

Let's say we have a role "test" with a "testvar" as parameter:
---
# file: roles/test/main.yml
- name: Who are you? {{ testvar }}
  debug: msg="Who are you? {{ testvar }}"

Now here is defaults/main.yml
---
#file: defaults/main.yml
testvar: I_Am_Test

Here is how I used the role:
    - { role: test }
    - { role: test, testvar: 'I Am Batman' }

Here is the output:
TASK: [Who are you? {{testvar}}] **********************************************
ok: [sanjose] => {"item": "", "msg": "Who are you? I_Am_Test"}

TASK: [Who are you? I Am Batman] **********************************************
ok: [sanjose] => {"item": "", "msg": "Who are you? I Am Batman"}


###


As you can see {{testvar}} (the default testvar) is being rendered only at the last second of the play. 
The problem with that is that you can't pass it as an argument to a file you're including...

Also, I suspect that the default variables won't be merged in a "merge_behavior" config. Because I suspect that defaults aren't really variables, their placeholders. Am I right?

If that's the case, what would be the best practice of using them?

Thanks.

James Cammarata

unread,
Oct 9, 2013, 4:32:15 PM10/9/13
to ansible...@googlegroups.com
The bit about simply being a placeholder is not correct, they're just held in a different variable dictionary that is later merged in with all of the others. The behavior you're seeing is related to this, as the defaults are not yet merged in, so they're not being injected into the template engine when the name of the task is read/saved. As others have noted on this ML, inventory variables suffer from the same problem. 

In the case of defaults though, they should be able to be included in that data, so please open a github issue for this and we'll look into it.

Thanks!


--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-proje...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.



--

James Cammarata <jcamm...@ansibleworks.com>
Sr. Software Engineer, AnsibleWorks, Inc.
http://www.ansibleworks.com/

Michael DeHaan

unread,
Oct 9, 2013, 4:35:19 PM10/9/13
to ansible...@googlegroups.com
Defaults are basically a value that gets loaded below inventory scope, so it is *mostly ok* that they don't load in at this level.

Loading them in at that level would clobber inventory scoped vars.

I'd basically say "don't worry about", more or less.
Michael DeHaan <mic...@ansibleworks.com>
CTO, AnsibleWorks, Inc.
http://www.ansibleworks.com/

Igal Pines

unread,
Oct 9, 2013, 4:47:17 PM10/9/13
to ansible...@googlegroups.com
Thanks for the reply James and Michael.

Here is why it annoys me:
I have a role that called pecl (it would be better to develop a module for that, right?). [it's package manager for PHP]

Now, one of the tasks inside main.yml of the role is:
  - name: Install ${package}-${version}
    shell: echo "$stdin" | pecl install ${package}-${version}
    when: php_package_exists|failed
    notify:
      - restart php5-fpm

I have another role that called "apc". It installs the APC module for PHP. It uses PECL...

The way I do it is by:
- include: ../../pecl/tasks/main.yml
    vars:
      package: ${package}
      version: ${version}

after this include I have some APC specific tasks...

$package and $version are defined in the defaults of "apc" role.

What really annoys me is that when the task is being installed, I see:

Install ${package}-${version} ********************

Basically, a lot of the tasks in my plays looks similar to the above. Do I create my plays in a wrong way?

And if we're discussing variables here, I'd be interested to know if I can make the following in some way or another:
- include: something.yml testvar={{ somevar|default(default_testvar) }}

The above statement doesn't work on include :(

Thanks a lot for reading,
looking forward :)


--
You received this message because you are subscribed to a topic in the Google Groups "Ansible Project" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/ansible-project/zlYLpIvTiYM/unsubscribe.
To unsubscribe from this group and all its topics, send an email to ansible-proje...@googlegroups.com.

Michael DeHaan

unread,
Oct 9, 2013, 6:44:11 PM10/9/13
to ansible...@googlegroups.com
"doesn't work" is one of the most confusing phrases in technical conversation and doesn't help me understand how something doesn't work.

You are also still using legacy variables, which I've asked everyone here to *NOT* do :)


Dan

unread,
Oct 10, 2013, 3:15:41 AM10/10/13
to ansible...@googlegroups.com
Hey Michael,

Thanks for your reply.

I know you said not to use ${var} and $var style. However I'm confused. I try to always use {{  }}, but sometimes it won't work.

For example, when I perform "with_items".

I also noticed that you're planning to deprecate "with_items" from includes. Why is that such a bad thing?

I find it really useful.

Here is a scenario where it's useful for me:
I have a role called "nginx/site". This role simply controls the state of an nginx site.

Now, I have another role that is called "nginx/sites", this role does the same, however it accepts lists of dictionaries regarding each site.

Inside this role, I perform:
- include: ../../site/tasks/main.yml nginx_site={{ item }}
  with_items: $nginx_sites

Is that such a bad thing?

I was thinking about the whole thing... I was fighting with this nginx/sites and php5-fpm/pools roles for sometime.
Maybe the right why to go here would be to develop a "module" for a single site and a module for a single php/pool.

And then the role would use those modules. It looks cleaner for me. Would it be a better way to approach it?

Another thing I've noticed in regards of defaults... When I define a list in defaults/main.yml, it won't be parsed inside a "with_items" call. 
When I move this var to the main playbook var decleration, it works. Is that on purpose?

Thanks a lot,
and sorry if I ask too many questions :) It's just I'm really passionate about Ansible and would like to know a lot about it :)

Michael DeHaan

unread,
Oct 10, 2013, 8:34:13 AM10/10/13
to ansible...@googlegroups.com
(A)

"For example, when I perform "with_items""

Common misconception.

This is wrong:

with_items: "{{ foo }}

You just do this:

with_items: foo

When in doubt, consult the docs, and we'll show the right way.

(B)

Include + with_items isn't supported.   Hasn't been documented for a long time.

Should have never been implemented.

Eats kittens for breakfast.

The solution is to always loop inside the role/item, or when including more than one thing it's ok to load the role more than once with different parameters if need be.

(C)

"Another thing I've noticed in regards of defaults... When I define a list in defaults/main.yml, it won't be parsed inside a "with_items" call."

Won't be parsed where?

Can you show me?

Thanks!


Dan

unread,
Oct 10, 2013, 4:56:00 PM10/10/13
to ansible...@googlegroups.com
Once again,
Thanks for your reply.

I tested with_items: var, and everything works.

Now, I have another misconception. This time it's about naming vars.

Let's say I have 2 roles. nginx_site and php_pool.

Both of them should take "user" as a parameter. Now, they both have a default "user" defined in defaults.
However, if a var called "user" that is used for different purposes is configured in a higher scope, the defaults are overridden, and it can cause some mess.

This led me to a point that it would be better that each role would depend on a dictionary and not on several single parameters. The other option is to prefix all the vars in each role. 

What would be the right choice?

thanks.

Michael DeHaan

unread,
Oct 10, 2013, 8:15:12 PM10/10/13
to ansible...@googlegroups.com
So roles are already implemented such that when using them variables set in one role won't be applied inside another, you are guaranteed to have those active roles "in scope".

Defaults however occur at a level lower than inventory and can't as easily do this, but it sounds like something we should explore if possible, such that it has the same behavior.

I'll talk to James about this as he did the magic features for regular role variables.

I'd prefer to not bring up implementation here and just get it doing what it should be :)




--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-proje...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

James Cammarata

unread,
Oct 10, 2013, 8:46:14 PM10/10/13
to ansible...@googlegroups.com
This is actually normal and expected, because defaults have the lowest priority so they are easily overridden. Best practices for default variables would be to namespace them with the role name to help prevent unexpected collisions.


--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-proje...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.



--

Michael DeHaan

unread,
Oct 10, 2013, 8:54:54 PM10/10/13
to ansible...@googlegroups.com
Yeah, to confirm, I was wrong about this earlier.

If you have a role default for "x", the whole point of role defaults was to have something *inventory* could override.

If you want something that's easily namespaced in the role, you can stick that in role/x/vars instead, and that is automatically guaranteed to be used in that role.

If you added that protection to defaults, there would be no way to specify which one in inventory.


Igal Pines

unread,
Oct 10, 2013, 9:27:28 PM10/10/13
to ansible...@googlegroups.com
Thanks for you replies.

Let's take a role for nginx_site for example. Which option is better:
1) several variables:
- { role: nginx_site, nginx_site_name="{{ some_name }}", nginx_site_ip="{{ some_ip }}" }

2) a dictionary that have it all:
vars:
  random_nginx_site:
    name: test name
    ip: 127.0.0.1

roles:
  - { role: nginx_site, nginx_site="{{ random_nginx_site }}" }

What do you think would be a better way?

Thanks.


--
You received this message because you are subscribed to a topic in the Google Groups "Ansible Project" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/ansible-project/zlYLpIvTiYM/unsubscribe.
To unsubscribe from this group and all its topics, send an email to ansible-proje...@googlegroups.com.

Michael DeHaan

unread,
Oct 10, 2013, 9:49:39 PM10/10/13
to ansible...@googlegroups.com
If you are going to set the variable in defaults and/or inventory you would not have to pass it along to the role as a parameter, it would be redundant, and cleaner to leave off.

Just do:

- { role: foo }

And the values from inventory and defaults will get used, no need to pass them along forcibly.

You did ask about something else though...

Right now {{ foo }} means "I want a string" and can't really be used to pass references.

However, if you want to pass references, currently this is the *only* valid use for old style vars

- { role: foo, params: $params }

This is something I'm working on cleaning up right now, so we can permanently encourage only the one standard.
Reply all
Reply to author
Forward
0 new messages