diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index f1dc425c..c25697c6 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -4,6 +4,10 @@ Please drag-drop large logs as text file attachments. Feel free to write an issue in your preferred format, however if in doubt, use the following checklist as a guide for what to include. +* Which version of Ansible are you running? +* Is your version of Ansible patched in any way? +* Are you running with any custom modules, or `module_utils` loaded? + * Have you tried the latest master version from Git? * Do you have some idea of what the underlying problem may be? https://mitogen.rtfd.io/en/stable/ansible.html#common-problems has diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 00000000..1aba38f6 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1 @@ +include LICENSE diff --git a/ansible_mitogen/connection.py b/ansible_mitogen/connection.py index c7e70c43..7bae8c25 100644 --- a/ansible_mitogen/connection.py +++ b/ansible_mitogen/connection.py @@ -547,7 +547,7 @@ class Connection(ansible.plugins.connection.ConnectionBase): def connected(self): return self.context is not None - def _spec_from_via(self, via_spec): + def _spec_from_via(self, proxied_inventory_name, via_spec): """ Produce a dict connection specifiction given a string `via_spec`, of the form `[[become_method:]become_user@]inventory_hostname`. @@ -555,17 +555,20 @@ class Connection(ansible.plugins.connection.ConnectionBase): become_user, _, inventory_name = via_spec.rpartition('@') become_method, _, become_user = become_user.rpartition(':') - via_vars = self.host_vars[inventory_name] - if isinstance(via_vars, jinja2.runtime.Undefined): + # must use __contains__ to avoid a TypeError for a missing host on + # Ansible 2.3. + if self.host_vars is None or inventory_name not in self.host_vars: raise ansible.errors.AnsibleConnectionFailure( self.unknown_via_msg % ( via_spec, - inventory_name, + proxied_inventory_name, ) ) + via_vars = self.host_vars[inventory_name] return ansible_mitogen.transport_config.MitogenViaSpec( inventory_name=inventory_name, + play_context=self._play_context, host_vars=dict(via_vars), # TODO: make it lazy become_method=become_method or None, become_user=become_user or None, @@ -615,7 +618,7 @@ class Connection(ansible.plugins.connection.ConnectionBase): if spec.mitogen_via(): stack = self._stack_from_spec( - self._spec_from_via(spec.mitogen_via()), + self._spec_from_via(spec.inventory_name(), spec.mitogen_via()), stack=stack, seen_names=seen_names + (spec.inventory_name(),), ) diff --git a/ansible_mitogen/process.py b/ansible_mitogen/process.py index 8137af9c..32f4a012 100644 --- a/ansible_mitogen/process.py +++ b/ansible_mitogen/process.py @@ -236,6 +236,41 @@ class MuxProcess(object): if secs: mitogen.debug.dump_to_logger(secs=secs) + def _setup_simplejson(self, responder): + """ + We support serving simplejson for Python 2.4 targets on Ansible 2.3, at + least so the package's own CI Docker scripts can run without external + help, however newer versions of simplejson no longer support Python + 2.4. Therefore override any installed/loaded version with a + 2.4-compatible version we ship in the compat/ directory. + """ + responder.whitelist_prefix('simplejson') + + # issue #536: must be at end of sys.path, in case existing newer + # version is already loaded. + compat_path = os.path.join(os.path.dirname(__file__), 'compat') + sys.path.append(compat_path) + + for fullname, is_pkg, suffix in ( + (u'simplejson', True, '__init__.py'), + (u'simplejson.decoder', False, 'decoder.py'), + (u'simplejson.encoder', False, 'encoder.py'), + (u'simplejson.scanner', False, 'scanner.py'), + ): + path = os.path.join(compat_path, 'simplejson', suffix) + fp = open(path, 'rb') + try: + source = fp.read() + finally: + fp.close() + + responder.add_source_override( + fullname=fullname, + path=path, + source=source, + is_pkg=is_pkg, + ) + def _setup_responder(self, responder): """ Configure :class:`mitogen.master.ModuleResponder` to only permit @@ -243,9 +278,7 @@ class MuxProcess(object): """ responder.whitelist_prefix('ansible') responder.whitelist_prefix('ansible_mitogen') - responder.whitelist_prefix('simplejson') - simplejson_path = os.path.join(os.path.dirname(__file__), 'compat') - sys.path.insert(0, simplejson_path) + self._setup_simplejson(responder) # Ansible 2.3 is compatible with Python 2.4 targets, however # ansible/__init__.py is not. Instead, executor/module_common.py writes diff --git a/ansible_mitogen/target.py b/ansible_mitogen/target.py index 01877e34..58b715ce 100644 --- a/ansible_mitogen/target.py +++ b/ansible_mitogen/target.py @@ -377,6 +377,11 @@ def init_child(econtext, log_level, candidate_temp_dirs): LOG.setLevel(log_level) logging.getLogger('ansible_mitogen').setLevel(log_level) + # issue #536: if the json module is available, remove simplejson from the + # importer whitelist to avoid confusing certain Ansible modules. + if json.__name__ == 'json': + econtext.importer.whitelist.remove('simplejson') + global _fork_parent if FORK_SUPPORTED: mitogen.parent.upgrade_router(econtext) diff --git a/ansible_mitogen/transport_config.py b/ansible_mitogen/transport_config.py index 3401b035..edc9ce1e 100644 --- a/ansible_mitogen/transport_config.py +++ b/ansible_mitogen/transport_config.py @@ -430,12 +430,33 @@ class MitogenViaSpec(Spec): having a configruation problem with connection delegation, the answer to your problem lies in the method implementations below! """ - def __init__(self, inventory_name, host_vars, - become_method, become_user): + def __init__(self, inventory_name, host_vars, become_method, become_user, + play_context): + """ + :param str inventory_name: + The inventory name of the intermediary machine, i.e. not the target + machine. + :param dict host_vars: + The HostVars magic dictionary provided by Ansible in task_vars. + :param str become_method: + If the mitogen_via= spec included a become method, the method it + specifies. + :param str become_user: + If the mitogen_via= spec included a become user, the user it + specifies. + :param PlayContext play_context: + For some global values **only**, the PlayContext used to describe + the real target machine. Values from this object are **strictly + restricted** to values that are Ansible-global, e.g. the passwords + specified interactively. + """ self._inventory_name = inventory_name self._host_vars = host_vars self._become_method = become_method self._become_user = become_user + # Dangerous! You may find a variable you want in this object, but it's + # almost certainly for the wrong machine! + self._dangerous_play_context = play_context def transport(self): return ( @@ -447,15 +468,17 @@ class MitogenViaSpec(Spec): return self._inventory_name def remote_addr(self): + # play_context.py::MAGIC_VARIABLE_MAPPING return ( + self._host_vars.get('ansible_ssh_host') or self._host_vars.get('ansible_host') or self._inventory_name ) def remote_user(self): return ( - self._host_vars.get('ansible_user') or self._host_vars.get('ansible_ssh_user') or + self._host_vars.get('ansible_user') or C.DEFAULT_REMOTE_USER ) @@ -463,7 +486,11 @@ class MitogenViaSpec(Spec): return bool(self._become_user) def become_method(self): - return self._become_method or C.DEFAULT_BECOME_METHOD + return ( + self._become_method or + self._host_vars.get('ansible_become_method') or + C.DEFAULT_BECOME_METHOD + ) def become_user(self): return self._become_user @@ -477,7 +504,6 @@ class MitogenViaSpec(Spec): def password(self): return optional_secret( - # TODO: Might have to come from PlayContext. self._host_vars.get('ansible_ssh_pass') or self._host_vars.get('ansible_password') ) @@ -495,7 +521,6 @@ class MitogenViaSpec(Spec): # interpreter is specified. return parse_python_path(s or '/usr/bin/python') - def private_key_file(self): # TODO: must come from PlayContext too. return ( diff --git a/docs/_static/style.css b/docs/_static/style.css index ec25901f..98dd4bac 100644 --- a/docs/_static/style.css +++ b/docs/_static/style.css @@ -1,4 +1,66 @@ +body { + font-size: 100%; +} + +.sphinxsidebar { + font-size: 80% !important; +} + +.sphinxsidebar h3 { + font-size: 130% !important; +} + +img + p, +h1 + p, +h2 + p, +h3 + p, +h4 + p, +h5 + p +{ + margin-top: 0; +} + +.section > h3:first-child { + margin-top: 15px !important; +} + +.body h1 { font-size: 200% !important; } +.body h2 { font-size: 165% !important; } +.body h3 { font-size: 125% !important; } +.body h4 { font-size: 110% !important; font-weight: bold; } +.body h5 { font-size: 100% !important; font-weight: bold; } + +.body h1, +.body h2, +.body h3, +.body h4, +.body h5 { + margin-top: 30px !important; + color: #7f0000; +} + +.body h1 { + margin-top: 0 !important; +} + +body, +.sphinxsidebar, +.sphinxsidebar h1, +.sphinxsidebar h2, +.sphinxsidebar h3, +.sphinxsidebar h4, +.sphinxsidebar h5, +.body h1, +.body h2, +.body h3, +.body h4, +.body h5 { + /*font-family: sans-serif !important;*/ + font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol !important; +} + + .document { width: 1000px !important; } @@ -38,10 +100,10 @@ div.body p, div.body dd, div.body li, div.body blockquote { width: 150px; } -.mitogen-right-200 { +.mitogen-right-180 { float: right; padding-left: 8px; - width: 200px; + width: 180px; } .mitogen-right-225 { diff --git a/docs/_templates/github.html b/docs/_templates/github.html index 12533b52..bb2b5ee5 100644 --- a/docs/_templates/github.html +++ b/docs/_templates/github.html @@ -1,8 +1,4 @@


