Merging dictionaries for "environment" parameter

Hi,

I am wanting to use the environment task parameter with dictionaries taken from two roles, e.g.

in roles/ruby/vars/main.yml:

ruby_environment:
    GEM_HOME: "/home/deploy/.gem/ruby/2.1.2"
    PATH:     "/home/deploy/.gem/ruby/2.1.2/bin:/opt/rubies/ruby-2.1.2/lib/ruby/gems/2.1.0/bin:/opt/rubies/ruby-2.1.2/bin"

in roles/rails/tasks/main.yml, I am wanting to do something like this:

- name: execute bundler
  command: bundle install --deployment
  environment:
    "{{ ruby_environment }}" merged with "RAILS_ENV: production"

Is this possible?

Thanks,

Steve

To add: would prefer not to use hash_behaviour = merge if possible.

I think this might be proposing something like a jinja2 filter that merges one hash with another and returns it.

I’m not aware of this, but some other folks go deeper than me.

I’m not opposed to a filter plugin function being added that does this being added if that makes sense.

I thought it might be possible to loop over each hash respectively and then create a new one with all of those.

That in particular, I’d think no.

What I’d really like might look something like

environment: “{{ dict1 | update(dict2) }}”

Anybody done anything like that with stock Jinja2-voodoo magic? If not, it’s about a two liner in filter_plugins/core.py to expose the native hash update function.

dict1 + dict2

I also have an update to ‘set theory’ that will allow you to do dict1|union(dict2)

So that’s a thing already with {{ dict1 + dict2 }}?

Nice.

Not really. Python doesn’t allow to merge two hashes using “{{ env1 + env2 }}”. Here’s what worked for me:

playbooks/filter_plugins/filters.py:

I think your filter plugin could be more easily written:

hash_a.update(hash_b)

perhaps?

If you send in a pull request, this seems reasonable to have as a core filter.

I’d probably call it ‘update’ if I’m correct on the above, which I think I am. If not, perhaps “merge”.

I could swear I had patched the union filter to use update when dicts were passed in, let me try this again.

Brian Coca

It would be nicer to have a filter for jinja2, but this works:

defaults.yml
d1:
k1: 1

d2:
k1: 2
k2: 3

tasks/main.yml

  • name: dbg
    debug: msg=“{{d1}} {{d2}}”

  • set_fact:
    d1: “{% do d1.update(d2) %}{{d1}}”

  • debug: msg=“{{d1[‘k1’]}}”

OUTPUT

TASK: [gnf_data | dbg] ********************************************************
ESTABLISH CONNECTION FOR USER: ubuntu
ok: [deploy-test-1] => {
“msg”: “{‘k1’: 1} {‘k2’: 3, ‘k1’: 2}”
}

TASK: [gnf_data | set_fact ] **************************************************
ESTABLISH CONNECTION FOR USER: ubuntu
ok: [deploy-test-1] => {“ansible_facts”: {“d1”: {“k1”: 2, “k2”: 3}}}

TASK: [gnf_data | debug msg=“{{d1[‘k1’]}}”] ***********************************
ESTABLISH CONNECTION FOR USER: ubuntu
ok: [deploy-test-1] => {
“msg”: “2”
}

I had exactly the same idea. Did anybody do a PR for this? I don’t see it in 1.9 nor in devel.

If nobody else has time for it I could do it. I think this is really a gap. Anyway this way is definitely more what they call pythonic than tweaking the union filter.

My code is simply:

`

`
def update(dic, odic):
dic.update(odic)
return dic

class FilterModule(object):

def filters(self):
return {

‘update’: update,
‘update_dict’: update,
‘merge_dict’: update
}

`

`

there is a hash_merge PR pending but it won't make it into 1.9.

Looks nice. And nice to know. Thank you!

Looks like update doesn’t do a recursive hash merge. This might be the solution, https://www.xormedia.com/recursively-merge-dictionaries-in-python/
Is the best practice be to add this as a jinja filter?

For version 2.0+ check this please:

http://docs.ansible.com/ansible/playbooks_filters.html#combining-hashes-dictionaries

"