Esempio n. 1
0
    def test_action_base__compute_environment_string(self):
        fake_loader = DictDataLoader({})

        # create our fake task
        mock_task = MagicMock()
        mock_task.action = "copy"
        mock_task.args = dict(a=1)

        # create a mock connection, so we don't actually try and connect to things
        def env_prefix(**args):
            return ' '.join([
                '%s=%s' % (k, shlex_quote(text_type(v)))
                for k, v in args.items()
            ])

        mock_connection = MagicMock()
        mock_connection._shell.env_prefix.side_effect = env_prefix

        # we're using a real play context here
        play_context = PlayContext()

        # and we're using a real templar here too
        templar = Templar(loader=fake_loader)

        # our test class
        action_base = DerivedActionBase(
            task=mock_task,
            connection=mock_connection,
            play_context=play_context,
            loader=fake_loader,
            templar=templar,
            shared_loader_obj=None,
        )

        # test standard environment setup
        mock_task.environment = [dict(FOO='foo'), None]
        env_string = action_base._compute_environment_string()
        self.assertEqual(env_string, "FOO=foo")

        # test where environment is not a list
        mock_task.environment = dict(FOO='foo')
        env_string = action_base._compute_environment_string()
        self.assertEqual(env_string, "FOO=foo")

        # test environment with a variable in it
        templar.available_variables = dict(the_var='bar')
        mock_task.environment = [dict(FOO='{{the_var}}')]
        env_string = action_base._compute_environment_string()
        self.assertEqual(env_string, "FOO=bar")

        # test with a bad environment set
        mock_task.environment = dict(FOO='foo')
        mock_task.environment = ['hi there']
        self.assertRaises(AssibleError,
                          action_base._compute_environment_string)
Esempio n. 2
0
    def _get_delegated_vars(self, play, task, existing_variables):
        # This method has a lot of code copied from ``TaskExecutor._get_loop_items``
        # if this is failing, and ``TaskExecutor._get_loop_items`` is not
        # then more will have to be copied here.
        # TODO: dedupe code here and with ``TaskExecutor._get_loop_items``
        #       this may be possible once we move pre-processing pre fork

        if not hasattr(task, 'loop'):
            # This "task" is not a Task, so we need to skip it
            return {}, None

        # we unfortunately need to template the delegate_to field here,
        # as we're fetching vars before post_validate has been called on
        # the task that has been passed in
        vars_copy = existing_variables.copy()

        # get search path for this task to pass to lookup plugins
        vars_copy['assible_search_path'] = task.get_search_path()

        # ensure basedir is always in (dwim already searches here but we need to display it)
        if self._loader.get_basedir() not in vars_copy['assible_search_path']:
            vars_copy['assible_search_path'].append(self._loader.get_basedir())

        templar = Templar(loader=self._loader, variables=vars_copy)

        items = []
        has_loop = True
        if task.loop_with is not None:
            if task.loop_with in lookup_loader:
                fail = True
                if task.loop_with == 'first_found':
                    # first_found loops are special. If the item is undefined then we want to fall through to the next
                    fail = False
                try:
                    loop_terms = listify_lookup_plugin_terms(
                        terms=task.loop,
                        templar=templar,
                        loader=self._loader,
                        fail_on_undefined=fail,
                        convert_bare=False)

                    if not fail:
                        loop_terms = [
                            t for t in loop_terms if not templar.is_template(t)
                        ]

                    mylookup = lookup_loader.get(task.loop_with,
                                                 loader=self._loader,
                                                 templar=templar)

                    # give lookup task 'context' for subdir (mostly needed for first_found)
                    for subdir in ['template', 'var',
                                   'file']:  # TODO: move this to constants?
                        if subdir in task.action:
                            break
                    setattr(mylookup, '_subdir', subdir + 's')

                    items = wrap_var(
                        mylookup.run(terms=loop_terms, variables=vars_copy))

                except AssibleTemplateError:
                    # This task will be skipped later due to this, so we just setup
                    # a dummy array for the later code so it doesn't fail
                    items = [None]
            else:
                raise AssibleError(
                    "Failed to find the lookup named '%s' in the available lookup plugins"
                    % task.loop_with)
        elif task.loop is not None:
            try:
                items = templar.template(task.loop)
            except AssibleTemplateError:
                # This task will be skipped later due to this, so we just setup
                # a dummy array for the later code so it doesn't fail
                items = [None]
        else:
            has_loop = False
            items = [None]

        # since host can change per loop, we keep dict per host name resolved
        delegated_host_vars = dict()
        item_var = getattr(task.loop_control, 'loop_var', 'item')
        cache_items = False
        for item in items:
            # update the variables with the item value for templating, in case we need it
            if item is not None:
                vars_copy[item_var] = item

            templar.available_variables = vars_copy
            delegated_host_name = templar.template(task.delegate_to,
                                                   fail_on_undefined=False)
            if delegated_host_name != task.delegate_to:
                cache_items = True
            if delegated_host_name is None:
                raise AssibleError(
                    message="Undefined delegate_to host for task:",
                    obj=task._ds)
            if not isinstance(delegated_host_name, string_types):
                raise AssibleError(
                    message=
                    "the field 'delegate_to' has an invalid type (%s), and could not be"
                    " converted to a string type." % type(delegated_host_name),
                    obj=task._ds)

            if delegated_host_name in delegated_host_vars:
                # no need to repeat ourselves, as the delegate_to value
                # does not appear to be tied to the loop item variable
                continue

            # now try to find the delegated-to host in inventory, or failing that,
            # create a new host on the fly so we can fetch variables for it
            delegated_host = None
            if self._inventory is not None:
                delegated_host = self._inventory.get_host(delegated_host_name)
                # try looking it up based on the address field, and finally
                # fall back to creating a host on the fly to use for the var lookup
                if delegated_host is None:
                    for h in self._inventory.get_hosts(
                            ignore_limits=True, ignore_restrictions=True):
                        # check if the address matches, or if both the delegated_to host
                        # and the current host are in the list of localhost aliases
                        if h.address == delegated_host_name:
                            delegated_host = h
                            break
                    else:
                        delegated_host = Host(name=delegated_host_name)
            else:
                delegated_host = Host(name=delegated_host_name)

            # now we go fetch the vars for the delegated-to host and save them in our
            # master dictionary of variables to be used later in the TaskExecutor/PlayContext
            delegated_host_vars[delegated_host_name] = self.get_vars(
                play=play,
                host=delegated_host,
                task=task,
                include_delegate_to=False,
                include_hostvars=True,
            )
            delegated_host_vars[delegated_host_name][
                'inventory_hostname'] = vars_copy.get('inventory_hostname')

        _assible_loop_cache = None
        if has_loop and cache_items:
            # delegate_to templating produced a change, so we will cache the templated items
            # in a special private hostvar
            # this ensures that delegate_to+loop doesn't produce different results than TaskExecutor
            # which may reprocess the loop
            _assible_loop_cache = items

        return delegated_host_vars, _assible_loop_cache