Merge pull request #1316 from moreati/release-v0.3.25

Release v0.3.25
pull/1321/head v0.3.25
Alex Willmer 4 months ago committed by GitHub
commit 021d712edc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,18 @@
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
VERSION="$1"
curl \
--fail \
--location \
--no-progress-meter \
--remote-name \
"https://downloads.sourceforge.net/project/sshpass/sshpass/${VERSION}/sshpass-${VERSION}.tar.gz"
tar xvf "sshpass-${VERSION}.tar.gz"
cd "sshpass-${VERSION}"
./configure
sudo make install

@ -17,17 +17,6 @@ with ci_lib.Fold('unit_tests'):
with ci_lib.Fold('job_setup'):
os.chmod(ci_lib.TESTS_SSH_PRIVATE_KEY_FILE, int('0600', 8))
# NOTE: sshpass v1.06 causes errors so pegging to 1.05 -> "msg": "Error when changing password","out": "passwd: DS error: eDSAuthFailed\n",
# there's a checksum error with "brew install http://git.io/sshpass.rb" though, so installing manually
if not ci_lib.exists_in_path('sshpass'):
subprocess.check_call(
"curl -O -L https://sourceforge.net/projects/sshpass/files/sshpass/1.05/sshpass-1.05.tar.gz && \
tar xvf sshpass-1.05.tar.gz && \
cd sshpass-1.05 && \
./configure && \
sudo make install",
shell=True,
)
with ci_lib.Fold('machine_prep'):

@ -177,10 +177,16 @@ jobs:
- name: Ans_313_11
python_version: '3.13'
tox_env: py313-mode_ansible-ansible11
- name: Ans_313_12
python_version: '3.13'
tox_env: py313-mode_ansible-ansible12
- name: Van_313_11
python_version: '3.13'
tox_env: py313-mode_ansible-ansible11-strategy_linear
- name: Van_313_12
python_version: '3.13'
tox_env: py313-mode_ansible-ansible12-strategy_linear
- name: Mito_313
python_version: '3.13'
@ -268,11 +274,19 @@ jobs:
tox_env: py313-mode_mitogen
- name: Loc_313_11
sshpass_version: "1.10"
tox_env: py313-mode_localhost-ansible11
- name: Van_313_11
sshpass_version: "1.10"
tox_env: py313-mode_localhost-ansible11-strategy_linear
- name: Loc_313_12
tox_env: py313-mode_localhost-ansible12
- name: Van_313_12
tox_env: py313-mode_localhost-ansible12-strategy_linear
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
@ -305,6 +319,8 @@ jobs:
# GitHub macOS 12 images: python2.7 is installed, but not on $PATH
echo "/Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7: sys.executable: $(/Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7 -c 'import sys; print(sys.executable)')"
fi
- run: .ci/install_sshpass ${{ matrix.sshpass_version }}
if: ${{ matrix.sshpass_version }}
- name: Install tooling
run: |
set -o errexit -o nounset -o pipefail

1
.gitignore vendored

@ -1,3 +1,4 @@
.ansible/
.coverage
.tox
.venv

@ -147,7 +147,7 @@ def _connect_ssh(spec):
'ssh_path': spec.ssh_executable(),
'connect_timeout': spec.timeout(),
'ssh_args': spec.ssh_args(),
'ssh_debug_level': spec.mitogen_ssh_debug_level(),
'ssh_debug_level': spec.verbosity(),
'remote_name': get_remote_name(spec),
'keepalive_count': (
spec.mitogen_ssh_keepalive_count() or 10
@ -767,7 +767,7 @@ class Connection(ansible.plugins.connection.ConnectionBase):
C.BECOME_ALLOW_SAME_USER):
stack += (CONNECTION_METHOD[spec.become_method()](spec),)
return stack
return ansible_mitogen.utils.unsafe.cast(stack)
def _build_stack(self):
"""

@ -49,7 +49,7 @@ __all__ = [
ANSIBLE_VERSION_MIN = (2, 10)
ANSIBLE_VERSION_MAX = (2, 18)
ANSIBLE_VERSION_MAX = (2, 19)
NEW_VERSION_MSG = (
"Your Ansible version (%s) is too recent. The most recent version\n"

@ -29,6 +29,7 @@
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import json
import logging
import os
import pwd
@ -42,7 +43,6 @@ import ansible.vars.clean
from ansible.module_utils.common.text.converters import to_bytes, to_text
from ansible.module_utils.six.moves import shlex_quote
from ansible.parsing.utils.jsonify import jsonify
import mitogen.core
import mitogen.select
@ -219,8 +219,13 @@ class ActionModuleMixin(ansible.plugins.action.ActionBase):
Used by the base _execute_module(), and in <2.4 also by the template
action module, and probably others.
"""
if data is None and ansible_mitogen.utils.ansible_version[:2] <= (2, 18):
data = '{}'
if isinstance(data, dict):
data = jsonify(data)
try:
data = json.dumps(data, ensure_ascii=False)
except UnicodeDecodeError:
data = json.dumps(data)
if not isinstance(data, bytes):
data = to_bytes(data, errors='surrogate_or_strict')
@ -402,15 +407,17 @@ class ActionModuleMixin(ansible.plugins.action.ActionBase):
if not self._mitogen_rediscovered_interpreter:
result['ansible_facts'][self._discovered_interpreter_key] = self._discovered_interpreter
if self._discovery_warnings:
discovery_warnings = getattr(self, '_discovery_warnings', [])
if discovery_warnings:
if result.get('warnings') is None:
result['warnings'] = []
result['warnings'].extend(self._discovery_warnings)
result['warnings'].extend(discovery_warnings)
if self._discovery_deprecation_warnings:
discovery_deprecation_warnings = getattr(self, '_discovery_deprecation_warnings', [])
if discovery_deprecation_warnings:
if result.get('deprecations') is None:
result['deprecations'] = []
result['deprecations'].extend(self._discovery_deprecation_warnings)
result['deprecations'].extend(discovery_deprecation_warnings)
return ansible.utils.unsafe_proxy.wrap_var(result)
@ -429,7 +436,10 @@ class ActionModuleMixin(ansible.plugins.action.ActionBase):
"stderr": "stderr data"
}
"""
data = self._parse_returned_data(result)
if ansible_mitogen.utils.ansible_version[:2] >= (2, 19):
data = self._parse_returned_data(result, profile='legacy')
else:
data = self._parse_returned_data(result)
# Cutpasted from the base implementation.
if 'stdout' in data and 'stdout_lines' not in data:

@ -170,6 +170,7 @@ class Planner(object):
"""
binding = self._inv.connection.get_binding()
kwargs = ansible_mitogen.utils.unsafe.cast(kwargs)
new = dict((mitogen.core.UnicodeType(k), kwargs[k])
for k in kwargs)
new.setdefault('good_temp_dir',
@ -204,7 +205,7 @@ class BinaryPlanner(Planner):
module=self._inv.module_name,
path=self._inv.module_path,
json_args=json.dumps(self._inv.module_args),
env=self._inv.env,
env=ansible_mitogen.utils.unsafe.cast(self._inv.env),
**kwargs
)
@ -546,7 +547,7 @@ def _invoke_async_task(invocation, planner):
call_recv = context.call_async(
ansible_mitogen.target.run_module_async,
job_id=job_id,
timeout_secs=invocation.timeout_secs,
timeout_secs=ansible_mitogen.utils.unsafe.cast(invocation.timeout_secs),
started_sender=started_recv.to_sender(),
kwargs=planner.get_kwargs(),
)

