-
Notifications
You must be signed in to change notification settings - Fork 23.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Passing dict variable defined in group_vars to role fails when dict merging is enabled #12915
Comments
This works as expected when dict merging is not enabled. This also works when the variable is accessed through hostvars:
|
We have the exact same error. When debugging it seems that it wants to merge a dictionary with a string, after digging into combine_vars(a, b) it appears that the value of a is a complete dict (with the contents of packages.foo_api already merged) and the value of b is the string "{{ packages.foo_api }}". See https://github.com/ansible/ansible/blob/stable-1.9/lib/ansible/utils/__init__.py#L1501 Why the additional merge of something that was already successfully merged is beyond me at this point. Here is our example: ansible.cfg [defaults]
hostfile = hosts
hash_behaviour = merge group_vars/all packages:
foo_api:
name: api
version: 1.2.3
tomcat: 10 roles/foo-framework/tasks/main.yml ---
- name: '{{ foo.name }} : Test merging of vars'
debug:
var: foo test62.yml ---
- hosts: localhost
gather_facts: no
roles:
- role: foo-framework
foo: '{{ packages.foo_api }}' With the following output:
|
Using pdb:
|
The issue here is that templating does not happen until the last possible moment (mostly task execution), in previous versions Ansible would template at several stages while ignoring errors as data was not available (which current behavior guarantees is an error and does not ignore). |
I have a simple workaround that in case the instance of b is of type basestring, it returns the a dict. |
@ngaya-ll Can you confirm that this fix works in your case for v1.9 ? |
From what @bcoca said, the fact that the second argument is a string is a bug, and ignoring it would just be masking the real issue. |
The fix I propose is not ignoring the string. I am turning the jinja template into the dictionary we were expecting. Look at PR #14565 |
My bad, I thought your above comment was describing the content of the pull request. Looking at the code, your change looks reasonable, but now I'm having trouble reproducing the original error. |
If you have already moved to v2.0+, the issue does not exist on v2.0 and newer, only starting from v1.8 to v1.9.4. |
Yep, I'm using 1.9.4 |
My bad, was forgetting the British spelling in Your fix did not solve the problem for my original test case. But a similar change with |
You are right, I was testing a few things and the PR got mixed up. I updated the PR with the change for role_params. PS Today I had the exact same issue when making a minimal test-case. Took me some time to pin it to a misspelled "hash_behaviour" ;-) |
This fixes ansible#12915 Since combine_vars() is being run directly on role_params, we have to avoid merge_hash() to complain about merging a dict with a string (jinja template).
This fix still seems fragile to me. I would worry that the wrong values would get substituted in the template. For instance, the I've seen a couple other bugs where dict merging results in mysterious type errors:
The fundamental problem seems to be the ill-defined model of how hash merging interacts with variable resolution. Does dict merging occur before or after template expansion? The answer seems to be "neither", some variables are templated and then merged, other things are merged before templating. It's also unclear what the correct behavior would be. If dict merging occurs first, then we get errors like this bug. If templating occurs first then the values picked up by the template will not reflect the final merged values. I'd be interested to learn how/if Ansible 2.0 solves this problem. |
I don't disagree. The problem is that with Ansible 2.0 a lot has been rewritten (and roles are a complete different beast) so you cannot simply pinpoint what change fixed it. (In fact, when tracing Ansible 2.0 does not call combine_vars() in the same way during the run) But this issue is very specific to merge_hash() behaviour, and templating at inject time is required for merge_hash() because otherwise we cannot merge dicts at that time as would be expected. So there aren't many options. Either we template beforehand, or we just skip merge_hash() if we are not given two dicts. BTW as I indicated above the case where it fails, it is in fact merging the same thing, albeit untemplated, onto itself so I don't see the point in doing this in the first place. Look at the below. Dict a and dict b are in fact the exact same thing, but one is templated, the other is not.
|
@ngaya-ll The alternative solution is to avoid untemplated strings (but still report everything else). Adding the below to the start of hash_merge(): # if a is a string, check if it is a template, if so return the other dict
if isinstance(a, basestring) and a.strip().startswith('{{'):
if isinstance(b, dict):
return b.copy()
# if b is a string, check if it is a template, if so return the other dict
if isinstance(b, basestring) and b.strip().startswith('{{'):
if isinstance(a, dict):
return a.copy() |
@ngaya-ll Greetings! Thanks for taking the time to open this issue. In order for the community to handle your issue effectively, we need a bit more information. Here are the items we could not find in your description:
Please set the description of this issue with this template: |
@ngaya-ll You have not responded to information requests in this issue so we will assume it no longer affects you. If you are still interested in this, please create a new issue with the requested information. |
Ansible version: 1.9.4
When dict merging is enabled, passing a dict as a role parameter produces an error.
Example:
Consider the following Ansible project:
ansible.cfg:
test.yml
group_vars/all:
inventory/local.ini:
roles/demo/tasks/main.yml:
Expected behavior:
Actual behavior:
The text was updated successfully, but these errors were encountered: