-
Notifications
You must be signed in to change notification settings - Fork 23.7k
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
Ansible not running interpreter discovery on host after run_once and gather_facts disabled #77219
Comments
Files identified in the description: If these files are incorrect, please update the |
This probably also affects |
I verified this, but I'm not entirely sure it's a bug. When run_once is true and a task returns ansible_facts, the facts are copied to all the play hosts that are not unreachable (here: https://github.com/ansible/ansible/blob/stable-2.12/lib/ansible/plugins/strategy/__init__.py#L699-L709). Interpreter discovery does not occur specially for gather_facts. If gather_facts is enabled for the first play it would work because the gather_facts setup task overrides the run_once keyword to False. Running gather_facts doesn't help for the second play, because at that point the interpreter fact has already been set by the run_once file task in the first play (the facts from the server that runs are copied to all hosts in the play). You could make the example work the way you want by doing one of these:
This doesn't appear to affect We could maybe make an exception for copying facts that look like interpreter discovery if run_once is set on any task, but that seems like it might be a breaking change for some users? @nitzmahone, what do you think of that? Click to expand diffdiff --git a/lib/ansible/plugins/strategy/__init__.py b/lib/ansible/plugins/strategy/__init__.py
index 9c9846a997..cc4255440c 100644
--- a/lib/ansible/plugins/strategy/__init__.py
+++ b/lib/ansible/plugins/strategy/__init__.py
@@ -743,7 +743,20 @@ class StrategyBase:
self._variable_manager.set_host_variable(target_host, var_name, var_value)
else:
cacheable = result_item.pop('_ansible_facts_cacheable', False)
+
+ # If the original task has been run with run_once, only set interpreter discovery vars on the original host
+ interpreter_discovery_vars = {}
+ if original_task.run_once:
+ for var_name in dict(result_item['ansible_facts']):
+ if var_name.startswith('discovered_interpreter_'):
+ interpreter_discovery_vars[var_name] = result_item['ansible_facts'].pop(var_name)
+
for target_host in host_list:
+ facts = result_item['ansible_facts'].copy()
+
+ if target_host == original_host.name:
+ facts.update(interpreter_discovery_vars)
+
# so set_fact is a misnomer but 'cacheable = true' was meant to create an 'actual fact'
# to avoid issues with precedence and confusion with set_fact normal operation,
# we set BOTH fact and nonpersistent_facts (aka hostvar)
@@ -751,9 +764,9 @@ class StrategyBase:
# but for playbook setting it the 'higher' precedence is kept
is_set_fact = original_task.action in C._ACTION_SET_FACT
if not is_set_fact or cacheable:
- self._variable_manager.set_host_facts(target_host, result_item['ansible_facts'].copy())
+ self._variable_manager.set_host_facts(target_host, facts)
if is_set_fact:
- self._variable_manager.set_nonpersistent_facts(target_host, result_item['ansible_facts'].copy())
+ self._variable_manager.set_nonpersistent_facts(target_host, facts)
if 'ansible_stats' in result_item and 'data' in result_item['ansible_stats'] and result_item['ansible_stats']['data']: |
I'm partial to look at this as a bug, since interpreter discovery is something we 'hijack' into facts, we should avoid polluting other hosts when run_once is used |
@s-hertel since this is something i can see us using (abusing) more in the future, we might want to start with a constant for 'non run once facts' to exclude from updating all hosts. |
Yeah, that seems like the right thing to do here- we should absolutely not be propagating a clearly host-specific fact like that in the run_once case... Makes me wonder if we've got it backwards there, like defaulting to any ansible_facts output should only hit the host it ran on, then have $mechanism_tbd to cause the propagation for things that need it, but that's another horse that left the barn a long time ago. |
Well, initially facts were just facts, so it made sense ... till they became nonfacts ... set_facts/include_vars ... and then 'discovery' ... |
Summary
When I run a playbook with
run_once: True
andgather_facts: False
ansible runs interpreter discovery on only the host that the task runs on. If a later play uses the same group withgather_facts: True
, ansible will not re-run interpreter discovery for the other hosts in the group, which can cause failures if the other hosts have different interpreters.Issue Type
Bug Report
Component Name
ansible.legacy.setup
Ansible Version
Configuration
OS / Environment
Host: Ubuntu Focal
Target 1 (Trusty w/ Python2) Dockerfile:
Target 2 (Focal w/ Python3) Dockerfile:
Steps to Reproduce
Inventory:
Playbook:
Command:
Expected Results
I expected ansible to run interpreter discovery on the second host when gathering facts, but instead it failed after trying to directly use
/usr/bin/python
on a host that did not have it.Actual Results
Code of Conduct
The text was updated successfully, but these errors were encountered: