Not sure if this is considered an edge case, or if there's a better way to model things (suggestions appreciated). FWIW, I just started using Ansible, so please let me know if there's a more idiomatic approach.
Let's say I have a group of docker containers that I want to build images for to form an environment - so containers for web servers, database servers, cache servers, some other arbitrary services, etc. I'd like to be able to automate the creation of this environment within a single playbook.
So, I create a playbook called "docker-cluster". It has some basic bootstrapping roles like making sure the right Python modules are installed (python-apt, python-pycurl, docker-py), etc. It also has a role for building each of the docker images (www, mysql, mongodb, redis, etc).
However, each of these docker images might be built on another local docker image (e.g., not something that will be pulled from the Docker repository, so it needs to be built as a prerequisite), so I have the dependencies listed out as other roles before the actual final containers' roles. The final list looks something like this:
roles:
- { role: base } # handles general python modules that Ansible requires
- { role: docker } # handles docker_image prereqs, e.g. docker-py
- { role: docker-base } # base container, e.g. supervisord, sshd, etc
- { role: docker-mount } # mount points for --volumes-from, e.g. shared NAS
- { role: docker-www-base } # boilerplate for web servers
- { role: docker-db-base } # boilerplate for database servers
- { role: docker-app-www } # final web server image
- { role: docker-app-db } # final database server image
etc. We could clean that up by using a common role that has vars passed in, but that makes reusability a bit more tedious since implementors need to be aware of any potential changes down the road. This works as expected.
The problem comes when I try to consolidate this a bit - I shouldn't need to specify these dependencies in the implementation, I should specify them in the role themselves. Which is great, because there's dependency support, and it's awesome. But since these roles are all very similar, they have the same variable names. So for the docker-app-www image, we might have the following task definition:
# app/www/tasks/main.yml
---
- name: Build {{ tag }}
docker_image: name="{{ tag }}" tag="latest" path="{{ build_path }}" state=present
# app/www/vars/main.yml
---
tag: my/www
build_path: /path/to/my/www/dockerfile/dir
Great. But now when we introduce a dependency:
# app/www/meta/main.yml
---
dependencies:
- { role: app/base }
which is the same, except for its own variables:
# app/base/tasks/main.yml
---
- name: Build {{ tag }}
docker_image: name="{{ tag }}" tag="latest" path="{{ build_path }}" state=present
# app/base/vars/main.yml
---
tag: my/base
build_path: /path/to/my/base/dockerfile/dir
we end up with the base task's variables being overridden by the depending context, so we end up injecting the wrong vars into the dependency.
As an side, this is intentionally a little verbose - these tasks are identical (except for the vars), so we could use include here instead, but the point still stands that if I reused the same task I would have to specify which variables to use anywhere each task was used - vs them being broken out into separate tasks and letting them have their own var definitions by virtue of Ansible's task magic.
Any thoughts about how to approach a situation like this (a task with N levels of dependencies that are all the same task except for dependency-specific variables, without having to explicitly define the variables everywhere the task was used) would be greatly appreciated - I'm not against a total restructuring either, so please let me know if there's a more sensible way to manage it according to Ansible.
Thanks!