Iterating over a list in a dictionary within a Jinja2 template

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 bw@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

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
    `

Thanks Mike! I’ll take a go at using this :slight_smile:

So, I solved what I wanted to do :slight_smile:
For anyone interested, here’s how I got it working:

Bot definition looks like this:

`

hubot_bots:
testbot:
owner: ‘Bot Wrangler <bw@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!