diff --git a/lib/ansible/runner/__init__.py b/lib/ansible/runner/__init__.py index 41e5d6054d..75a8a0766a 100644 --- a/lib/ansible/runner/__init__.py +++ b/lib/ansible/runner/__init__.py @@ -663,9 +663,24 @@ class Runner(object): if os.path.exists(filesdir): basedir = filesdir - items_terms = self.module_vars.get('items_lookup_terms', '') - items_terms = template.template(basedir, items_terms, inject) - items = utils.plugins.lookup_loader.get(items_plugin, runner=self, basedir=basedir).run(items_terms, inject=inject) + try: + items_terms = self.module_vars.get('items_lookup_terms', '') + items_terms = template.template(basedir, items_terms, inject) + items = utils.plugins.lookup_loader.get(items_plugin, runner=self, basedir=basedir).run(items_terms, inject=inject) + except errors.AnsibleUndefinedVariable, e: + if 'has no attribute' in str(e): + # the undefined variable was an attribute of a variable that does + # exist, so try and run this through the conditional check to see + # if the user wanted to skip something on being undefined + if utils.check_conditional(self.conditional, self.basedir, inject, fail_on_undefined=True): + # the conditional check passed, so we have to fail here + raise + else: + # the conditional failed, so we skip this task + result = utils.jsonify(dict(changed=False, skipped=True)) + self.callbacks.on_skipped(host, None) + return ReturnData(host=host, result=result) + # strip out any jinja2 template syntax within # the data returned by the lookup plugin items = utils._clean_data_struct(items, from_remote=True) diff --git a/test/integration/roles/test_conditionals/tasks/main.yml b/test/integration/roles/test_conditionals/tasks/main.yml index f2aa0068c6..136e9501ea 100644 --- a/test/integration/roles/test_conditionals/tasks/main.yml +++ b/test/integration/roles/test_conditionals/tasks/main.yml @@ -267,3 +267,28 @@ that: - "result.changed" +- name: test a with_items loop using a variable with a missing attribute + debug: var=item + with_items: foo.results + when: foo is defined and 'results' in foo + register: result + +- name: assert the task was skipped + assert: + that: + - "'skipped' in result" + - result.skipped + +- name: test a with_items loop skipping a single item + debug: var=item + with_items: items.results + when: item != 'b' + register: result + +- debug: var=result + +- name: assert only a single item was skipped + assert: + that: + - result.results|length == 3 + - result.results[1].skipped diff --git a/test/integration/roles/test_conditionals/vars/main.yml b/test/integration/roles/test_conditionals/vars/main.yml new file mode 100644 index 0000000000..dddcfc5998 --- /dev/null +++ b/test/integration/roles/test_conditionals/vars/main.yml @@ -0,0 +1,13 @@ +--- +# foo is a dictionary that will be used to check that +# a conditional passes a with_items loop on a variable +# with a missing attribute (ie. foo.results) +foo: + bar: a + +items: + results: + - a + - b + - c +