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

[2.15] Validate task attributes with first finalized attrs after loop (#80476) #80517

Merged
merged 1 commit into from
Apr 17, 2023
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelogs/fragments/80476-fix-loop-task-post-validation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bugfixes:
- Fix post-validating looped task fields so the strategy uses the correct values after task execution.
12 changes: 12 additions & 0 deletions lib/ansible/executor/task_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@ def run(self):
self._task.ignore_errors = item_ignore
elif self._task.ignore_errors and not item_ignore:
self._task.ignore_errors = item_ignore
if 'unreachable' in item and item['unreachable']:
item_ignore_unreachable = item.pop('_ansible_ignore_unreachable')
if not res.get('unreachable'):
self._task.ignore_unreachable = item_ignore_unreachable
elif self._task.ignore_unreachable and not item_ignore_unreachable:
self._task.ignore_unreachable = item_ignore_unreachable

# ensure to accumulate these
for array in ['warnings', 'deprecations']:
Expand Down Expand Up @@ -277,6 +283,7 @@ def _run_loop(self, items):
u" to something else to avoid variable collisions and unexpected behavior." % (self._task, loop_var))

ran_once = False
task_fields = None
no_log = False
items_len = len(items)
results = []
Expand Down Expand Up @@ -348,6 +355,7 @@ def _run_loop(self, items):

res['_ansible_item_result'] = True
res['_ansible_ignore_errors'] = task_fields.get('ignore_errors')
res['_ansible_ignore_unreachable'] = task_fields.get('ignore_unreachable')

# gets templated here unlike rest of loop_control fields, depends on loop_var above
try:
Expand Down Expand Up @@ -392,6 +400,10 @@ def _run_loop(self, items):
del task_vars[var]

self._task.no_log = no_log
# NOTE: run_once cannot contain loop vars because it's templated earlier also
# This is saving the post-validated field from the last loop so the strategy can use the templated value post task execution
self._task.run_once = task_fields.get('run_once')
self._task.action = task_fields.get('action')

return results

Expand Down
6 changes: 1 addition & 5 deletions lib/ansible/plugins/strategy/linear.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
from ansible.errors import AnsibleError, AnsibleAssertionError, AnsibleParserError
from ansible.executor.play_iterator import IteratingStates, FailedStates
from ansible.module_utils._text import to_text
from ansible.module_utils.parsing.convert_bool import boolean
from ansible.playbook.handler import Handler
from ansible.playbook.included_file import IncludedFile
from ansible.playbook.task import Task
Expand Down Expand Up @@ -214,10 +213,7 @@ def run(self, iterator, play_context):
skip_rest = True
break

if templar.is_template(task.run_once):
setattr(task, 'run_once', boolean(templar.template(task.run_once), strict=True))

run_once = task.run_once or action and getattr(action, 'BYPASS_HOST_LOOP', False)
run_once = templar.template(task.run_once) or action and getattr(action, 'BYPASS_HOST_LOOP', False)

if (task.any_errors_fatal or run_once) and not task.ignore_errors:
any_errors_fatal = True
Expand Down