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"