Skip to content
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

Added basic support for hash_behaviour=merge in roles #3114

Merged
merged 1 commit into from
Jun 20, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 9 additions & 9 deletions lib/ansible/playbook/play.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,11 @@ def __init__(self, playbook, ds, basedir):
# tasks/handlers as they may have inventory scope overrides
_tasks = ds.pop('tasks', [])
_handlers = ds.pop('handlers', [])
ds = template(basedir, ds, self.vars)
ds = template(basedir, ds, self.vars)
ds['tasks'] = _tasks
ds['handlers'] = _handlers

self._ds = ds
self._ds = ds

hosts = ds.get('hosts')
if hosts is None:
Expand Down Expand Up @@ -114,7 +114,7 @@ def __init__(self, playbook, ds, basedir):

if self.sudo_user != 'root':
self.sudo = True


# *************************************************

Expand Down Expand Up @@ -146,7 +146,7 @@ def _load_roles(self, roles, ds):
# flush handlers after pre_tasks
new_tasks.append(dict(meta='flush_handlers'))

# variables if the role was parameterized (i.e. given as a hash)
# variables if the role was parameterized (i.e. given as a hash)
has_dict = {}

for orig_path in roles:
Expand Down Expand Up @@ -178,14 +178,14 @@ def _load_roles(self, roles, ds):
raise errors.AnsibleError("found role at %s, but cannot find %s or %s or %s or %s" % (path, task, handler, vars_file, library))
if os.path.isfile(task):
nt = dict(include=task, vars=has_dict)
if when:
if when:
nt['when'] = when
if with_items:
nt['with_items'] = with_items
new_tasks.append(nt)
if os.path.isfile(handler):
nt = dict(include=handler, vars=has_dict)
if when:
if when:
nt['when'] = when
if with_items:
nt['with_items'] = with_items
Expand Down Expand Up @@ -242,7 +242,7 @@ def _load_tasks(self, tasks, vars={}, additional_conditions=[], original_file=No
if x['meta'] == 'flush_handlers':
results.append(Task(self,x))
continue

task_vars = self.vars.copy()
task_vars.update(vars)
if original_file:
Expand All @@ -269,7 +269,7 @@ def _load_tasks(self, tasks, vars={}, additional_conditions=[], original_file=No
raise errors.AnsibleError("parse error: task includes cannot be used with other directives: %s" % k)

if 'vars' in x:
task_vars.update(x['vars'])
task_vars = utils.combine_vars(task_vars, x['vars'])
if 'only_if' in x:
included_additional_conditions.append(x['only_if'])

Expand All @@ -281,7 +281,7 @@ def _load_tasks(self, tasks, vars={}, additional_conditions=[], original_file=No
mv[k] = template(self.basedir, v, mv)
dirname = self.basedir
if original_file:
dirname = os.path.dirname(original_file)
dirname = os.path.dirname(original_file)
include_file = template(dirname, tokens[0], mv)
include_filename = utils.path_dwim(dirname, include_file)
data = utils.parse_yaml_from_file(include_filename)
Expand Down
39 changes: 19 additions & 20 deletions lib/ansible/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,24 +335,23 @@ def parse_kv(args):
return options

def merge_hash(a, b):
''' merges hash b into a
this means that if b has key k, the resulting has will have a key k
which value comes from b
said differently, all key/value combination from b will override a's '''
''' recursively merges hash b into a
keys from b take precedende over keys from a '''

# and iterate over b keys
result = copy.deepcopy(a)

# next, iterate over b keys and values
for k, v in b.iteritems():
if k in a and isinstance(a[k], dict):
# if this key is a hash and exists in a
# we recursively call ourselves with
# the key value of b
a[k] = merge_hash(a[k], v)
# if there's already such key in a
# and that key contains dict
if k in result and isinstance(result[k], dict):
# merge those dicts recursively
result[k] = merge_hash(a[k], v)
else:
# k is not in a, no need to merge b, we just deecopy
# or k is not a dictionnary, no need to merge b either, we just deecopy it
a[k] = v
# finally, return the resulting hash when we're done iterating keys
return a
# otherwise, just copy a value from b to a
result[k] = v

return result

def md5s(data):
''' Return MD5 hex digest of data. '''
Expand Down Expand Up @@ -604,7 +603,7 @@ def compile_when_to_only_if(expression):
# when: int $x in $alist
# when: float $x > 2 and $y <= $z
# when: str $x != $y
# when: jinja2_compare asdf # implies {{ asdf }}
# when: jinja2_compare asdf # implies {{ asdf }}

if type(expression) not in [ str, unicode ]:
raise errors.AnsibleError("invalid usage of when_ operator: %s" % expression)
Expand Down Expand Up @@ -723,21 +722,21 @@ def get_diff(diff):
return ">> the files are different, but the diff library cannot compare unicode strings"

def is_list_of_strings(items):
for x in items:
for x in items:
if not isinstance(x, basestring):
return False
return True

def safe_eval(str):
'''
'''
this is intended for allowing things like:
with_items: {{ a_list_variable }}
where Jinja2 would return a string
but we do not want to allow it to call functions (outside of Jinja2, where
the env is constrained)
'''
# FIXME: is there a more native way to do this?

def is_set(var):
return not var.startswith("$") and not '{{' in var

Expand Down Expand Up @@ -776,7 +775,7 @@ def listify_lookup_plugin_terms(terms, basedir, inject):
if isinstance(new_terms, basestring) and new_terms.find("{{") != -1:
pass
else:
terms = new_terms
terms = new_terms
except:
pass

Expand Down
58 changes: 35 additions & 23 deletions test/TestPlayBook.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ def setUp(self):
os.unlink('/tmp/ansible_test_data_template.out')
if os.path.exists('/tmp/ansible_test_messages.out'):
os.unlink('/tmp/ansible_test_messages.out')
if os.path.exists('/tmp/ansible_test_role_messages.out'):
os.unlink('/tmp/ansible_test_role_messages.out')

def _prepare_stage_dir(self):
stage_path = os.path.join(self.test_dir, 'test_data')
Expand Down Expand Up @@ -304,20 +306,17 @@ def test_playbook_hash_replace(self):
)
playbook.run()

with open('/tmp/ansible_test_messages.out') as f:
actual = [l.strip() for l in f.readlines()]

print "**ACTUAL**"
print actual

expected = [
filename = '/tmp/ansible_test_messages.out'
expected_lines = [
"goodbye: Goodbye World!"
]
self._compare_file_output(filename, expected_lines)

print "**EXPECTED**"
print expected

assert actual == expected
filename = '/tmp/ansible_test_role_messages.out'
expected_lines = [
"inside_a_role: Indeed!"
]
self._compare_file_output(filename, expected_lines)

# restore default hash behavior
C.DEFAULT_HASH_BEHAVIOUR = saved_hash_behavior
Expand All @@ -337,21 +336,34 @@ def test_playbook_hash_merge(self):
)
playbook.run()

with open('/tmp/ansible_test_messages.out') as f:
actual = [l.strip() for l in f.readlines()]

print "**ACTUAL**"
print actual
filename = '/tmp/ansible_test_messages.out'
expected_lines = [
"goodbye: Goodbye World!",
"hello: Hello World!"
]
self._compare_file_output(filename, expected_lines)

expected = [
filename = '/tmp/ansible_test_role_messages.out'
expected_lines = [
"goodbye: Goodbye World!",
"hello: Hello World!",
"goodbye: Goodbye World!"
"inside_a_role: Indeed!"
]

print "**EXPECTED**"
print expected

assert actual == expected
self._compare_file_output(filename, expected_lines)

# restore default hash behavior
C.DEFAULT_HASH_BEHAVIOUR = saved_hash_behavior

def _compare_file_output(self, filename, expected_lines):
actual_lines = []
with open(filename) as f:
actual_lines = [l.strip() for l in f.readlines()]
actual_lines = sorted(actual_lines)

print "**ACTUAL**"
print actual_lines

print "**EXPECTED**"
print expected_lines

assert actual_lines == expected_lines
5 changes: 5 additions & 0 deletions test/test_hash_behavior/playbook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,8 @@
tasks:
- name: generate messages
action: template src=message.j2 dest=/tmp/ansible_test_messages.out

roles:
- role: hash_behavior_test_role
messages:
inside_a_role: "Indeed!"
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- name: generate role messages
action: template src=role_message.j2 dest=/tmp/ansible_test_role_messages.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{% for k, v in messages.iteritems() %}
{{ k }}: {{ v }}
{% endfor %}