|
60 | 60 | except ImportError: |
61 | 61 | from ansible.vars.unsafe_proxy import wrap_var |
62 | 62 |
|
| 63 | +try: |
| 64 | + # ansible 2.8 moved remove_internal_keys to the clean module |
| 65 | + from ansible.vars.clean import remove_internal_keys |
| 66 | +except ImportError: |
| 67 | + try: |
| 68 | + from ansible.vars.manager import remove_internal_keys |
| 69 | + except ImportError: |
| 70 | + # ansible 2.3.3 has remove_internal_keys as a protected func on the action class |
| 71 | + # we'll fallback to calling self._remove_internal_keys in this case |
| 72 | + remove_internal_keys = lambda a: "Not found" |
| 73 | + |
63 | 74 |
|
64 | 75 | LOG = logging.getLogger(__name__) |
65 | 76 |
|
@@ -108,6 +119,16 @@ def __init__(self, task, connection, *args, **kwargs): |
108 | 119 | if not isinstance(connection, ansible_mitogen.connection.Connection): |
109 | 120 | _, self.__class__ = type(self).__bases__ |
110 | 121 |
|
| 122 | + # required for python interpreter discovery |
| 123 | + connection.templar = self._templar |
| 124 | + self._finding_python_interpreter = False |
| 125 | + self._rediscovered_python = False |
| 126 | + # redeclaring interpreter discovery vars here in case running ansible < 2.8.0 |
| 127 | + self._discovered_interpreter_key = None |
| 128 | + self._discovered_interpreter = False |
| 129 | + self._discovery_deprecation_warnings = [] |
| 130 | + self._discovery_warnings = [] |
| 131 | + |
111 | 132 | def run(self, tmp=None, task_vars=None): |
112 | 133 | """ |
113 | 134 | Override run() to notify Connection of task-specific data, so it has a |
@@ -370,6 +391,34 @@ def _execute_module(self, module_name=None, module_args=None, tmp=None, |
370 | 391 | # on _execute_module(). |
371 | 392 | self._remove_tmp_path(tmp) |
372 | 393 |
|
| 394 | + # prevents things like discovered_interpreter_* or ansible_discovered_interpreter_* from being set |
| 395 | + # handle ansible 2.3.3 that has remove_internal_keys in a different place |
| 396 | + check = remove_internal_keys(result) |
| 397 | + if check == 'Not found': |
| 398 | + self._remove_internal_keys(result) |
| 399 | + |
| 400 | + # taken from _execute_module of ansible 2.8.6 |
| 401 | + # propagate interpreter discovery results back to the controller |
| 402 | + if self._discovered_interpreter_key: |
| 403 | + if result.get('ansible_facts') is None: |
| 404 | + result['ansible_facts'] = {} |
| 405 | + |
| 406 | + # only cache discovered_interpreter if we're not running a rediscovery |
| 407 | + # rediscovery happens in places like docker connections that could have different |
| 408 | + # python interpreters than the main host |
| 409 | + if not self._rediscovered_python: |
| 410 | + result['ansible_facts'][self._discovered_interpreter_key] = self._discovered_interpreter |
| 411 | + |
| 412 | + if self._discovery_warnings: |
| 413 | + if result.get('warnings') is None: |
| 414 | + result['warnings'] = [] |
| 415 | + result['warnings'].extend(self._discovery_warnings) |
| 416 | + |
| 417 | + if self._discovery_deprecation_warnings: |
| 418 | + if result.get('deprecations') is None: |
| 419 | + result['deprecations'] = [] |
| 420 | + result['deprecations'].extend(self._discovery_deprecation_warnings) |
| 421 | + |
373 | 422 | return wrap_var(result) |
374 | 423 |
|
375 | 424 | def _postprocess_response(self, result): |
@@ -407,17 +456,54 @@ def _low_level_execute_command(self, cmd, sudoable=True, in_data=None, |
407 | 456 | """ |
408 | 457 | LOG.debug('_low_level_execute_command(%r, in_data=%r, exe=%r, dir=%r)', |
409 | 458 | cmd, type(in_data), executable, chdir) |
| 459 | + |
410 | 460 | if executable is None: # executable defaults to False |
411 | 461 | executable = self._play_context.executable |
412 | 462 | if executable: |
413 | 463 | cmd = executable + ' -c ' + shlex_quote(cmd) |
414 | 464 |
|
415 | | - rc, stdout, stderr = self._connection.exec_command( |
416 | | - cmd=cmd, |
417 | | - in_data=in_data, |
418 | | - sudoable=sudoable, |
419 | | - mitogen_chdir=chdir, |
420 | | - ) |
| 465 | + # TODO: HACK: if finding python interpreter then we need to keep |
| 466 | + # calling exec_command until we run into the right python we'll use |
| 467 | + # chicken-and-egg issue, mitogen needs a python to run low_level_execute_command |
| 468 | + # which is required by Ansible's discover_interpreter function |
| 469 | + if self._finding_python_interpreter: |
| 470 | + possible_pythons = [ |
| 471 | + '/usr/bin/python', |
| 472 | + 'python3', |
| 473 | + 'python3.7', |
| 474 | + 'python3.6', |
| 475 | + 'python3.5', |
| 476 | + 'python2.7', |
| 477 | + 'python2.6', |
| 478 | + '/usr/libexec/platform-python', |
| 479 | + '/usr/bin/python3', |
| 480 | + 'python' |
| 481 | + ] |
| 482 | + else: |
| 483 | + # not used, just adding a filler value |
| 484 | + possible_pythons = ['python'] |
| 485 | + |
| 486 | + def _run_cmd(): |
| 487 | + return self._connection.exec_command( |
| 488 | + cmd=cmd, |
| 489 | + in_data=in_data, |
| 490 | + sudoable=sudoable, |
| 491 | + mitogen_chdir=chdir, |
| 492 | + ) |
| 493 | + |
| 494 | + for possible_python in possible_pythons: |
| 495 | + try: |
| 496 | + self._possible_python_interpreter = possible_python |
| 497 | + rc, stdout, stderr = _run_cmd() |
| 498 | + # TODO: what exception is thrown? |
| 499 | + except: |
| 500 | + # we've reached the last python attempted and failed |
| 501 | + # TODO: could use enumerate(), need to check which version of python first had it though |
| 502 | + if possible_python == 'python': |
| 503 | + raise |
| 504 | + else: |
| 505 | + continue |
| 506 | + |
421 | 507 | stdout_text = to_text(stdout, errors=encoding_errors) |
422 | 508 |
|
423 | 509 | return { |
|
0 commit comments