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

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:

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!

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.

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 :frowning:

Thanks a lot for reading,
looking forward :slight_smile:

“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 :slight_smile:

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 :slight_smile: It’s just I’m really passionate about Ansible and would like to know a lot about it :slight_smile:

(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!

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.

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 :slight_smile:

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.

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.

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 }}” }
  1. 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.

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.