@ -73,6 +73,7 @@ except ImportError:
from io import StringIO
# Prevent accidental import of an Ansible module from hanging on stdin read.
# FIXME Should probably be b'{}' or None. Ansible 2.19 has bytes | None = None.
import ansible.module_utils.basic
ansible.module_utils.basic._ANSIBLE_ARGS = '{}'
@ -635,6 +636,7 @@ class NewStyleStdio(object):
sys.stderr = StringIO()
encoded = json.dumps({'ANSIBLE_MODULE_ARGS': args})
ansible.module_utils.basic._ANSIBLE_ARGS = utf8(encoded)
ansible.module_utils.basic._ANSIBLE_PROFILE = 'legacy'
sys.stdin = StringIO(mitogen.core.to_text(encoded))
self.original_get_path = getattr(ansible.module_utils.basic,
@ -649,7 +651,9 @@ class NewStyleStdio(object):
sys.stdout = self.original_stdout
sys.stderr = self.original_stderr
sys.stdin = self.original_stdin
# FIXME Should probably be b'{}' or None. Ansible 2.19 has bytes | None = None.
ansible.module_utils.basic._ANSIBLE_ARGS = '{}'
ansible.module_utils.basic._ANSIBLE_PROFILE = None
class ProgramRunner(Runner):

@ -57,6 +57,7 @@ import mitogen.service
import ansible_mitogen.loaders
import ansible_mitogen.module_finder
import ansible_mitogen.target
import ansible_mitogen.utils
import ansible_mitogen.utils.unsafe
@ -338,7 +339,12 @@ class ContextService(mitogen.service.Service):
'ansible_mitogen.target',
'mitogen.fork',
'mitogen.service',
)
) + ((
'ansible.module_utils._internal._json._profiles._module_legacy_c2m',
'ansible.module_utils._internal._json._profiles._module_legacy_m2c',
'ansible.module_utils._internal._json._profiles._module_modern_c2m',
'ansible.module_utils._internal._json._profiles._module_legacy_m2c',
) if ansible_mitogen.utils.ansible_version[:2] >= (2, 19) else ())
def _send_module_forwards(self, context):
if hasattr(self.router.responder, 'forward_modules'):

