We’ve got a set of playbooks that we use to set up production, testing and development environments for a project.
A very useful pattern on which we rely heavily is using template source directory names based on inventory_hostname, e.g:
- name: copy over nginx config for some site
template:
src=nginx/{{ inventory_hostname }}/some_site.j2
[…]
This allows us to easily make host-specific config alterations without relying on variables exclusively.
Right now I’m trying to make a similar pattern happen using a group name. That is, we’d have a directory tree like this (group names in bold):
roles/
some_role/
templates/
nginx/
servers/
some_site.j2
testservers/
some_site.j2
Mostly our servers only ever belong to one group, but it’s possible for them to belong to multiple ones. This is why we can’t simply use something like:
template:
src=nginx/{{ group_names[0] }}/some_site.j2
…because there’s no guarantee that the group name for which a directory exists is at index zero of group_names for any given host.
Assume we have a server that belongs to three groups: linode-london, testservers and, say, xyzzy, appearing in this order in group_names, with the directory tree shown above. Here’s what I would like the playbook to do for this server when encountering a task that relies on group names as a path component:
-
We’re looking for a template called some_site.j2. Start looping through group_names.
-
For linode-london, we see that the path nginx/linode-london/some_site.j2 does not exist. This is not an error, continue to the next loop item.
-
For testservers, we find that the path nginx/testservers/some_site.j2 does exist. Great! We’ll use that and stop the loop.
-
Task successful, move on to the next task.
Finding a path for the testservers role means we’d never even try to see if we had one for xyzzy. We’d only use this group name-based directory layout in cases where no host would ever belong to two or more groups that had corresponding directories under templates/nginx. (e.g. it would make no sense in our server modeling setup to have a server belong to both servers and testservers).
If no path corresponding to any of the group names of the current host was found, we’d probably want to treat that as an error.
It would also be an acceptable solution to iterate through all three group names and then just filter out the single one that exists, feeding that to the template task. (If two or more existed, it should be treated as an error.)
I’ve tried a variety of approaches, both simple and complicated (jinja2 filtering and JSON querying stat loops, set_fact, etc.), but have not yet found one that would achieve what I’m trying to do here, at the playbook task level. Of course, we could have group_vars/servers and group_vars/testservers both define a variable called some_role_nginx_template_path and use it in a task like this: nginx/{{ some_role_nginx_template_path }}/some_site.j2 but honestly, where’s the fun in that?
It feels like there’s a pure task-based solution based on path existence (as with our inventory_hostname approach) that I’m really close to, but cannot quite reach. Anybody?