Recently we have been writing ansible scripts to deploy one of our applications. In doing so, we came across the need to configure 3 node processes as services. All of these node processes used a similar init script but required different values writing into the script.
We chose to use the template task thinking that with the 4 variables we needed to populate on each occasion we should be able to use the with_items method on the task to do what we wanted. We did plenty of Googling and couldn’t find a way forward and so were all set to revert to writing three separate implementations of the template task, and three associated templates.
Asking a question on the ansible IRC channel eventually yielded an answer and I thought I would share our implementation.
We start with a generic template, service_init_script.j2. The script itself is not important to this blog post, you should note that way in which the ansible ( / jinja2) variables refer to item.foo – this is important, and was our initial stumbling block. Each time the task is iterated upon the items within the with_items block are referred to as an item:
#!/bin/bash ### BEGIN INIT INFO # Provides: {{ item.service_name }} # Required-Start: $all # Required-Stop: $all # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: {{ item.service_description }} ### END INIT INFO # Taken loosely from https://gist.github.com/tilfin/5004848 prgcmd={{ item.service_exec }} # What gets executed? prgname={{ item.service_name }} # What's the name (used to ensure only one instance is running) prguser={{ item.service_user }} # Which user should be used pidfile=/var/run/{{ item.service_name }}.pid # Where should the pid file be stored? logfile=/var/log/{{ item.service_name }}.log export BJA_CONFIG={{ bja_config }} start() { if [ -f $pidfile ]; then pid=`cat $pidfile` kill -0 $pid >& /dev/null if [ $? -eq 0 ]; then echo "{{ item.service_name }} has already been started." return 1 fi fi echo -n "Starting {{ item.service_name }}" nohup start-stop-daemon -c $prguser -n $prgname -p $pidfile -m --exec /usr/bin/env --start $prgcmd >> $logfile 2>&1 & if [ $? -eq 0 ]; then echo "." return 0 else echo "Failed to start {{ item.service_name }}." return 1 fi } stop() { if [ ! -f $pidfile ]; then echo "{{ item.service_name }} not started." return 1 fi echo -n "Stopping {{ item.service_name }}." start-stop-daemon -p $pidfile --stop if [ $? -ne 0 ]; then echo "Failed to stop {{ item.service_name }}." return 1 fi rm $pidfile echo "." } status() { if [ -f $pidfile ]; then pid=`cat $pidfile` kill -0 $pid >& /dev/null if [ $? -eq 0 ]; then echo "{{ item.service_name }} running. (PID: ${pid})" return 0 else echo "{{ item.service_name }} might have crashed. (PID: ${pid} file remains)" return 1 fi else echo "{{ item.service_name }} not started." return 0 fi } restart() { stop if [ $? -ne 0 ]; then return 1 fi sleep 2 start return $? } case "$1" in start | stop | status | restart) $1 ;; *) echo "Usage: $0 {start|stop|status|restart}" exit 2 esac exit $?
We then wrote our ansible script and call the template task thus:
- name: Create the service init scripts template: src=service_init_script.j2 dest=/etc/init.d/{{ item.service_name }} owner=root group=root mode=755 with_items: - { service_name: paymentHandler, service_description: 'Handles payments', service_user: blackjack_attack, service_exec: "{{ application_directory }}/apps/paymentHandler.js" } - { service_name: tablePool, service_description: 'The pool of tables available', service_user: blackjack_attack, service_exec: "{{ application_directory }}/apps/tablePool.js" } - { service_name: userManager, service_description: 'Manages users', service_user: blackjack_attack, service_exec: "{{ application_directory }}/apps/userManager.js" } sudo: yes
So, we are calling the same template task, which in turn refers to the same jinja2 template, passing in a collection of dictionary objects.
The key point to understand here is that when the playbook is run ansible enumerates the items specified referring to each object as ‘item’. This is the reason why we had to prefix the variable names in the template with item.*
I’ve exposed both of these pieces of code as gists here, and here.