Iterating over a list in a dictionary within a Jinja2 template

499 views
Skip to first unread message

cmacrae

unread,
Dec 8, 2015, 5:57:23 AM12/8/15
to Ansible Project
Hi guys,

I've got something I'd really like some help on, not just the solution, but the understanding and perhaps any related documentation/articles.

I'm writing a role for deployment of Hubot.
To define bots to be deployed, I'm using a dictionary, like so:
hubot_bots:
  testbot
:
    owner
: 'Bot Wrangler <b...@example.com>'
    name
: Hubot
    descr
: Delightfully aware robutt
    adapter
: rocketchat
    environment
:
     
- rocketchat_room: GENERAL
     
- rocketchat_user: Hubot
     
- rocketchat_password: Hubot

Using 'with_items' I can iterate over items in 'hubot_bots', like 'testbot' here, and create resources, such as directories named after the key 'testbot' and then run commands accessing the values using 'item.value.owner' and 'item.value.adapter', etc.

Now, for each of these bot definitions, I want to deploy a Systemd service file, for management of each bot process.
Within this template, I want to refer to some of the values as defined above, including iterating over each key/value in the 'environment' list.

I tried my best the other evening with a few combinations of 'with_dict' and 'with_nested' and felt I was making some progress, but definitely not getting the desired results, nor understanding how it was working.

Here's the task I'm deploying the template with:
  - name: Deploy service files for each Hubot
   
template:
      src
: hubot.service.j2
      dest
: /usr/lib/systemd/system/hubot-{{ item.key }}.service
      owner
: root
     
group: root
      mode
: 0644
    with_dict
: hubot_bots


And here's the template I'm using:
[Unit]
Description=Hubot-{{ item }}
Requires=network.target
After=network.target

[Service]
Type=simple
WorkingDirectory={{ hubot_village_path }}/{{ item }}
User={{ hubot_admin_user }}

Restart=always
RestartSec=10

{% for env in item.value.environment %}
Environment={{ env|upper}}={{ env.value }}
{% endfor %}

ExecStart={{ hubot_village_path }}/{{ item }}/bin/hubot --adapter {{ item.value.adapter }}

[Install]
WantedBy=multi-user.target


As you can see, I want to refer to each bot defined under 'hubot_bots' and then some other values.

Would anyone be able to point me in the right direction on how to get the desired outcome, and understand how this works.
Any documentation or articles would be great. I'm still grasping concepts with YAML structures and the Jinja2 template engine - I'd love to learn as much as I can about it, as I can see it can be very powerful.

So, thank you so much in advance if anyone can help me on this!

Kind Regards,
Calum

Mike Biancaniello

unread,
Dec 8, 2015, 10:06:08 AM12/8/15
to Ansible Project
First, in your j2, you can't ever reference `item` without `item.key` or `item.value`.

However, I think your amin problem is that your first iteration is a dict (not a list) and the second is a list of dicts without deterministic keys.

Try this:

hubot_bots
:
  testbot
:
    owner
: 'Bot Wrangler <b...@example.com>'
    name
: Hubot
    descr
: Delightfully aware robutt
    adapter
: rocketchat
    environment
:

     
- key: rocketchat_room
        value
: GENERAL
     
- key: rocketchat_user
        value
: Hubot
     
- key: rocketchat_password
        value
: Hubot




or:

hubot_bots:
  testbot
:
    owner
: 'Bot Wrangler <b...@example.com>'
    name
: Hubot
    descr
: Delightfully aware robutt
    adapter
: rocketchat
    environment
:

      rocketchat_room
: GENERAL
      rocketchat_user
: Hubot
      rocketchat_password
: Hubot


You could also go with with_items and change the vars to:

hubot_bots:
 
- label: testbot
    owner
: 'Bot Wrangler <b...@example.com>'

    name
: Hubot
    descr
: Delightfully aware robutt
    adapter
: rocketchat
    environment
:

      rocketchat_room
: GENERAL
      rocketchat_user
: Hubot
      rocketchat_password
: Hubot




cmacrae

unread,
Dec 11, 2015, 9:51:51 AM12/11/15
to Ansible Project
Thanks Mike! I'll take a go at using this :)
Message has been deleted

cmacrae

unread,
Dec 14, 2015, 10:38:54 AM12/14/15
to Ansible Project
So, I solved what I wanted to do :)  
For anyone interested, here's how I got it working:

Bot definition looks like this:
hubot_bots:
  testbot:
    owner: 'Bot Wrangler <b...@example.com>'
    name: Hubot
    descr: Delightfully aware robutt
    adapter: rocketchat
    environment:
      - rocketchat_room: GENERAL
      - rocketchat_user: Hubot
      - rocketchat_password: Hubot


Task that deploys the template which reads the above values, looks like this:
  - name: Deploy service files for each Hubot
    template:
      src: hubot.service.j2
      dest: /usr/lib/systemd/system/hubot-{{ item.key }}.service
      owner: root
      group: root
      mode: 0644
    with_dict: hubot_bots


And finally, the Jinja2 code that does what I need (the significant part being the for loops):
[Unit]
Description=Hubot-{{ item.key }}
Requires=network.target
After=network.target

[Service]
Type=simple
WorkingDirectory={{ hubot_village_path }}/{{ item.key }}
User={{ hubot_admin_user }}

Restart=always
RestartSec=10

{% for env in item.value.environment %}
{% for k, v in env.items() %}
Environment={{ k |upper }}={{ v }}
{% endfor %}
{% endfor %}  
 

ExecStart={{ hubot_village_path }}/{{ item.key }}/bin/hubot --adapter {{ item.value.adapter }}

[Install]
WantedBy=multi-user.target


Hope this helps anyone that stumbles upon it!
Reply all
Reply to author
Forward
0 new messages