-Star -

- -

-GitHub Repository +Star

diff --git a/docs/ansible.rst b/docs/ansible.rst index 17354755..f7788011 100644 --- a/docs/ansible.rst +++ b/docs/ansible.rst @@ -3,7 +3,7 @@ Mitogen for Ansible =================== .. image:: images/ansible/ansible_mitogen.svg - :class: mitogen-right-200 mitogen-logo-wrap + :class: mitogen-right-180 mitogen-logo-wrap An extension to `Ansible`_ is included that implements connections over Mitogen, replacing embedded shell invocations with pure-Python equivalents @@ -246,6 +246,11 @@ container. as duplicate connections between hops, due to not perfectly replicating the configuration Ansible would normally use for the intermediary. + * Intermediary machines cannot use login and become passwords that were + supplied to Ansible interactively. If an intermediary requires a + password, it must be supplied via ``ansible_ssh_pass``, + ``ansible_password``, or ``ansible_become_pass`` inventory variables. + * Automatic tunnelling of SSH-dependent actions, such as the ``synchronize`` module, is not yet supported. This will be added in the 0.3 series. diff --git a/docs/changelog.rst b/docs/changelog.rst index 635115e7..b037ace1 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -10,7 +10,7 @@ Release Notes @@ -128,16 +128,40 @@ Core Library v0.2.5 (2019-02-1?) ------------------- +Fixes +~~~~~ + * `#511 `_, `#536 `_: changes in 0.2.4 to repair ``delegate_to`` handling broke default ``ansible_python_interpreter`` handling. Test coverage was added. +* `#536 `_: changes in 0.2.4 to + support Python 2.4 interacted poorly with modules that imported + ``simplejson`` from a controller that also loaded an incompatible newer + version of ``simplejson``. + +* `#538 `_: the Mitogen source + distribution includes a requisite ``LICENSE`` file. + +* `748f5f67 `_: the + ``ansible_ssh_host`` variable is respected when ``mitogen_via=`` is active. + +* `21ad299d `_: the + precedence of ``ansible_ssh_user`` and ``ansible_user`` variables were + corrected when ``mitogen_via=`` is active. + +* `8ae6ca1d `_: the + ``ansible_become_method`` variable is respected when ``mitogen_via=`` is + active. + + Thanks! ~~~~~~~ Mitogen would not be possible without the support of users. A huge thanks for bug reports, testing, features and fixes in this release contributed by +`Carl George `_, `Guy Knights `_, and `Josh Smift `_, diff --git a/docs/conf.py b/docs/conf.py index abb6e97e..3708a943 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,6 +17,10 @@ html_theme = 'alabaster' html_theme_options = { 'font_family': "Georgia, serif", 'head_font_family': "Georgia, serif", + 'fixed_sidebar': True, + 'show_powered_by': False, + 'pink_2': 'fffafaf', + 'pink_1': '#fff0f0', } htmlhelp_basename = 'mitogendoc' intersphinx_mapping = {'python': ('https://docs.python.org/3', None)} diff --git a/docs/index.rst b/docs/index.rst index 066d6716..6b5deb71 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -11,7 +11,7 @@ Mitogen .. image:: images/mitogen.svg - :class: mitogen-right-200 mitogen-logo-wrap + :class: mitogen-right-180 mitogen-logo-wrap Mitogen is a Python library for writing distributed self-replicating programs. diff --git a/mitogen/core.py b/mitogen/core.py index a48e13ed..9f329d88 100644 --- a/mitogen/core.py +++ b/mitogen/core.py @@ -1125,6 +1125,11 @@ class Importer(object): self.whitelist = list(whitelist) or [''] self.blacklist = list(blacklist) + self.ALWAYS_BLACKLIST + # Preserve copies of the original server-supplied whitelist/blacklist + # for later use by children. + self.master_whitelist = self.whitelist[:] + self.master_blacklist = self.blacklist[:] + # Presence of an entry in this map indicates in-flight GET_MODULE. self._callbacks = {} self._cache = {} diff --git a/mitogen/master.py b/mitogen/master.py index 257fb81b..18323844 100644 --- a/mitogen/master.py +++ b/mitogen/master.py @@ -485,8 +485,10 @@ class ModuleFinder(object): return path, source, is_pkg def _get_module_via_sys_modules(self, fullname): - """Attempt to fetch source code via sys.modules. This is specifically - to support __main__, but it may catch a few more cases.""" + """ + Attempt to fetch source code via sys.modules. This is specifically to + support __main__, but it may catch a few more cases. + """ module = sys.modules.get(fullname) LOG.debug('_get_module_via_sys_modules(%r) -> %r', fullname, module) if not isinstance(module, types.ModuleType): @@ -883,10 +885,13 @@ class ModuleResponder(object): if msg.is_dead: return - LOG.debug('%r._on_get_module(%r)', self, msg.data) - self.get_module_count += 1 stream = self._router.stream_by_id(msg.src_id) + if stream is None: + return + fullname = msg.data.decode() + LOG.debug('%s requested module %s', stream.name, fullname) + self.get_module_count += 1 if fullname in stream.sent_modules: LOG.warning('_on_get_module(): dup request for %r from %r', fullname, stream) diff --git a/mitogen/parent.py b/mitogen/parent.py index 91a4e5eb..814d20e3 100644 --- a/mitogen/parent.py +++ b/mitogen/parent.py @@ -2054,12 +2054,12 @@ class Router(mitogen.core.Router): def get_module_blacklist(self): if mitogen.context_id == 0: return self.responder.blacklist - return self.importer.blacklist + return self.importer.master_blacklist def get_module_whitelist(self): if mitogen.context_id == 0: return self.responder.whitelist - return self.importer.whitelist + return self.importer.master_whitelist def allocate_id(self): return self.id_allocator.allocate() diff --git a/tests/ansible/ansible.cfg b/tests/ansible/ansible.cfg index a968f84a..bec749f7 100644 --- a/tests/ansible/ansible.cfg +++ b/tests/ansible/ansible.cfg @@ -1,5 +1,5 @@ [defaults] -inventory = hosts,lib/inventory +inventory = hosts gathering = explicit strategy_plugins = ../../ansible_mitogen/plugins/strategy action_plugins = lib/action diff --git a/tests/ansible/hosts/transport_config.hosts b/tests/ansible/hosts/transport_config.hosts index d9685811..c98d4f41 100644 --- a/tests/ansible/hosts/transport_config.hosts +++ b/tests/ansible/hosts/transport_config.hosts @@ -1,10 +1,33 @@ # integration/transport_config # Hosts with twiddled configs that need to be checked somehow. + +# tansport() +tc-transport-unset +tc-transport-local ansible_connection=local + +# python_path() tc-python-path-unset tc-python-path-hostvar ansible_python_interpreter=/hostvar/path/to/python - -# local connections get virtualenv python path tc-python-path-local-unset ansible_connection=local tc-python-path-local-explicit ansible_connection=local ansible_python_interpreter=/a/b/c +# remote_addr() +tc-remote-addr-unset # defaults to inventory_hostname +tc-remote-addr-explicit-ssh ansible_ssh_host=ansi.ssh.host +tc-remote-addr-explicit-host ansible_host=ansi.host +tc-remote-addr-explicit-both ansible_ssh_host=a.b.c ansible_host=b.c.d + +# password() +tc-password-unset +tc-password-explicit-ssh ansible_ssh_pass=ansi-ssh-pass +tc-password-explicit-user ansible_password=ansi-pass +tc-password-explicit-both ansible_password=a.b.c ansible_ssh_pass=c.b.a + +# become() +tc-become-unset +tc-become-set + +# become_method() +tc-become-method-unset +tc-become-method-su ansible_become_method=su diff --git a/tests/ansible/integration/_mitogen_only.yml b/tests/ansible/integration/_mitogen_only.yml new file mode 100644 index 00000000..85ef378e --- /dev/null +++ b/tests/ansible/integration/_mitogen_only.yml @@ -0,0 +1,4 @@ +# Include me for plays that can't run on vanilla. +# +- meta: end_play + when: not is_mitogen diff --git a/tests/ansible/integration/transport_config/all.yml b/tests/ansible/integration/transport_config/all.yml index 704760d1..908f6735 100644 --- a/tests/ansible/integration/transport_config/all.yml +++ b/tests/ansible/integration/transport_config/all.yml @@ -1,2 +1,7 @@ - +- include: become.yml +- include: become_method.yml +- include: password.yml - include: python_path.yml +- include: remote_addr.yml +- include: remote_user.yml +- include: transport.yml diff --git a/tests/ansible/integration/transport_config/become.yml b/tests/ansible/integration/transport_config/become.yml new file mode 100644 index 00000000..baa2085e --- /dev/null +++ b/tests/ansible/integration/transport_config/become.yml @@ -0,0 +1,68 @@ +# Each case is followed by mitogen_via= case to test hostvars method. + + +# No become set. +- name: integration/transport_config/become.yml + hosts: tc-become-unset + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert: + that: + - out.result|length == 1 + - out.result[0].method == "ssh" + - out.result[0].kwargs.username == "ansible-cfg-remote-user" + +- hosts: tc-become-unset + vars: {mitogen_via: becomeuser@tc-become-set} + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert: + that: + - out.result|length == 3 + - out.result[0].method == "ssh" + - out.result[0].kwargs.username == "ansible-cfg-remote-user" + + - out.result[1].method == "sudo" + - out.result[1].kwargs.username == "becomeuser" + + - out.result[2].method == "ssh" + - out.result[2].kwargs.hostname == "tc-become-unset" + + +# Become set. +- name: integration/transport_config/become.yml + hosts: tc-become-set + become: true + become_user: becomeuser + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert: + that: + - out.result|length == 2 + - out.result[0].method == "ssh" + - out.result[0].kwargs.username == "ansible-cfg-remote-user" + - out.result[1].method == "sudo" + - out.result[1].kwargs.username == "becomeuser" + +- hosts: tc-become-set + vars: {mitogen_via: tc-become-unset} + become: true + become_user: becomeuser + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert: + that: + - out.result|length == 3 + - out.result[0].method == "ssh" + - out.result[0].kwargs.hostname == "tc-become-unset" + - out.result[0].kwargs.username == "ansible-cfg-remote-user" + + - out.result[1].method == "ssh" + - out.result[1].kwargs.hostname == "tc-become-set" + + - out.result[2].method == "sudo" + - out.result[2].kwargs.username == "becomeuser" diff --git a/tests/ansible/integration/transport_config/become_method.yml b/tests/ansible/integration/transport_config/become_method.yml new file mode 100644 index 00000000..5129e5b8 --- /dev/null +++ b/tests/ansible/integration/transport_config/become_method.yml @@ -0,0 +1,83 @@ +# Each case is followed by mitogen_via= case to test hostvars method. + + +# No become-method set. +- name: integration/transport_config/become-method.yml + hosts: tc-become-method-unset + become: true + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert: + that: + - out.result|length == 2 + - out.result[0].method == "ssh" + - out.result[1].method == "sudo" + +- hosts: tc-become-method-unset + vars: {mitogen_via: becomeuser@tc-become-method-su} + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert: + that: + - out.result|length == 3 + - out.result[0].method == "ssh" + - out.result[1].method == "su" + - out.result[1].kwargs.username == "becomeuser" + - out.result[2].method == "ssh" + - out.result[2].kwargs.hostname == "tc-become-method-unset" + + +# ansible_become_method=su +- hosts: tc-become-method-su + become: true + become_user: becomeuser + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert: + that: + - out.result|length == 2 + - out.result[0].method == "ssh" + - out.result[1].method == "su" + - out.result[1].kwargs.username == "becomeuser" + +- hosts: tc-become-method-su + vars: {mitogen_via: tc-become-method-unset} + become: true + become_user: becomeuser + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert: + that: + - out.result|length == 3 + - out.result[0].method == "ssh" + - out.result[0].kwargs.hostname == "tc-become-method-unset" + + - out.result[1].method == "ssh" + - out.result[1].kwargs.hostname == "tc-become-method-su" + + - out.result[2].method == "su" + - out.result[2].kwargs.username == "becomeuser" + + + +# mitogen_via used to specify explicit become method +- hosts: tc-become-method-unset + vars: {mitogen_via: "doas:doasuser@tc-become-method-su"} + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert: + that: + - out.result|length == 3 + - out.result[0].method == "ssh" + - out.result[0].kwargs.hostname == "tc-become-method-su" + + - out.result[1].method == "doas" + - out.result[1].kwargs.username == "doasuser" + + - out.result[2].method == "ssh" + - out.result[2].kwargs.hostname == "tc-become-method-unset" diff --git a/tests/ansible/integration/transport_config/password.yml b/tests/ansible/integration/transport_config/password.yml new file mode 100644 index 00000000..ac236d66 --- /dev/null +++ b/tests/ansible/integration/transport_config/password.yml @@ -0,0 +1,94 @@ +# Each case is followed by mitogen_via= case to test hostvars method. + + +# When no ansible_ssh_pass/ansible_password= is set, password comes via +# interactive input. +- name: integration/transport_config/password.yml + hosts: tc-password-unset + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.password + right: "" # actually null, but assert_equal limitation + +- hosts: tc-password-unset + vars: {mitogen_via: tc-password-explicit-ssh} + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.password + right: "ansi-ssh-pass" + - assert_equal: + left: out.result[1].kwargs.password + right: "" + + +# ansible_ssh_user= + +- hosts: tc-password-explicit-ssh + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.password + right: "ansi-ssh-pass" + +- hosts: tc-password-explicit-ssh + vars: {mitogen_via: tc-password-unset} + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.password + right: "" + - assert_equal: + left: out.result[1].kwargs.password + right: "ansi-ssh-pass" + + +# ansible_user= + +- hosts: tc-password-explicit-pass + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.password + right: "ansi-pass" + +- hosts: tc-password-explicit-pass + vars: {mitogen_via: tc-password-unset} + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.password + right: "" + - assert_equal: + left: out.result[1].kwargs.password + right: "ansi-pass" + + +# both; ansible_ssh_user= takes precedence according to play_context.py. + +- hosts: tc-password-explicit-both + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.password + right: "c.b.a" + +- hosts: tc-password-explicit-both + vars: {mitogen_via: tc-password-unset} + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.password + right: "" + - assert_equal: + left: out.result[1].kwargs.password + right: "c.b.a" diff --git a/tests/ansible/integration/transport_config/python_path.yml b/tests/ansible/integration/transport_config/python_path.yml index d0c16788..c5359e93 100644 --- a/tests/ansible/integration/transport_config/python_path.yml +++ b/tests/ansible/integration/transport_config/python_path.yml @@ -1,4 +1,5 @@ # related: issue #511, #536 +# Each case is followed by mitogen_via= case to test hostvars method. # When no ansible_python_interpreter is set, executor/module_common.py chooses @@ -6,57 +7,108 @@ - name: integration/transport_config/python_path.yml hosts: tc-python-path-unset tasks: - - meta: end_play - when: not is_mitogen - - mitogen_get_stack: - register: out + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} - assert_equal: left: out.result[0].kwargs.python_path right: ["/usr/bin/python"] +- hosts: tc-python-path-hostvar + vars: {mitogen_via: tc-python-path-unset} + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.python_path + right: ["/usr/bin/python"] + - assert_equal: + left: out.result[1].kwargs.python_path + right: ["/hostvar/path/to/python"] + # Non-localhost with explicit ansible_python_interpreter - hosts: tc-python-path-hostvar tasks: - - meta: end_play - when: not is_mitogen - - mitogen_get_stack: - register: out + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} - assert_equal: left: out.result[0].kwargs.python_path right: [/hostvar/path/to/python] +- hosts: tc-python-path-unset + vars: {mitogen_via: tc-python-path-hostvar} + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.python_path + right: ["/hostvar/path/to/python"] + - assert_equal: + left: out.result[1].kwargs.python_path + right: ["/usr/bin/python"] + # Implicit localhost gets ansible_python_interpreter=virtualenv interpreter - hosts: localhost tasks: - - meta: end_play - when: not is_mitogen - - mitogen_get_stack: - register: out + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.python_path + right: ["{{ansible_playbook_python}}"] + +- hosts: tc-python-path-unset + vars: {mitogen_via: localhost} + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} - assert_equal: left: out.result[0].kwargs.python_path right: ["{{ansible_playbook_python}}"] + - assert_equal: + left: out.result[1].kwargs.python_path + right: ["/usr/bin/python"] # explicit local connections get the same treatment as everything else. - hosts: tc-python-path-local-unset tasks: - - meta: end_play - when: not is_mitogen - - mitogen_get_stack: - register: out + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} - assert_equal: left: out.result[0].kwargs.python_path right: ["/usr/bin/python"] +- hosts: localhost + vars: {mitogen_via: tc-python-path-local-unset} + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.python_path + right: ["/usr/bin/python"] + - assert_equal: + left: out.result[1].kwargs.python_path + right: ["{{ansible_playbook_python}}"] + +# explicit local connection with explicit interpreter - hosts: tc-python-path-local-explicit tasks: - - meta: end_play - when: not is_mitogen - - mitogen_get_stack: - register: out + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} - assert_equal: left: out.result[0].kwargs.python_path right: ["/a/b/c"] + +- hosts: localhost + vars: {mitogen_via: tc-python-path-local-explicit} + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.python_path + right: ["/a/b/c"] + - assert_equal: + left: out.result[1].kwargs.python_path + right: ["{{ansible_playbook_python}}"] diff --git a/tests/ansible/integration/transport_config/remote_addr.yml b/tests/ansible/integration/transport_config/remote_addr.yml new file mode 100644 index 00000000..b9887202 --- /dev/null +++ b/tests/ansible/integration/transport_config/remote_addr.yml @@ -0,0 +1,95 @@ + +# Each case is followed by mitogen_via= case to test hostvars method. + + +# When no ansible_host/ansible_ssh_host= is set, hostname is same as inventory +# name. +- name: integration/transport_config/remote_addr.yml + hosts: tc-remote-addr-unset + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.hostname + right: "tc-remote-addr-unset" + +- hosts: tc-remote-addr-unset + vars: {mitogen_via: tc-remote-addr-explicit-ssh} + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.hostname + right: "ansi.ssh.host" + - assert_equal: + left: out.result[1].kwargs.hostname + right: "tc-remote-addr-unset" + + +# ansible_ssh_host= + +- hosts: tc-remote-addr-explicit-ssh + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.hostname + right: "ansi.ssh.host" + +- hosts: tc-remote-addr-explicit-ssh + vars: {mitogen_via: tc-remote-addr-unset} + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.hostname + right: "tc-remote-addr-unset" + - assert_equal: + left: out.result[1].kwargs.hostname + right: "ansi.ssh.host" + + +# ansible_host= + +- hosts: tc-remote-addr-explicit-host + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.hostname + right: "ansi.host" + +- hosts: tc-remote-addr-explicit-host + vars: {mitogen_via: tc-remote-addr-unset} + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.hostname + right: "tc-remote-addr-unset" + - assert_equal: + left: out.result[1].kwargs.hostname + right: "ansi.host" + + +# both; ansible_ssh_host= takes precedence according to play_context.py. + +- hosts: tc-remote-addr-explicit-both + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.hostname + right: "a.b.c" + +- hosts: tc-remote-addr-explicit-both + vars: {mitogen_via: tc-remote-addr-unset} + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.hostname + right: "tc-remote-addr-unset" + - assert_equal: + left: out.result[1].kwargs.hostname + right: "a.b.c" diff --git a/tests/ansible/integration/transport_config/remote_user.yml b/tests/ansible/integration/transport_config/remote_user.yml new file mode 100644 index 00000000..b873fcbe --- /dev/null +++ b/tests/ansible/integration/transport_config/remote_user.yml @@ -0,0 +1,96 @@ + +# Each case is followed by mitogen_via= case to test hostvars method. + + +# When no ansible_user/ansible_ssh_user= is set, username is +# C.DEFAULT_REMOTE_USER. +- name: integration/transport_config/remote_user.yml + hosts: tc-remote-user-unset + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.username + # We set DEFAULT_REMOTE_USER in our ansible.cfg + right: "ansible-cfg-remote-user" + +- hosts: tc-remote-user-unset + vars: {mitogen_via: tc-remote-user-explicit-ssh} + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.username + right: "ansi-ssh-user" + - assert_equal: + left: out.result[1].kwargs.username + right: "ansible-cfg-remote-user" + + +# ansible_ssh_user= + +- hosts: tc-remote-user-explicit-ssh + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.username + right: "ansi-ssh-user" + +- hosts: tc-remote-user-explicit-ssh + vars: {mitogen_via: tc-remote-user-unset} + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.username + right: "ansible-cfg-remote-user" + - assert_equal: + left: out.result[1].kwargs.username + right: "ansi-ssh-user" + + +# ansible_user= + +- hosts: tc-remote-user-explicit-user + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.username + right: "ansi-user" + +- hosts: tc-remote-user-explicit-host + vars: {mitogen_via: tc-remote-user-unset} + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.username + right: "ansible-cfg-remote-user" + - assert_equal: + left: out.result[1].kwargs.username + right: "ansi-user" + + +# both; ansible_ssh_user= takes precedence according to play_context.py. + +- hosts: tc-remote-user-explicit-both + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.username + right: "c.b.a" + +- hosts: tc-remote-user-explicit-both + vars: {mitogen_via: tc-remote-user-unset} + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].kwargs.username + right: "ansible-cfg-remote-user" + - assert_equal: + left: out.result[1].kwargs.username + right: "c.b.a" diff --git a/tests/ansible/integration/transport_config/transport.yml b/tests/ansible/integration/transport_config/transport.yml new file mode 100644 index 00000000..efedc8d4 --- /dev/null +++ b/tests/ansible/integration/transport_config/transport.yml @@ -0,0 +1,48 @@ +# Each case is followed by mitogen_via= case to test hostvars method. + + +# When no ansible_connection= is set, transport comes via ansible.cfg ("smart" +# is parsed away to either paramiko or ssh). +- name: integration/transport_config/transport.yml + hosts: tc-transport-unset + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].method + right: "ssh" + +- hosts: tc-transport-local + vars: {mitogen_via: tc-transport-unset} + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].method + right: "ssh" + - assert_equal: + left: out.result[1].method + right: "local" + + +# ansible_connection=local + +- hosts: tc-transport-local + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].method + right: "local" + +- hosts: tc-transport-unset + vars: {mitogen_via: tc-transport-local} + tasks: + - include: ../_mitogen_only.yml + - {mitogen_get_stack: {}, register: out} + - assert_equal: + left: out.result[0].method + right: "local" + - assert_equal: + left: out.result[1].method + right: "ssh"