diff --git a/lib/ansible/playbook/base.py b/lib/ansible/playbook/base.py index 3cc9c342381..3205ce4f0d5 100644 --- a/lib/ansible/playbook/base.py +++ b/lib/ansible/playbook/base.py @@ -49,7 +49,7 @@ class Base: # vars and flags _vars = FieldAttribute(isa='dict', default=dict()) - _environment = FieldAttribute(isa='dict', default=dict()) + _environment = FieldAttribute(isa='list', default=[]) _no_log = FieldAttribute(isa='bool', default=False) def __init__(self): diff --git a/lib/ansible/playbook/block.py b/lib/ansible/playbook/block.py index e7947685737..b31f92935fc 100644 --- a/lib/ansible/playbook/block.py +++ b/lib/ansible/playbook/block.py @@ -301,6 +301,18 @@ class Block(Base, Become, Conditional, Taggable): return value + def _get_attr_environment(self): + ''' + Override for the 'tags' getattr fetcher, used from Base. + ''' + environment = self._attributes['tags'] + if environment is None: + environment = dict() + + environment = self._get_parent_attribute('environment', extend=True) + + return environment + def filter_tagged_tasks(self, play_context, all_vars): ''' Creates a new block, with task lists filtered based on the tags contained diff --git a/lib/ansible/playbook/play_context.py b/lib/ansible/playbook/play_context.py index 5056130c39d..762f3ce6cb0 100644 --- a/lib/ansible/playbook/play_context.py +++ b/lib/ansible/playbook/play_context.py @@ -217,7 +217,6 @@ class PlayContext(Base): # non connection related self.no_log = play.no_log - self.environment = play.environment if play.force_handlers is not None: self.force_handlers = play.force_handlers @@ -299,7 +298,7 @@ class PlayContext(Base): # loop through a subset of attributes on the task object and set # connection fields based on their values - for attr in ('connection', 'remote_user', 'become', 'become_user', 'become_pass', 'become_method', 'environment', 'no_log'): + for attr in ('connection', 'remote_user', 'become', 'become_user', 'become_pass', 'become_method', 'no_log'): if hasattr(task, attr): attr_val = getattr(task, attr) if attr_val is not None: diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py index b5ed98befb4..ca163ca7e55 100644 --- a/lib/ansible/playbook/task.py +++ b/lib/ansible/playbook/task.py @@ -34,7 +34,6 @@ from ansible.playbook.block import Block from ansible.playbook.conditional import Conditional from ansible.playbook.role import Role from ansible.playbook.taggable import Taggable -from ansible.utils.vars import combine_vars __all__ = ['Task'] @@ -293,25 +292,21 @@ class Task(Base, Conditional, Taggable, Become): if self._task_include: self._task_include.set_loader(loader) - def _get_parent_attribute(self, attr, extend=False, combine=False): + def _get_parent_attribute(self, attr, extend=False): ''' Generic logic to get the attribute or parent attribute for a task value. ''' value = self._attributes[attr] - if self._block and (value is None or extend or combine): + if self._block and (value is None or extend): parent_value = getattr(self._block, attr) if extend: value = self._extend_value(value, parent_value) - elif combine and isinstance(parent_value, dict) and isinstance(value, dict): - value = combine_vars(parent_value, value) else: value = parent_value - if self._task_include and (value is None or extend or combine): + if self._task_include and (value is None or extend): parent_value = getattr(self._task_include, attr) if extend: value = self._extend_value(value, parent_value) - elif combine: - value = combine_vars(parent_value, value) else: value = parent_value return value @@ -324,7 +319,7 @@ class Task(Base, Conditional, Taggable, Become): if environment is None: environment = dict() - environment = self._get_parent_attribute('environment', combine=True) + environment = self._get_parent_attribute('environment', extend=True) return environment diff --git a/lib/ansible/plugins/action/__init__.py b/lib/ansible/plugins/action/__init__.py index 4e19aeb48fc..5d454efbff0 100644 --- a/lib/ansible/plugins/action/__init__.py +++ b/lib/ansible/plugins/action/__init__.py @@ -81,11 +81,20 @@ class ActionBase: Builds the environment string to be used when executing the remote task. ''' - if self._task.environment: - if type(self._task.environment) != dict: - raise errors.AnsibleError("environment must be a dictionary, received %s" % self._task.environment) - - return self._connection._shell.env_prefix(**self._task.environment) + final_environment = dict() + if self._task.environment is not None: + environments = self._task.environment + if not isinstance(environments, list): + environments = [ environments ] + + for environment in environments: + if type(environment) != dict: + raise errors.AnsibleError("environment must be a dictionary, received %s" % environment) + # very deliberatly using update here instead of combine_vars, as + # these environment settings should not need to merge sub-dicts + final_environment.update(environment) + + return self._connection._shell.env_prefix(**final_environment) def _early_needs_tmp_path(self): ''' diff --git a/test/units/playbook/test_play_context.py b/test/units/playbook/test_play_context.py index e077398fb8b..1eade6f540e 100644 --- a/test/units/playbook/test_play_context.py +++ b/test/units/playbook/test_play_context.py @@ -73,7 +73,6 @@ class TestPlayContext(unittest.TestCase): mock_play.become_method = 'mock' mock_play.become_user = 'mockroot' mock_play.no_log = True - mock_play.environment = dict(mock='mockenv') play_context = PlayContext(play=mock_play, options=options) self.assertEqual(play_context.connection, 'mock') @@ -81,7 +80,6 @@ class TestPlayContext(unittest.TestCase): self.assertEqual(play_context.password, '') self.assertEqual(play_context.port, 1234) self.assertEqual(play_context.no_log, True) - self.assertEqual(play_context.environment, dict(mock="mockenv")) self.assertEqual(play_context.become, True) self.assertEqual(play_context.become_method, "mock") self.assertEqual(play_context.become_user, "mockroot") @@ -94,7 +92,6 @@ class TestPlayContext(unittest.TestCase): mock_task.become_user = 'mocktaskroot' mock_task.become_pass = 'mocktaskpass' mock_task.no_log = False - mock_task.environment = dict(mock='mocktaskenv') mock_host = MagicMock() mock_host.get_vars.return_value = dict( @@ -108,7 +105,6 @@ class TestPlayContext(unittest.TestCase): self.assertEqual(play_context.remote_user, 'mocktask') self.assertEqual(play_context.port, 4321) self.assertEqual(play_context.no_log, False) - self.assertEqual(play_context.environment, dict(mock="mocktaskenv")) self.assertEqual(play_context.become, True) self.assertEqual(play_context.become_method, "mocktask") self.assertEqual(play_context.become_user, "mocktaskroot")