Skip python interpreter discovery for 'forced local' module execution (#74824)

mostly for use with network_os
  use 'remote is local' property as indicator
  ensure task_vars are as expected in test
pull/75430/head
Brian Coca 3 years ago committed by GitHub
parent 1d2af0b89e
commit 8d41b97329
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,2 @@
bugfixes:
- do not trigger interpreter discovery in the forced_local module path as they should use the ansible playbook python unless otherwise configured.

@ -587,7 +587,7 @@ def _slurp(path):
return data return data
def _get_shebang(interpreter, task_vars, templar, args=tuple()): def _get_shebang(interpreter, task_vars, templar, args=tuple(), remote_is_local=False):
""" """
Note not stellar API: Note not stellar API:
Returns None instead of always returning a shebang line. Doing it this Returns None instead of always returning a shebang line. Doing it this
@ -595,44 +595,59 @@ def _get_shebang(interpreter, task_vars, templar, args=tuple()):
file rather than trust that we reformatted what they already have file rather than trust that we reformatted what they already have
correctly. correctly.
""" """
interpreter_name = os.path.basename(interpreter).strip()
# FUTURE: add logical equivalence for python3 in the case of py3-only modules # FUTURE: add logical equivalence for python3 in the case of py3-only modules
# check for first-class interpreter config interpreter_name = os.path.basename(interpreter).strip()
interpreter_config_key = "INTERPRETER_%s" % interpreter_name.upper()
if C.config.get_configuration_definitions().get(interpreter_config_key): # name for interpreter var
# a config def exists for this interpreter type; consult config for the value interpreter_config = u'ansible_%s_interpreter' % interpreter_name
interpreter_out = C.config.get_config_value(interpreter_config_key, variables=task_vars) # key for config
discovered_interpreter_config = u'discovered_interpreter_%s' % interpreter_name interpreter_config_key = "INTERPRETER_%s" % interpreter_name.upper()
interpreter_out = templar.template(interpreter_out.strip()) interpreter_out = None
facts_from_task_vars = task_vars.get('ansible_facts', {}) # looking for python, rest rely on matching vars
if interpreter_name == 'python':
# skip detection for network os execution, use playbook supplied one if possible
if remote_is_local:
interpreter_out = task_vars['ansible_playbook_python']
# handle interpreter discovery if requested # a config def exists for this interpreter type; consult config for the value
if interpreter_out in ['auto', 'auto_legacy', 'auto_silent', 'auto_legacy_silent']: elif C.config.get_configuration_definition(interpreter_config_key):
if discovered_interpreter_config not in facts_from_task_vars:
# interpreter discovery is desired, but has not been run for this host
raise InterpreterDiscoveryRequiredError("interpreter discovery needed",
interpreter_name=interpreter_name,
discovery_mode=interpreter_out)
else:
interpreter_out = facts_from_task_vars[discovered_interpreter_config]
else:
# a config def does not exist for this interpreter type; consult vars for a possible direct override
interpreter_config = u'ansible_%s_interpreter' % interpreter_name
if interpreter_config not in task_vars: interpreter_from_config = C.config.get_config_value(interpreter_config_key, variables=task_vars)
return None, interpreter interpreter_out = templar.template(interpreter_from_config.strip())
interpreter_out = templar.template(task_vars[interpreter_config].strip()) # handle interpreter discovery if requested or empty interpreter was provided
if not interpreter_out or interpreter_out in ['auto', 'auto_legacy', 'auto_silent', 'auto_legacy_silent']:
shebang = u'#!' + interpreter_out discovered_interpreter_config = u'discovered_interpreter_%s' % interpreter_name
facts_from_task_vars = task_vars.get('ansible_facts', {})
if args: if discovered_interpreter_config not in facts_from_task_vars:
shebang = shebang + u' ' + u' '.join(args) # interpreter discovery is desired, but has not been run for this host
raise InterpreterDiscoveryRequiredError("interpreter discovery needed", interpreter_name=interpreter_name, discovery_mode=interpreter_out)
else:
interpreter_out = facts_from_task_vars[discovered_interpreter_config]
else:
raise InterpreterDiscoveryRequiredError("interpreter discovery required", interpreter_name=interpreter_name, discovery_mode='auto_legacy')
elif interpreter_config in task_vars:
# for non python we consult vars for a possible direct override
interpreter_out = templar.template(task_vars.get(interpreter_config).strip())
if not interpreter_out:
# nothing matched(None) or in case someone configures empty string or empty intepreter
interpreter_out = interpreter
shebang = None
elif interpreter_out == interpreter:
# no change, no new shebang
shebang = None
else:
# set shebang cause we changed interpreter
shebang = u'#!' + interpreter_out
if args:
shebang = shebang + u' ' + u' '.join(args)
return shebang, interpreter_out return shebang, interpreter_out
@ -1067,7 +1082,7 @@ def _add_module_to_zip(zf, remote_module_fqn, b_module_data):
def _find_module_utils(module_name, b_module_data, module_path, module_args, task_vars, templar, module_compression, async_timeout, become, def _find_module_utils(module_name, b_module_data, module_path, module_args, task_vars, templar, module_compression, async_timeout, become,
become_method, become_user, become_password, become_flags, environment): become_method, become_user, become_password, become_flags, environment, remote_is_local=False):
""" """
Given the source of the module, convert it to a Jinja2 template to insert Given the source of the module, convert it to a Jinja2 template to insert
module code and return whether it's a new or old style module. module code and return whether it's a new or old style module.
@ -1221,7 +1236,7 @@ def _find_module_utils(module_name, b_module_data, module_path, module_args, tas
'Look at traceback for that process for debugging information.') 'Look at traceback for that process for debugging information.')
zipdata = to_text(zipdata, errors='surrogate_or_strict') zipdata = to_text(zipdata, errors='surrogate_or_strict')
shebang, interpreter = _get_shebang(u'/usr/bin/python', task_vars, templar) shebang, interpreter = _get_shebang(u'/usr/bin/python', task_vars, templar, remote_is_local=remote_is_local)
if shebang is None: if shebang is None:
shebang = u'#!/usr/bin/python' shebang = u'#!/usr/bin/python'
@ -1313,7 +1328,7 @@ def _find_module_utils(module_name, b_module_data, module_path, module_args, tas
def modify_module(module_name, module_path, module_args, templar, task_vars=None, module_compression='ZIP_STORED', async_timeout=0, become=False, def modify_module(module_name, module_path, module_args, templar, task_vars=None, module_compression='ZIP_STORED', async_timeout=0, become=False,
become_method=None, become_user=None, become_password=None, become_flags=None, environment=None): become_method=None, become_user=None, become_password=None, become_flags=None, environment=None, remote_is_local=False):
""" """
Used to insert chunks of code into modules before transfer rather than Used to insert chunks of code into modules before transfer rather than
doing regular python imports. This allows for more efficient transfer in doing regular python imports. This allows for more efficient transfer in
@ -1345,7 +1360,7 @@ def modify_module(module_name, module_path, module_args, templar, task_vars=None
(b_module_data, module_style, shebang) = _find_module_utils(module_name, b_module_data, module_path, module_args, task_vars, templar, module_compression, (b_module_data, module_style, shebang) = _find_module_utils(module_name, b_module_data, module_path, module_args, task_vars, templar, module_compression,
async_timeout=async_timeout, become=become, become_method=become_method, async_timeout=async_timeout, become=become, become_method=become_method,
become_user=become_user, become_password=become_password, become_flags=become_flags, become_user=become_user, become_password=become_password, become_flags=become_flags,
environment=environment) environment=environment, remote_is_local=remote_is_local)
if module_style == 'binary': if module_style == 'binary':
return (b_module_data, module_style, to_text(shebang, nonstring='passthru')) return (b_module_data, module_style, to_text(shebang, nonstring='passthru'))
@ -1359,7 +1374,7 @@ def modify_module(module_name, module_path, module_args, templar, task_vars=None
# _get_shebang() takes text strings # _get_shebang() takes text strings
args = [to_text(a, errors='surrogate_or_strict') for a in args] args = [to_text(a, errors='surrogate_or_strict') for a in args]
interpreter = args[0] interpreter = args[0]
b_new_shebang = to_bytes(_get_shebang(interpreter, task_vars, templar, args[1:])[0], b_new_shebang = to_bytes(_get_shebang(interpreter, task_vars, templar, args[1:], remote_is_local=remote_is_local)[0],
errors='surrogate_or_strict', nonstring='passthru') errors='surrogate_or_strict', nonstring='passthru')
if b_new_shebang: if b_new_shebang:

@ -234,6 +234,7 @@ class ActionBase(with_metaclass(ABCMeta, object)):
module_compression=self._play_context.module_compression, module_compression=self._play_context.module_compression,
async_timeout=self._task.async_val, async_timeout=self._task.async_val,
environment=final_environment, environment=final_environment,
remote_is_local=bool(getattr(self._connection, '_remote_is_local', False)),
**become_kwargs) **become_kwargs)
break break
except InterpreterDiscoveryRequiredError as idre: except InterpreterDiscoveryRequiredError as idre:

@ -159,7 +159,8 @@ class TestActionBase(unittest.TestCase):
mock_task.args = dict(a=1, foo='fö〩') mock_task.args = dict(a=1, foo='fö〩')
mock_connection.module_implementation_preferences = ('',) mock_connection.module_implementation_preferences = ('',)
(style, shebang, data, path) = action_base._configure_module(mock_task.action, mock_task.args, (style, shebang, data, path) = action_base._configure_module(mock_task.action, mock_task.args,
task_vars=dict(ansible_python_interpreter='/usr/bin/python')) task_vars=dict(ansible_python_interpreter='/usr/bin/python',
ansible_playbook_python='/usr/bin/python'))
self.assertEqual(style, "new") self.assertEqual(style, "new")
self.assertEqual(shebang, u"#!/usr/bin/python") self.assertEqual(shebang, u"#!/usr/bin/python")

Loading…
Cancel
Save