@ -406,6 +406,12 @@ class Spec(with_metaclass(abc.ABCMeta, object)):
Value of "ansible_doas_exe" variable.
"""
@abc.abstractmethod
def verbosity(self):
"""
How verbose to make logging or diagnostics output.
"""
class PlayContextSpec(Spec):
"""
@ -601,6 +607,17 @@ class PlayContextSpec(Spec):
os.environ.get('ANSIBLE_DOAS_EXE')
)
def verbosity(self):
try:
verbosity = self._connection.get_option('verbosity', hostvars=self._task_vars)
except KeyError:
verbosity = self.mitogen_ssh_debug_level()
if verbosity:
return int(verbosity)
return 0
class MitogenViaSpec(Spec):
"""
@ -836,3 +853,13 @@ class MitogenViaSpec(Spec):
self._host_vars.get('ansible_doas_exe') or
os.environ.get('ANSIBLE_DOAS_EXE')
)
def verbosity(self):
verbosity = self._host_vars.get('ansible_ssh_verbosity')
if verbosity is None:
verbosity = self.mitogen_ssh_debug_level()
if verbosity:
return int(verbosity)
return 0

@ -16,8 +16,11 @@ __all__ = [
def _cast_to_dict(obj): return {cast(k): cast(v) for k, v in obj.items()}
def _cast_to_list(obj): return [cast(v) for v in obj]
def _cast_to_set(obj): return set(cast(v) for v in obj)
def _cast_to_tuple(obj): return tuple(cast(v) for v in obj)
def _cast_unsafe(obj): return obj._strip_unsafe()
def _passthrough(obj): return obj
def _untag(obj): return obj._native_copy()
# A dispatch table to cast objects based on their exact type.
@ -26,25 +29,64 @@ _CAST_DISPATCH = {
bytes: bytes,
dict: _cast_to_dict,
list: _cast_to_list,
tuple: _cast_to_list,
mitogen.core.UnicodeType: mitogen.core.UnicodeType,
}
_CAST_DISPATCH.update({t: _passthrough for t in mitogen.utils.PASSTHROUGH})
if hasattr(ansible.utils.unsafe_proxy.AnsibleUnsafeText, '_strip_unsafe'):
_CAST_SUBTYPES = [
dict,
list,
]
if hasattr(ansible.utils.unsafe_proxy, 'TrustedAsTemplate'):
import datetime
import ansible.module_utils._internal._datatag
_CAST_DISPATCH.update({
set: _cast_to_set,
tuple: _cast_to_tuple,
ansible.module_utils._internal._datatag._AnsibleTaggedBytes: _untag,
ansible.module_utils._internal._datatag._AnsibleTaggedDate: _untag,
ansible.module_utils._internal._datatag._AnsibleTaggedDateTime: _untag,
ansible.module_utils._internal._datatag._AnsibleTaggedDict: _cast_to_dict,
ansible.module_utils._internal._datatag._AnsibleTaggedFloat: _untag,
ansible.module_utils._internal._datatag._AnsibleTaggedInt: _untag,
ansible.module_utils._internal._datatag._AnsibleTaggedList: _cast_to_list,
ansible.module_utils._internal._datatag._AnsibleTaggedSet: _cast_to_set,
ansible.module_utils._internal._datatag._AnsibleTaggedStr: _untag,
ansible.module_utils._internal._datatag._AnsibleTaggedTime: _untag,
ansible.module_utils._internal._datatag._AnsibleTaggedTuple: _cast_to_tuple,
ansible.utils.unsafe_proxy.AnsibleUnsafeBytes: bytes,
ansible.utils.unsafe_proxy.AnsibleUnsafeText: mitogen.core.UnicodeType,
datetime.date: _passthrough,
datetime.datetime: _passthrough,
datetime.time: _passthrough,
})
_CAST_SUBTYPES.extend([
set,
tuple,
])
elif hasattr(ansible.utils.unsafe_proxy.AnsibleUnsafeText, '_strip_unsafe'):
_CAST_DISPATCH.update({
tuple: _cast_to_list,
ansible.utils.unsafe_proxy.AnsibleUnsafeBytes: _cast_unsafe,
ansible.utils.unsafe_proxy.AnsibleUnsafeText: _cast_unsafe,
ansible.utils.unsafe_proxy.NativeJinjaUnsafeText: _cast_unsafe,
})
_CAST_SUBTYPES.extend([
tuple,
])
elif ansible_mitogen.utils.ansible_version[:2] <= (2, 16):
_CAST_DISPATCH.update({
tuple: _cast_to_list,
ansible.utils.unsafe_proxy.AnsibleUnsafeBytes: bytes,
ansible.utils.unsafe_proxy.AnsibleUnsafeText: mitogen.core.UnicodeType,
})
_CAST_SUBTYPES.extend([
tuple,
])
else:
mitogen_ver = '.'.join(str(v) for v in mitogen.__version__)
raise ImportError("Mitogen %s can't unwrap Ansible %s AnsibleUnsafe objects"
raise ImportError("Mitogen %s can't cast Ansible %s objects"
% (mitogen_ver, ansible.__version__))
@ -73,7 +115,9 @@ def cast(obj):
return unwrapper(obj)
# Slow path: obj is some unknown subclass
if isinstance(obj, dict): return _cast_to_dict(obj)
if isinstance(obj, (list, tuple)): return _cast_to_list(obj)
for typ_ in _CAST_SUBTYPES:
if isinstance(obj, typ_):
unwrapper = _CAST_DISPATCH[typ_]
return unwrapper(obj)
return mitogen.utils.cast(obj)

@ -141,7 +141,9 @@ Noteworthy Differences
+-----------------+ 3.10 - 3.13 |
| 10 | |
+-----------------+-----------------+
| 11 | 3.11 - 3.13 |
| 11 | |
+-----------------+ 3.11 - 3.13+ |
| 12 | |
+-----------------+-----------------+
Verify your installation is running one of these versions by checking
@ -1016,6 +1018,8 @@ Like the :ans:conn:`ssh` except connection delegation is supported.
* ``ansible_ssh_private_key_file``
* ``ansible_ssh_pass``, ``ansible_ssh_password``, ``ansible_password``
(default: assume passwordless)
* ``ansible_ssh_host_key_checking``, ``ansible_host_key_checking`` (default:
:data:`True`)
* ``ssh_args``, ``ssh_common_args``, ``ssh_extra_args``
* ``mitogen_mask_remote_name``: if :data:`True`, mask the identity of the
Ansible controller process on remote machines. To simplify diagnostics,

@ -18,6 +18,59 @@ To avail of fixes in an unreleased version, please download a ZIP file
`directly from GitHub <https://github.com/mitogen-hq/mitogen/>`_.
v0.3.25 (2025-07-29)
--------------------
Ansible 12 has deprecated third-party strategy plugins. This is currently
how Mitogen integrates with Ansible (e.g. `ANSIBLE_STRATEGY=mitogen_linear`).
Running Ansible 12 + Mitogen will currently print a deprecation warning
[DEPRECATION WARNING]: Use of strategy plugins not included in
ansible.builtin are deprecated [...]. This feature will be removed from
ansible-core in a future release.
Ansible + Mitogen will still work for now. Mitogen is considering alternatives
to strategy plugins under :gh:issue:`1278`.
* :gh:issue:`1258` Ansible 12 (ansible-core 2.19) support
v0.3.25b1 (2025-07-21)
----------------------
* :gh:issue:`1303` CI: Switch to archived Debian 10 (buster) apt repository
v0.3.25a3 (2025-07-02)
----------------------
* :gh:issue:`1285` CI: use `result_format = yaml` for Ansible test output,
instead of deprecated `stdout_callback = yaml`
* :gh:issue:`1293` CI: Fix ``ansible_version`` comparisons when an Ansible
release candidate is under test
* :gh:issue:`1275` CI: Test ``ansible_ssh_password`` behaviour without
``sshpass`` installed
* :gh:issue:`1282` :mod:`ansible_mitogen`: Support ``ANSIBLE_SSH_VERBOSITY``
with Ansible 12
v0.3.25a2 (2025-06-21)
----------------------
* :gh:issue:`1274` :mod:`ansible_mitogen`: Replace use of `jsonify()`, which
is deprecated form Ansible 12 (ansible-core 2.19)
v0.3.25a1 (2025-06-05)
----------------------
* :gh:issue:`1258` Initial Ansible 12 (ansible-core 2.19) support
* :gh:issue:`1258` :mod:`ansible_mitogen`: Initial Ansible datatag support
(:gh:anspull:`84621`)
* :gh:issue:`1258` :mod:`ansible_mitogen`: Ansible 12 (ansible-core 2.19) test
jobs
v0.3.24 (2025-05-29)
--------------------

@ -5,8 +5,11 @@ sys.path.append('.')
def changelog_version(path, encoding='utf-8'):
"Return the 1st *stable* (not pre, dev) version in the changelog"
# See also grep_version() in setup.py
# e.g. "0.1.2, (1999-12-31)\n"
version_pattern = re.compile(
r'^v(?P<version>[0-9]+\.[0-9]+\.[0-9]+)',
r'^v(?P<version>\d+\.\d+\.\d+) \((?P<date>\d\d\d\d-\d\d-\d\d)\)$',
re.MULTILINE,
)
@ -64,6 +67,15 @@ domainrefs = {
'text': '#%s',
'url': 'https://github.com/mitogen-hq/mitogen/pull/%s',
},
'gh:ansissue': {
'text': 'Ansible #%s',
'url': 'https://github.com/ansible/ansible/issues/%s',
},
'gh:anspull': {
'text': 'Ansible #%s',
'url': 'https://github.com/ansible/ansible/pull/%s',
},
'ans:mod': {
'text': '%s module',
'url': 'https://docs.ansible.com/ansible/latest/modules/%s_module.html',

@ -35,7 +35,7 @@ be expected. On the slave, it is built dynamically during startup.
#: Library version as a tuple.
__version__ = (0, 3, 24)
__version__ = (0, 3, 25)
#: This is :data:`False` in slave contexts. Previously it was used to prevent

@ -1,5 +1,5 @@
[bdist_wheel]
universal=1
python_tag = py2.py3
[coverage:run]
branch = true

@ -33,15 +33,34 @@ from setuptools import find_packages, setup
def grep_version():
# See also changlelog_version() in docs.conf.py
path = os.path.join(os.path.dirname(__file__), 'mitogen/__init__.py')
# Based on https://packaging.python.org/en/latest/specifications/version-specifiers/#appendix-parsing-version-strings-with-regular-expressions
# e.g. "__version__ = (0, 1, 2)", "__version__ = (0, 1, 3, 'dev')",
# "__version__ = (0, 1, 4, 'a', 1)"
version_pattern = re.compile(
r"__version__ = \((\d+), (\d+), (\d+)(?:, '(dev)')?\)",
r'''
^__version__\s=\s\(
(?P<major>\d+)
,\s
(?P<minor>\d+)
,\s
(?P<patch>\d+)
(?:
(?:,\s '(?P<dev_l>dev)')
| (?:,\s '(?P<pre_l>a|b)' ,\s (?P<pre_n>\d+))
)?
\)
$
''',
re.MULTILINE | re.VERBOSE,
)
with open(path) as fp:
match = version_pattern.search(fp.read())
if match is None:
raise ValueError('Could not find __version__ string in %s', path)
# E.g. '0.1.2', '0.1.3dev'
# e.g. '0.1.2', '0.1.3dev', '0.1.4a1'
return '.'.join(str(part) for part in match.groups() if part)

@ -1,5 +1,7 @@
[defaults]
any_errors_fatal = true
# Ansible >= 6 (ansible-core >= 2.13)
callback_result_format = yaml
# callbacks_enabled was added in Ansible 4 (ansible-core 2.11).
# profile_tasks: Displays timing for each task and summary table of top N tasks
# timer: Displays "Playbook run took 0 days, 0 hours, ..."
@ -17,13 +19,13 @@ strategy_plugins = ../../ansible_mitogen/plugins/strategy
inventory_plugins = lib/inventory
action_plugins = lib/action
callback_plugins = lib/callback
stdout_callback = yaml
vars_plugins = lib/vars
library = lib/modules
filter_plugins = lib/filters
module_utils = lib/module_utils
retry_files_enabled = False
show_task_path_on_failure = true # Added in ansible-core 2.11
# Ansible >= 4 (ansible-core >= 2.11)
show_task_path_on_failure = true
display_args_to_stdout = True
forks = 100

@ -1,4 +1,11 @@
---
# Avoid `ansible_version.full is version(..., strict=True)` limitations.
# Pre-release info (alpha/beta/rc) is intentionally ignored.
# Behaviour that is present or expected in ansible-core 2.50.x should be
# tested even if ansible-core 2.50.0a1 or 2.50.0rc1 is under test.
ansible_version_major_minor: "{{ ansible_version.major }}.{{ ansible_version.minor }}"
ansible_version_major_minor_patch: "{{ ansible_version.major }}.{{ ansible_version.minor }}.{{ ansible_version.revision | regex_search('^[0-9]+') }}"
become_unpriv_available: >-
{#
Vanilla Ansible >= 4 (ansible-core >= 2.11) can use `setfacl` for
@ -13,7 +20,7 @@ become_unpriv_available: >-
(
not is_mitogen
and is_macos_controller
and ansible_version.full is version("2.11", ">=", strict=True)
and ansible_version_major_minor is version("2.11", ">=", strict=True)
)
or (
is_mitogen
@ -22,7 +29,7 @@ become_unpriv_available: >-
or (
is_mitogen
and ansible_python_interpreter is not defined
and ansible_version.full is version("2.18", "<", strict=True)
and ansible_version_major_minor is version("2.18", "<", strict=True)
)
-}}

@ -0,0 +1,4 @@
package_manager_repos:
- dest: /etc/apt/sources.list
content: |
deb http://archive.debian.org/debian buster main contrib non-free

@ -1,5 +1,7 @@
package_manager_keys:
- src: debian-archive-bullseye-automatic.gpg # Debian 11
dest: /etc/apt/trusted.gpg.d/debian-archive-bullseye-automatic.gpg
- src: debian-archive-bullseye-security-automatic.asc # Debian 11
dest: /etc/apt/trusted.gpg.d/debian-archive-bullseye-security-automatic.asc
- src: debian-archive-bookworm-automatic.gpg # Debian 12
dest: /etc/apt/trusted.gpg.d/debian-archive-bookworm-automatic.gpg

@ -11,8 +11,10 @@
- set_fact:
expected_ssh_port: null
when: ansible_version.full is version('2.11.1', '>=', strict=True)
when:
- ansible_version_major_minor_patch is version('2.11.1', '>=', strict=True)
- set_fact:
expected_ssh_port: 22
when: ansible_version.full is version('2.11.1', '<', strict=True)
when:
- ansible_version_major_minor_patch is version('2.11.1', '<', strict=True)

@ -1,7 +1,14 @@
- name: integration/action/transfer_data.yml
- name: integration/action/transfer_data.yml, json
hosts: test-targets
tasks:
- meta: end_play
when:
# Ansible >= 12 (ansible-core >= 2.19) only allows bytes|str through
# `ansible.plugins.action.ActionBase._transfer_data()`.
- ansible_version_major_minor is version('2.19', '>=', strict=True)
- not is_mitogen
- name: Cleanup transfer data
file:
path: /tmp/transfer-data
@ -15,26 +22,41 @@
data: {
"I am JSON": true
}
- name: Slurp JSON transfer data
slurp:
src: /tmp/transfer-data
register: out
- assert:
that: |
out.content|b64decode == '{"I am JSON": true}'
fail_msg: |
out={{ out }}
- name: Cleanup transfer data
file:
path: /tmp/transfer-data
state: absent
tags:
- transfer_data
- name: integration/action/transfer_data.yml, text
hosts: test-targets
tasks:
- name: Create text transfer data
action_passthrough:
method: _transfer_data
kwargs:
remote_path: /tmp/transfer-data
data: "I am text."
- name: Slurp text transfer data
slurp:
src: /tmp/transfer-data
register: out
- assert:
that:
out.content|b64decode == 'I am text.'

@ -35,8 +35,8 @@
# | Ansible <= 3 | ansible-core <= 2.10 | present | True |
# | Ansible 4 - 6 | ansible-core 2.11 - 2.13 | deprecated | False |
# | Ansible >= 7 | ansible-core >= 2.14 | absent | n/a |
- (ansible_version.full is version("2.14", ">=", strict=True) and async_out.invocation.module_args.warn is not defined)
or (ansible_version.full is version("2.11", ">=", strict=True) and async_out.invocation.module_args.warn == False)
- (ansible_version_major_minor is version("2.14", ">=", strict=True) and async_out.invocation.module_args.warn is not defined)
or (ansible_version_major_minor is version("2.11", ">=", strict=True) and async_out.invocation.module_args.warn == False)
or (async_out.invocation.module_args.warn == True)
- async_out.rc == 0
- async_out.start.startswith("20")
@ -53,7 +53,7 @@
fail_msg: |
async_out={{ async_out }}
when:
- ansible_version.full is version('2.4', '>=', strict=True)
- ansible_version_major_minor is version('2.4', '>=', strict=True)
vars:
async_out: "{{result.content|b64decode|from_json}}"
tags:

@ -41,9 +41,9 @@
- result1.changed == True
# ansible/b72e989e1837ccad8dcdc926c43ccbc4d8cdfe44
- |
(ansible_version.full is version('2.8', ">=", strict=True) and
(ansible_version_major_minor is version('2.8', ">=", strict=True) and
result1.cmd == "echo alldone;\nsleep 1;\n") or
(ansible_version.full is version('2.8', '<', strict=True) and
(ansible_version_major_minor is version('2.8', '<', strict=True) and
result1.cmd == "echo alldone;\n sleep 1;")
- result1.delta|length == 14
- result1.start|length == 26
@ -61,7 +61,7 @@
fail_msg: |
result1={{ result1 }}
when:
- ansible_version.full is version('2.8', '>', strict=True) # ansible#51393
- ansible_version_major_minor is version('2.8', '>', strict=True) # ansible#51393
- assert:
that:
@ -69,6 +69,6 @@
fail_msg: |
result1={{ result1 }}
when:
- ansible_version.full is version('2.4', '>', strict=True)
- ansible_version_major_minor is version('2.4', '>', strict=True)
tags:
- runner_one_job

@ -66,6 +66,6 @@
fail_msg: |
result2={{ result2 }}
when:
- ansible_version.full is version('2.8', '>=', strict=True) # ansible#51393
- ansible_version_major_minor is version('2.8', '>=', strict=True) # ansible#51393
tags:
- runner_two_simultaneous_jobs

@ -10,11 +10,11 @@
- debug: msg="reset.yml skipped on Ansible<2.5.6"
when:
- ansible_version.full is version('2.5.6', '<', strict=True)
- ansible_version_major_minor_patch is version('2.5.6', '<', strict=True)
- meta: end_play
when:
- ansible_version.full is version('2.5.6', '<', strict=True)
- ansible_version_major_minor_patch is version('2.5.6', '<', strict=True)
- custom_python_detect_environment:
register: out

@ -8,11 +8,11 @@
tasks:
- debug: msg="reset_become.yml skipped on Ansible<2.5.6"
when:
- ansible_version.full is version('2.5.6', '<', strict=True)
- ansible_version_major_minor_patch is version('2.5.6', '<', strict=True)
- meta: end_play
when:
- ansible_version.full is version('2.5.6', '<', strict=True)
- ansible_version_major_minor_patch is version('2.5.6', '<', strict=True)
- name: save pid of the become acct
custom_python_detect_environment:

@ -19,7 +19,7 @@
- meta: end_play
when:
- ansible_version.full is version('2.4', '<', strict=True)
- ansible_version_major_minor is version('2.4', '<', strict=True)
- mitogen_get_stack:
delegate_to: "{{ physical_host }}"
@ -50,7 +50,7 @@
-o, PubkeyAcceptedKeyTypes=+ssh-rsa,
-o, UserKnownHostsFile=/dev/null,
],
'ssh_debug_level': null,
'ssh_debug_level': 0,
'ssh_path': 'ssh',
'username': 'alias-user',
},
@ -78,7 +78,7 @@
-o, PubkeyAcceptedKeyTypes=+ssh-rsa,
-o, UserKnownHostsFile=/dev/null,
],
'ssh_debug_level': null,
'ssh_debug_level': 0,
'ssh_path': 'ssh',
'username': 'ansible-cfg-remote-user',
},

@ -87,7 +87,7 @@
-o, PubkeyAcceptedKeyTypes=+ssh-rsa,
-o, UserKnownHostsFile=/dev/null,
],
'ssh_debug_level': null,
'ssh_debug_level': 0,
'ssh_path': 'ssh',
'username': 'alias-user',
},
@ -131,7 +131,7 @@
-o, PubkeyAcceptedKeyTypes=+ssh-rsa,
-o, UserKnownHostsFile=/dev/null,
],
'ssh_debug_level': null,
'ssh_debug_level': 0,
'ssh_path': 'ssh',
'username': 'alias-user',
},
@ -186,7 +186,7 @@
-o, PubkeyAcceptedKeyTypes=+ssh-rsa,
-o, UserKnownHostsFile=/dev/null,
],
'ssh_debug_level': null,
'ssh_debug_level': 0,
'ssh_path': 'ssh',
'username': 'ansible-cfg-remote-user',
},
@ -230,7 +230,7 @@
-o, PubkeyAcceptedKeyTypes=+ssh-rsa,
-o, UserKnownHostsFile=/dev/null,
],
'ssh_debug_level': null,
'ssh_debug_level': 0,
'ssh_path': 'ssh',
'username': 'alias-user',
},
@ -258,7 +258,7 @@
-o, PubkeyAcceptedKeyTypes=+ssh-rsa,
-o, UserKnownHostsFile=/dev/null,
],
'ssh_debug_level': null,
'ssh_debug_level': 0,
'ssh_path': 'ssh',
'username': 'ansible-cfg-remote-user',
},
@ -312,7 +312,7 @@
-o, PubkeyAcceptedKeyTypes=+ssh-rsa,
-o, UserKnownHostsFile=/dev/null,
],
'ssh_debug_level': null,
'ssh_debug_level': 0,
'ssh_path': 'ssh',
'username': 'newuser-normal-normal-user',
},
@ -357,7 +357,7 @@
-o, PubkeyAcceptedKeyTypes=+ssh-rsa,
-o, UserKnownHostsFile=/dev/null,
],
'ssh_debug_level': null,
'ssh_debug_level': 0,
'ssh_path': 'ssh',
'username': 'alias-user',
},

@ -8,7 +8,7 @@
- meta: end_play
when:
- ansible_version.full is version('2.5.6', '<', strict=True)
- ansible_version_major_minor_patch is version('2.5.6', '<', strict=True)
# Start with a clean slate.
- mitogen_shutdown_all:

@ -46,9 +46,9 @@
'20': /usr/bin/python3.8
discovered_interpreter_expected: >-
{%- if ansible_version.full is version('2.12', '<', strict=True) -%}
{%- if ansible_version_major_minor is version('2.12', '<', strict=True) -%}
{{ DISCOVERED_INTERPRETER_EXPECTED_MAP__ANSIBLE_lt_2_12[distro][distro_major] }}
{%- elif ansible_version.full is version('2.17', '<', strict=True) -%}
{%- elif ansible_version_major_minor is version('2.17', '<', strict=True) -%}
{{ DISCOVERED_INTERPRETER_EXPECTED_MAP__ANSIBLE_2_12_to_2_16[distro][distro_major] }}
{%- else -%}
{{ DISCOVERED_INTERPRETER_EXPECTED_MAP__ANSIBLE_ge_2_17[distro][distro_major] }}
@ -141,7 +141,7 @@
when:
- legacy.ansible_facts.discovered_interpreter_python == '/usr/bin/python'
- auto_out.ansible_facts.discovered_interpreter_python != '/usr/bin/python'
- ansible_version.full is version_compare('2.12.0', '<', strict=True)
- ansible_version_major_minor is version('2.12', '<', strict=True)
- name: check for warning (only on platforms where auto result is not /usr/bin/python and legacy is) from ansible 2.12 on
assert:
@ -153,7 +153,7 @@
when:
- legacy.ansible_facts.discovered_interpreter_python == '/usr/bin/python'
- auto_out.ansible_facts.discovered_interpreter_python != '/usr/bin/python'
- ansible_version.full is version_compare('2.12.0', '>=', strict=True)
- ansible_version_major_minor is version('2.12', '>=', strict=True)
- name: test that auto_silent never warns and got the same answer as auto
block:
@ -229,6 +229,6 @@
always:
- meta: clear_facts
when:
- ansible_version.full is version_compare('2.8.0', '>=', strict=True)
- ansible_version_major_minor is version('2.8', '>=', strict=True)
tags:
- ansible_2_8_tests

@ -14,7 +14,7 @@
- meta: end_play
when:
- not is_mitogen
- ansible_version.full is version('2.17.1', '>=', strict=True)
- ansible_version_major_minor_patch is version('2.17.1', '>=', strict=True)
- name: create temp file to source
file:

@ -11,6 +11,8 @@
vars:
ansible_python_interpreter: "{{ ansible_playbook_python }}"
shell: >-
ANSIBLE_CALLBACK_RESULT_FORMAT=json
ANSIBLE_LOAD_CALLBACK_PLUGINS=false
ANSIBLE_STRATEGY=mitogen_linear
ANSIBLE_SSH_ARGS="-o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedKeyTypes=+ssh-rsa"
ANSIBLE_VERBOSITY="{{ ansible_verbosity }}"

@ -18,10 +18,17 @@
- not out.changed
- out is failed
# https://github.com/ansible/ansible/commit/62d8c8fde6a76d9c567ded381e9b34dad69afcd6
- out.msg is match(msg_pattern)
- (out.module_stdout == "" and out.module_stderr is search(tb_pattern))
or
(out.module_stdout is search(tb_pattern) and out.module_stderr is match("Shared connection to localhost closed."))
- |
out.msg is match(msg_pattern)
or out.msg in (
"Task failed: Module failed: name 'kaboom' is not defined",
'Module result deserialization failed: No start of json char found',
)
# - out.exception is undefined
# or out.exception | default('') is match(tb_pattern)
# or out.module_stderr is search(tb_pattern)
# - out.module_stdout == ''
# - out.module_stderr is search(tb_pattern)
fail_msg: |
out={{ out }}
tags:

@ -7,7 +7,7 @@
- meta: end_play
when:
- not is_mitogen
- ansible_version.full is version('2.17.1', '>=', strict=True)
- ansible_version_major_minor_patch is version('2.17.1', '>=', strict=True)
- custom_bash_old_style_module:
foo: true

@ -30,6 +30,7 @@
- out.failed
- out.results[0].failed
- out.results[0].msg.startswith('MODULE FAILURE')
or out.results[0].msg.startswith('Module result deserialization failed')
- out.results[0].rc == 0
fail_msg: |
out={{ out }}

@ -15,7 +15,9 @@
that:
- "out.failed"
- "out.results[0].failed"
- "out.results[0].msg.startswith('MODULE FAILURE')"
- |
out.results[0].msg.startswith('MODULE FAILURE')
or out.results[0].msg == 'Module result deserialization failed: No start of json char found'
# On Ubuntu 16.04 /bin/sh is dash 0.5.8. It treats custom_binary_single_null
# as a valid executable. There's no error message, and rc == 0.
- |

@ -20,6 +20,6 @@
fail_msg: |
out={{ out }}
when:
- ansible_version.full is version('2.4', '>=', strict=True)
- ansible_version_major_minor is version('2.4', '>=', strict=True)
tags:
- custom_perl_json_args_module

@ -20,6 +20,6 @@
fail_msg: |
out={{ out }}
when:
- ansible_version.full is version('2.4', '>=', strict=True)
- ansible_version_major_minor is version('2.4', '>=', strict=True)
tags:
- custom_perl_want_json_module

@ -5,6 +5,8 @@
- name: Run missing_module
connection: local
environment:
ANSIBLE_CALLBACK_RESULT_FORMAT: json
ANSIBLE_LOAD_CALLBACK_PLUGINS: "false"
ANSIBLE_STRATEGY: "{{ lookup('env', 'ANSIBLE_STRATEGY') | mandatory }}"
ANSIBLE_VERBOSITY: "{{ ansible_verbosity }}"
vars:
@ -26,6 +28,7 @@
- assert:
that: |
'The module missing_module was not found in configured module paths' in out.stdout
or "Cannot resolve 'missing_module' to an action or module" in out.stdout
fail_msg: |
out={{ out }}
tags:

@ -8,3 +8,4 @@
- import_playbook: templated_by_play_taskvar.yml
- import_playbook: templated_by_task_keyword.yml
- import_playbook: variables.yml
- import_playbook: verbosity.yml

@ -31,6 +31,11 @@
- assert:
that:
- ssh_no_password_result.unreachable == True
- >-
ssh_no_password_result.msg is search('SSH password was requested, but none specified')
or ssh_no_password_result.msg is search('SSH password is incorrect')
or ssh_no_password_result.msg is search('Invalid/incorrect password')
or ssh_no_password_result.msg is search('Permission denied \(publickey,password(,keyboard-interactive)?\)')
fail_msg: |
ssh_no_password_result={{ ssh_no_password_result }}
@ -64,5 +69,9 @@
- assert:
that:
- ssh_wrong_password_result.unreachable == True
- >-
ssh_wrong_password_result.msg is search('SSH password is incorrect')
or ssh_wrong_password_result.msg is search('Invalid/incorrect password')
or ssh_wrong_password_result.msg is search('Permission denied \(publickey,password(,keyboard-interactive)?\)')
fail_msg: |
ssh_wrong_password_result={{ ssh_wrong_password_result }}

@ -32,7 +32,7 @@
when:
# https://github.com/ansible/ansible/issues/84238
- not is_mitogen
- ansible_version.full is version('2.19', '<', strict=True)
- ansible_version_major_minor is version('2.19', '<', strict=True)
- meta: reset_connection
- name: Templated variables in play, key authentication
ping:

@ -13,6 +13,8 @@
- name: Cause Ansible connection timeout
connection: local
environment:
ANSIBLE_CALLBACK_RESULT_FORMAT: json
ANSIBLE_LOAD_CALLBACK_PLUGINS: "false"
ANSIBLE_SSH_TIMEOUT: 10
ANSIBLE_STRATEGY: "{{ lookup('env', 'ANSIBLE_STRATEGY') | mandatory }}"
ANSIBLE_VERBOSITY: "{{ ansible_verbosity }}"
@ -43,6 +45,7 @@
'"unreachable": true' in out.stdout
- |
'"msg": "Connection timed out."' in out.stdout
or '"msg": "Task failed: Connection timed out."' in out.stdout
fail_msg: |
out={{ out }}
tags:

@ -19,6 +19,8 @@
- name: ansible_user, ansible_ssh_private_key_file
shell: >
ANSIBLE_ANY_ERRORS_FATAL=false
ANSIBLE_CALLBACK_RESULT_FORMAT=json
ANSIBLE_LOAD_CALLBACK_PLUGINS=false
ANSIBLE_STRATEGY=mitogen_linear
ANSIBLE_SSH_ARGS="-o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedKeyTypes=+ssh-rsa"
ANSIBLE_VERBOSITY="{{ ansible_verbosity }}"
@ -37,6 +39,8 @@
- name: ansible_user, wrong ansible_ssh_private_key_file
shell: >
ANSIBLE_ANY_ERRORS_FATAL=false
ANSIBLE_CALLBACK_RESULT_FORMAT=json
ANSIBLE_LOAD_CALLBACK_PLUGINS=false
ANSIBLE_STRATEGY=mitogen_linear
ANSIBLE_SSH_ARGS="-o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedKeyTypes=+ssh-rsa"
ANSIBLE_VERBOSITY="{{ ansible_verbosity }}"

@ -0,0 +1,41 @@
# Ansible >= 12 (ansible-core >= 2.19) adds SSH connection `verbosity` property.
# Ansible <= 11 (ansible-core <= 2.18) applies ANSIBLE_VERBOSITY -> ssh.
- name: integration/ssh/verbosity.yml
hosts: test-targets
gather_facts: false
tasks:
- meta: end_play
when:
- ansible_version_major_minor is version('2.19', '<', strict=True)
- name: Exercise ssh verbosity
connection: local
environment:
ANSIBLE_CALLBACK_RESULT_FORMAT: json
ANSIBLE_LOAD_CALLBACK_PLUGINS: "false"
ANSIBLE_SSH_VERBOSITY: 3
ANSIBLE_STRATEGY: "{{ lookup('env', 'ANSIBLE_STRATEGY') | mandatory }}"
ANSIBLE_VERBOSITY: 3
vars:
ansible_python_interpreter: "{{ ansible_playbook_python }}"
command:
cmd:
ansible
{% for inv in ansible_inventory_sources %}
-i "{{ inv }}"
{% endfor %}
"{{ inventory_hostname }}"
-m ping
chdir: ../..
register: out
changed_when: false
check_mode: false
- name: Verify ssh -vvv output is included
assert:
that:
- >-
out.stdout is search('debug1: Reading configuration data')
fail_msg: |
out={{ out }}

@ -9,7 +9,7 @@
- command: sudo -n whoami
args:
warn: "{{ False if ansible_version.full is version('2.10', '<=', strict=True) else omit }}"
warn: "{{ False if ansible_version_major_minor is version('2.10', '<=', strict=True) else omit }}"
ignore_errors: true
register: sudo_available

@ -7,7 +7,7 @@
- meta: end_play
when:
- ansible_version.full is version('2.5', '<', strict=True)
- ansible_version_major_minor is version('2.5', '<', strict=True)
- custom_python_detect_environment:
vars:

@ -15,6 +15,8 @@
- name: Run stub-lxc-info.py
environment:
ANSIBLE_CALLBACK_RESULT_FORMAT: json
ANSIBLE_LOAD_CALLBACK_PLUGINS: "false"
ANSIBLE_STRATEGY: "{{ lookup('env', 'ANSIBLE_STRATEGY') | mandatory }}"
ANSIBLE_VERBOSITY: "{{ ansible_verbosity }}"
command: |
@ -30,7 +32,7 @@
localhost
args:
chdir: ../..
warn: "{{ False if ansible_version.full is version('2.10', '<=', strict=True) else omit }}"
warn: "{{ False if ansible_version_major_minor is version('2.10', '<=', strict=True) else omit }}"
register: result
- assert:

@ -15,6 +15,8 @@
- name: Run ansible stub-lxc.py
environment:
ANSIBLE_CALLBACK_RESULT_FORMAT: json
ANSIBLE_LOAD_CALLBACK_PLUGINS: "false"
ANSIBLE_STRATEGY: "{{ lookup('env', 'ANSIBLE_STRATEGY') | mandatory }}"
ANSIBLE_VERBOSITY: "{{ ansible_verbosity }}"
command: |
@ -30,7 +32,7 @@
localhost
args:
chdir: ../..
warn: "{{ False if ansible_version.full is version('2.10', '<=', strict=True) else omit }}"
warn: "{{ False if ansible_version_major_minor is version('2.10', '<=', strict=True) else omit }}"
register: result
- assert:

@ -8,9 +8,16 @@
tasks:
- include_tasks: ../_mitogen_only.yml
- {mitogen_get_stack: {}, register: out}
- assert_equal:
left: out.result[0].kwargs.password
right: null
when:
- ansible_version_major_minor is version('2.19', '>=', strict=True)
- assert_equal:
left: out.result[0].kwargs.password
right: "" # actually null, but assert_equal limitation
when:
- ansible_version_major_minor is version('2.19', '<', strict=True)
tags:
- mitogen_only
@ -23,9 +30,16 @@
- assert_equal:
left: out.result[0].kwargs.password
right: "ansi-ssh-pass"
- assert_equal:
left: out.result[1].kwargs.password
right: null
when:
- ansible_version_major_minor is version('2.19', '>=', strict=True)
- assert_equal:
left: out.result[1].kwargs.password
right: ""
when:
- ansible_version_major_minor is version('2.19', '<', strict=True)
tags:
- mitogen_only
@ -48,9 +62,16 @@
tasks:
- include_tasks: ../_mitogen_only.yml
- {mitogen_get_stack: {}, register: out}
- assert_equal:
left: out.result[0].kwargs.password
right: null
when:
- ansible_version_major_minor is version('2.19', '>=', strict=True)
- assert_equal:
left: out.result[0].kwargs.password
right: ""
when:
- ansible_version_major_minor is version('2.19', '<', strict=True)
- assert_equal:
left: out.result[1].kwargs.password
right: "ansi-ssh-pass"
@ -76,9 +97,16 @@
tasks:
- include_tasks: ../_mitogen_only.yml
- {mitogen_get_stack: {}, register: out}
- assert_equal:
left: out.result[0].kwargs.password
right: null
when:
- ansible_version_major_minor is version('2.19', '>=', strict=True)
- assert_equal:
left: out.result[0].kwargs.password
right: ""
when:
- ansible_version_major_minor is version('2.19', '<', strict=True)
- assert_equal:
left: out.result[1].kwargs.password
right: "ansi-pass"
@ -104,9 +132,16 @@
tasks:
- include_tasks: ../_mitogen_only.yml
- {mitogen_get_stack: {}, register: out}
- assert_equal:
left: out.result[0].kwargs.password
right: null
when:
- ansible_version_major_minor is version('2.19', '>=', strict=True)
- assert_equal:
left: out.result[0].kwargs.password
right: ""
when:
- ansible_version_major_minor is version('2.19', '<', strict=True)
- assert_equal:
left: out.result[1].kwargs.password
right: "c.b.a"

@ -12,7 +12,7 @@
- include_tasks: ../_mitogen_only.yml
- meta: end_play
when:
- ansible_version.full is version('2.17', '>=', strict=True)
- ansible_version_major_minor is version('2.17', '>=', strict=True)
- {mitogen_get_stack: {}, register: out}
- assert_equal:
left: out.result[0].kwargs.python_path
@ -27,7 +27,7 @@
- include_tasks: ../_mitogen_only.yml
- meta: end_play
when:
- ansible_version.full is version('2.17', '>=', strict=True)
- ansible_version_major_minor is version('2.17', '>=', strict=True)
- {mitogen_get_stack: {}, register: out}
- assert_equal:
left: out.result[0].kwargs.python_path
@ -57,7 +57,7 @@
- include_tasks: ../_mitogen_only.yml
- meta: end_play
when:
- ansible_version.full is version('2.17', '>=', strict=True)
- ansible_version_major_minor is version('2.17', '>=', strict=True)
- {mitogen_get_stack: {}, register: out}
- assert_equal:
left: out.result[0].kwargs.python_path
@ -87,7 +87,7 @@
- include_tasks: ../_mitogen_only.yml
- meta: end_play
when:
- ansible_version.full is version('2.17', '>=', strict=True)
- ansible_version_major_minor is version('2.17', '>=', strict=True)
- {mitogen_get_stack: {}, register: out}
- assert_equal:
left: out.result[0].kwargs.python_path

@ -31,7 +31,7 @@
# FIXME https://github.com/mitogen-hq/mitogen/issues/1096
- meta: end_play
when:
- ansible_version.full is version('2.17', '>=', strict=True)
- ansible_version_major_minor is version('2.17', '>=', strict=True)
- meta: reset_connection
# The host key might be in ~/.ssh/known_hosts. If it's removed then no
@ -65,7 +65,7 @@
# ansible_host_key_checking don't work on Vanilla Ansible 2.10, even for
# static inventory hosts (ansible/ansible#49254, ansible/ansible#73708).
when:
- ansible_version.full is version('2.11', '>=', strict=True)
- ansible_version_major_minor is version('2.11', '>=', strict=True)
or is_mitogen
tags:
- issue_1066

@ -7,7 +7,7 @@
tasks:
- meta: end_play
when:
- ansible_version.full is version('2.6', '<', strict=True)
- ansible_version_major_minor is version('2.6', '<', strict=True)
- name: Copy the naughty ansible into place
copy:
@ -24,7 +24,8 @@
assert:
that:
- env.cwd == ansible_user_dir
- (not env.mitogen_loaded) or (env.python_path.count("") == 1)
- not env.mitogen_loaded
or (env.python_path | select('eq', '') | length == 1)
fail_msg: |
ansible_user_dir={{ ansible_user_dir }}
env={{ env }}

@ -3,7 +3,7 @@
tasks:
- meta: end_play
when:
- ansible_version.full is version('2.8', '<', strict=True)
- ansible_version_major_minor is version('2.8', '<', strict=True)
- custom_python_uses_distro:
register: out

@ -22,7 +22,7 @@
when:
# Ansible 10 (ansible-core 2.17+) require Python 3.7+ on targets.
# On CentOS 8 /usr/libexec/platform-python is Python 3.6
- ansible_version.full is version('2.17', '>=', strict=True)
- ansible_version_major_minor is version('2.17', '>=', strict=True)
- name: set up test container and run tests inside it
block:

@ -25,8 +25,15 @@
# with Ansible 4 (ansible-core 2.11) & associated collections.
# ansible.module_utils.connection.ConnectionError: Method not found
# https://github.com/mitogen-hq/mitogen/actions/runs/12854359099/job/35838635886
- ansible_version.full is version('2.11', '>=', strict=True)
- ansible_version.full is version('2.12', '<', strict=True)
- ansible_version_major_minor is version('2.11', '>=', strict=True)
- ansible_version_major_minor is version('2.12', '<', strict=True)
- meta: end_play
when:
# TASK [Get running configuration and state data ]
# Error: : Task failed: ActionBase._parse_returned_data() missing 1 required positional argument: 'profile'
# https://github.com/ansible-collections/ansible.netcommon/issues/698#issuecomment-2910082548
- ansible_version_major_minor is version('2.19', '>=', strict=True)
- block:
- name: Start container

@ -17,7 +17,7 @@
# support Python 2.x on targets.
- meta: end_play
when:
- ansible_version.full is version('2.17', '>=', strict=True)
- ansible_version_major_minor is version('2.17', '>=', strict=True)
roles:
- role: package_manager

@ -4,6 +4,7 @@ from ansible.utils.unsafe_proxy import AnsibleUnsafeBytes
from ansible.utils.unsafe_proxy import AnsibleUnsafeText
from ansible.utils.unsafe_proxy import wrap_var
import ansible_mitogen.utils
import ansible_mitogen.utils.unsafe
import mitogen.core
@ -17,7 +18,7 @@ class Text(mitogen.core.UnicodeType): pass
class Tuple(tuple): pass
class CastTest(unittest.TestCase):
class CastMixin(unittest.TestCase):
def assertIsType(self, obj, cls, msg=None):
self.assertIs(type(obj), cls, msg)
@ -29,6 +30,8 @@ class CastTest(unittest.TestCase):
self.assertEqual(cast(obj), expected)
self.assertIsType(cast(obj), type(expected))
class CastKnownTest(CastMixin):
def test_ansible_unsafe(self):
self.assertCasts(AnsibleUnsafeBytes(b'abc'), b'abc')
self.assertCasts(AnsibleUnsafeText(u'abc'), u'abc')
@ -47,14 +50,12 @@ class CastTest(unittest.TestCase):
self.assertCasts(wrap_var({}), {})
self.assertCasts(wrap_var([]), [])
self.assertCasts(wrap_var(u''), u'')
self.assertCasts(wrap_var(()), [])
def test_subtypes_roundtrip(self):
self.assertCasts(wrap_var(Bytes()), b'')
self.assertCasts(wrap_var(Dict()), {})
self.assertCasts(wrap_var(List()), [])
self.assertCasts(wrap_var(Text()), u'')
self.assertCasts(wrap_var(Tuple()), [])
def test_subtype_nested_dict(self):
obj = Dict(foo=Dict(bar=u'abc'))
@ -75,18 +76,59 @@ class CastTest(unittest.TestCase):
self.assertIsType(unwrapped[0], list)
self.assertIsType(unwrapped[0][0], mitogen.core.UnicodeType)
def test_subtype_roundtrip_tuple(self):
# wrap_var() preserves sequence types, cast() does not (for now)
@unittest.skipIf(
ansible_mitogen.utils.ansible_version[:2] <= (2, 18),
'Ansible <= 11 (ansible-core >= 2.18) does not send/receive sets',
)
class CastSetTest(CastMixin):
def test_set(self):
self.assertCasts(wrap_var(set()), set())
def test_set_subclass(self):
self.assertCasts(wrap_var(Set()), set())
class CastTupleTest(CastMixin):
def test_tuple(self):
if ansible_mitogen.utils.ansible_version[:2] >= (2, 19):
expected = ()
else:
expected = []
self.assertCasts(wrap_var(Tuple()), expected)
def test_tuple_subclass(self):
if ansible_mitogen.utils.ansible_version[:2] >= (2, 19):
expected = ()
else:
expected = []
self.assertCasts(wrap_var(()), expected)
def test_tuple_subclass_with_contents(self):
if ansible_mitogen.utils.ansible_version[:2] >= (2, 19):
expected = ((u'abc',),)
else:
expected = [[u'abc']]
obj = Tuple([Tuple([u'abc'])])
wrapped = wrap_var(obj)
unwrapped = ansible_mitogen.utils.unsafe.cast(wrapped)
self.assertEqual(unwrapped, [[u'abc']])
self.assertIsType(unwrapped, list)
self.assertIsType(unwrapped[0], list)
self.assertEqual(unwrapped, expected)
self.assertIsType(unwrapped, type(expected))
self.assertIsType(unwrapped[0], type(expected[0]))
self.assertIsType(unwrapped[0][0], mitogen.core.UnicodeType)
def test_unknown_types_raise(self):
class CastUknownTypeTest(unittest.TestCase):
@unittest.skipIf(
ansible_mitogen.utils.ansible_version[:2] >= (2, 19),
'Ansible >= 12 (ansible-core >= 2.19) uses/preserves sets',
)
def test_set_raises(self):
cast = ansible_mitogen.utils.unsafe.cast
self.assertRaises(TypeError, cast, set())
self.assertRaises(TypeError, cast, Set())
def test_complex_raises(self):
cast = ansible_mitogen.utils.unsafe.cast
self.assertRaises(TypeError, cast, 4j)

@ -66,7 +66,7 @@
dnf: dnf clean all
command: "{{ clean_command[ansible_pkg_mgr] }}"
args:
warn: "{{ False if ansible_version.full is version('2.10', '<=', strict=True) else omit }}"
warn: "{{ False if ansible_version_major_minor is version('2.10', '<=', strict=True) else omit }}"
- name: Clean up apt package lists
shell: rm -rf {{item}}/*

@ -1,3 +1,5 @@
ansible_version_major_minor: "{{ ansible_version.major }}.{{ ansible_version.minor }}"
common_packages:
- openssh-server
- rsync

@ -0,0 +1,186 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
TT9yj35WJXkuIZ7GV189g3gogTpu
=Xxba
-----END PGP PUBLIC KEY BLOCK-----

@ -47,16 +47,17 @@
# ansible == 9.x ansible-core ~= 2.16.0
# ansible == 10.x ansible-core ~= 2.17.0
# ansible == 11.x ansible-core ~= 2.18.0
# ansible == 12.x ansible-core ~= 2.19.0
# See also
# - https://docs.ansible.com/ansible/latest/reference_appendices/release_and_maintenance.html#ansible-core-support-matrix
# - https://docs.ansible.com/ansible/devel/reference_appendices/release_and_maintenance.html#ansible-core-support-matrix
[tox]
envlist =
init,
py{27,36}-mode_ansible-ansible{2.10,3,4},
py{311}-mode_ansible-ansible{2.10,3,4,5},
py{313}-mode_ansible-ansible{6,7,8,9,10,11},
py{313}-mode_ansible-ansible{6,7,8,9,10,11,12},
py{27,36,313}-mode_mitogen,
report,
@ -80,12 +81,14 @@ deps =
ansible3: ansible~=3.0
ansible4: ansible~=4.0
ansible5: ansible~=5.0
# From Ansible 6 PyPI distributions include a wheel
ansible6: ansible~=6.0
ansible7: ansible~=7.0
ansible8: ansible~=8.0
ansible9: ansible~=9.0
ansible10: ansible~=10.0
ansible11: ansible>=11.0
ansible11: ansible~=11.0
ansible12: ansible>=12.0.0b2
install_command =
python -m pip --no-python-version-warning --disable-pip-version-check install {opts} {packages}
commands_pre =
@ -106,6 +109,9 @@ setenv =
NOCOVERAGE_ERASE = 1
NOCOVERAGE_REPORT = 1
PIP_CONSTRAINT={toxinidir}/tests/constraints.txt
# Superceded in Ansible >= 6 (ansible-core >= 2.13) by result_format=yaml
# Deprecated in Ansible 12 (ansible-core 2.19)
ansible{2.10,3-5}: DEFAULT_STDOUT_CALLBACK=yaml
# Print warning on the first occurence at each module:linenno in Mitogen. Available Python 2.7, 3.2+.
PYTHONWARNINGS=default:::ansible_mitogen,default:::mitogen
# Ansible 6 - 8 (ansible-core 2.13 - 2.15) require Python 2.7 or >= 3.5 on targets
@ -118,6 +124,7 @@ setenv =
ansible10: MITOGEN_TEST_DISTRO_SPECS=debian10-py3 debian11-py3 ubuntu2004-py3
# Ansible 11 (ansible-core 2.18) requires Python >= 3.8 on targets
ansible11: MITOGEN_TEST_DISTRO_SPECS=debian11-py3 ubuntu2004-py3
ansible12: MITOGEN_TEST_DISTRO_SPECS=debian11-py3 ubuntu2004-py3
distros_centos: MITOGEN_TEST_DISTRO_SPECS=centos6 centos7 centos8
distros_centos5: MITOGEN_TEST_DISTRO_SPECS=centos5
distros_centos6: MITOGEN_TEST_DISTRO_SPECS=centos6

Loading…
Cancel
Save