Pickle handles encoding of unicode itself, there's no need to manually do it
and when using Pickle protocol 2 byte strings are inefficient going between
Python 2.x and 3.x.
The new classes are modelled closely on their existing Module* counterparts.
For now I've duplicated the code, once it's bedded in I may refactor it. I
didn't replicate the FORWARD_MODULE plumbing, it didn't seem to be necessary
and may be dead code.
The current implementation can cause an infinite loop, leading to a process that
hangs and consumes 100% CPU. This occurs because the EOF condition is not
handled properly, resulting in repeated select(...) and read(...) calls.
The fix is to properly handle the EOF condition and break out of the loop when
it occurs.
-SSH command size: 822
+SSH command size: 838
Preamble (mitogen.core + econtext) size: 18226 (17.80KiB)
-mitogen.parent 99062 96.7KiB 51235 50.0KiB 51.7% 12936 12.6KiB 13.1%
+mitogen.parent 99240 96.9KiB 51244 50.0KiB 51.6% 12956 12.7KiB 13.1%
Fixes: https://github.com/mitogen-hq/mitogen/issues/1348
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Use testlib.subprocess instead of subprocess and make the test description a
docstring that can be used by the test runner.
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
When I run
$ MITOGEN_LOG_LEVEL=debug SKIP_ANSIBLE=1 ./run_tests -v -k first_stage_test.CommandLineTest
in a interactive Shell (with a tty), it ends in a hanging process as the
`have_python2` and `have_python3` ends up ends up in an interactive Python
shell. Therefore check the Python version instead.
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Previous phrasing was misleading - it implied a given module was explicitly on
the blacklist, even if it was due to a restrictive whitelist and the blacklist
was empty.
Arguably the blacklist/whitelist semantics are themselves misleading. A
redesign is tempting.
This shouldn't change the interpreter ultimately chosen by Ansible. It should
only improve the hit rate of performing interpreter discovery, particular in
cases where only pythonX.Y is present on the target.
Interpreter discovery may take longer or shorter, depending on the Ansible
version and the interpreters present on the target.
On my laptop his reduces the time to execute `ansible -mping ...` by approx
300 ms with `strategy=mitogen_linear`.
Until this commit Mitogen was unnecessarily sending large chunks of Ansible
from the controller to targets, due to `__main__` being identified as a
related module of `ansible.module_utils.basic`, and resolving to something
within `ansible.cli...`.
On Ansible target hosts executing any Ansible Module `__main__` is imported by
`ansible.module_utils.basic` as part of Ansible's module delivery mechanism.
When `mitogen.master.ModuleResponder` (on the controller) processes the
request for `ansible.module_utils.basic` from the target, it scans
`ansible.module_utils.basic` for related imports and finds `__main__`. However
`__main__` on the controller is not the same module as `__main__` on the
target. On the controller it is a module in `ansible.cli...` that implements
one of the ansible commands (e.g. `ansible`, `ansible-playbook`).
Only the Mitogen unit tests will run against CentOS 5, providing atleast some
Python 2.4test coverage. There is no version of Ansible that supports Python
2.4 that is also supported by Mitogen 0.3.
The SSH key exchange argument is to persuade newer SSH clients to talk with
such an old SSH server.
See https://www.openssh.org/legacy.html
Previously this test used a manually compiled list of results, which is
fragile and an ongoing maintenance burden. New method should 'just work' and
be more transparent.
This technique might be more widely applicable in the test suite.
centos8-test:2025.02 no longer has a /usr/bin/python installed, so use
centos8-py3 target which sets `ansible_python_interpreter=/usr/bin/python3` in
the templated inventory.
Ansible <= 9 (ansible-core <= 2.6) now discover the interpreter as
/usr/bin/python3 on debian11-test:2025.02, as opposed to
/usr/bin/python3.9 on debian11-test:2021. I'm don't know the exact
cause. From manual tests the change in observed behaviour appears to be common to
vanilla Ansible (strategy=linear) and Mitogen flavour
(strategy=mitogen_linear).
```console
(ans9) ➜ mitogen git:(4efb7158) ✗ ANSIBLE_STRATEGY=mitogen_linear ANSIBLE_STRATEGY_PLUGINS=ansible_mitogen/plugins/strategy ans9/bin/ansible -e ansible_python_interpreter=auto -mping d11.lan
d11.lan | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
(ans9) ➜ mitogen git:(4efb7158) ✗ ans9/bin/ansible -e ansible_python_interpreter=auto -mping d11.lan
d11.lan | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
```
Update some tests which assume `/usr/bin/python` exists or that `env python`
will resolve successfully.
It's not consistant across Ansible versions, particular the oldest ones. This
may have contributed to older test images containing usernames from the host
OS that they were built on (e.g. dmw, alex).
It was necessary to split setup.yml because there is no common subset of
supported include/import keywords across Ansible 2.3 - 2.11. The yaml stdout
callback is unavailabe in Ansible 2.3.
This eliminates use of third-party *-vault images and performs repository
config during image prep.
The Apache httpd proxy is necessary because https://vault.centos.org now only
accepts TLS 1.x connections, and CentOS 5 can only do upto SSL 3.0. It is
developed to run on Debian 11.
Loading the ansible_mitogen Ansible plugins apparently doesn't follow the same
rules as importing a Python module. So sys.path manipulations in __init__.py
weren't fired when Ansible tried to load the plugins from a /custom/path that
wasn't already on sys.path.
This wasn't picked up by the test because CI always installs Mitogen as a
Python package (in a virtual env).
This reverses 6145508312.
Since Python 3.2 the log record factory can be changed by using
`logging.setLogRecordFactory` [1]. Therefore use `logging.makeLogRecord` as
recommended in the documentation:
"LogRecord instances are created automatically by the Logger every time
something is logged, and can be created manually via makeLogRecord() (for
example, from a pickled event received over the wire)." [2]
This fixes the test case
`log_handler_test.LogRecordFactoryTest.test_logrecordfactory`.
[1] https://docs.python.org/3/library/logging.html#logging.setLogRecordFactory
[2] https://docs.python.org/3/library/logging.html#logrecord-objects
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
The test currently fails with the following error:
$ PYTHONPATH=$(pwd)/tests:$PYTHONPATH python3 -m unittest -v log_handler_test
...
test_logrecordfactory (log_handler_test.LogRecordFactoryTest.test_logrecordfactory) ... --- Logging error ---
Traceback (most recent call last):
File "/usr/lib/python3.12/logging/__init__.py", line 464, in format
return self._format(record)
^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/logging/__init__.py", line 460, in _format
return self._fmt % values
~~~~~~~~~~^~~~~~~~
KeyError: 'custom_attribute'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3.12/logging/__init__.py", line 1160, in emit
msg = self.format(record)
^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/logging/__init__.py", line 999, in format
return fmt.format(record)
^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/logging/__init__.py", line 999, in format
return fmt.format(record)
^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/logging/__init__.py", line 706, in format
s = self.formatMessage(record)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/logging/__init__.py", line 675, in formatMessage
return self._style.format(record)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/logging/__init__.py", line 466, in format
raise ValueError('Formatting field not found in record: %s' % e)
ValueError: Formatting field not found in record: 'custom_attribute'
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Fixes warning seen during packaging operations
```
➜ mitogen git:(master) ✗ uv build --sdist
Building source distribution...
...
!!
********************************************************************************
Please consider removing the following classifiers in favor of a SPDX license expression:
License :: OSI Approved :: BSD License
See https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#license for details.
********************************************************************************
!!
self._finalize_license_expression()
running egg_info
...
```
Previously the command size could very depanding on the current username, hostname, and process pid.
Before
```
SSH command size: 759
Preamble (mitogen.core + econtext) size: 18227 (17.80KiB)
...
```
After
SSH command size: 755
Preamble (mitogen.core + econtext) size: 18227 (17.80KiB)
...
```
This replaces `mitogen.master.scan_code_imports()` with
`mitogen.imports.codeobj_imports()`. The Python 3.x implementation now uses
`str.find()`, relying on Python >= 3.6 "widecode" format. Behaviour and
semantics should be unchanged. Now implementations are approx
- 1.5 x faster on Python 2.x
- 2 - 3 x faster on Python 3.x
Before
```console
$ ./tests/bench/scan_code
scan_code_imports python2.7 100 loops, best of 3: 3.19 msec per loop
scan_code_imports python3.9 500 loops, best of 5: 685 usec per loop
scan_code_imports python3.10 500 loops, best of 5: 727 usec per loop
scan_code_imports python3.11 500 loops, best of 5: 601 usec per loop
scan_code_imports python3.12 500 loops, best of 5: 609 usec per loop
scan_code_imports python3.13 500 loops, best of 5: 586 usec per loop
```
After
```console
codeobj_imports python2.7 1000 loops, best of 3: 1.98 msec per loop
codeobj_imports python3.9 1000 loops, best of 5: 302 usec per loop
codeobj_imports python3.10 1000 loops, best of 5: 297 usec per loop
codeobj_imports python3.11 1000 loops, best of 5: 243 usec per loop
codeobj_imports python3.12 1000 loops, best of 5: 278 usec per loop
codeobj_imports python3.13 1000 loops, best of 5: 259 usec per loop
```
```console
$ uname -a
Darwin kintha 24.6.0 Darwin Kernel Version 24.6.0: Mon Jul 14 11:30:29 PDT
2025; root:xnu-11417.140.69~1/RELEASE_ARM64_T6000 arm64
```
```console
$ ./tests/bench/scan_code
scan_code_imports python2.7 100 loops, best of 3: 3.19 msec per loop
scan_code_imports python3.9 500 loops, best of 5: 685 usec per loop
scan_code_imports python3.10 500 loops, best of 5: 727 usec per loop
scan_code_imports python3.11 500 loops, best of 5: 601 usec per loop
scan_code_imports python3.12 500 loops, best of 5: 609 usec per loop
scan_code_imports python3.13 500 loops, best of 5: 586 usec per loop
```
This covers existing behaviours of `mitogen.master.scan_code_imports()` some
of which are relied on, some not, but regardless weren't tested. Notably
- Explicit relative imports return level > 0
- Imports inside `class` and `def` are excluded
- Imports inside other blocks are included
- Python 3.x prunes impossible if/else branches (previously unknown)
It also
- Decouples the test results from the implementation details of the unit test.
- Fixes a missing import
- Fixes at least one Python 2.4 incompatibility (use of with block)
Mitogen was leaving the stdout and stderr of subprocesses in non-blocking
mode. When Python code ran in the remote process created by Mitogen calls such
as `print(long_string)` or `os.stout.write(bigger_than_the_buffer)` sometimes
raised `BlockingIOError`, or similar.
This change
- Removes code in `mitogen.core.Side` that set blocking/non-blocking mode
- Adds blocking/non-blocking control to `os.mitogen.pipe()` and a new
function `mitogen.core.socketpair()`
- Replaces `mitogen.core.set_block` and `mitogen.core.set_nonblock`
with `mitogen.core.set_blocking`, mirroring `os.set_blocking`
- Updates call sites as appropriate
- Adds tests for new functions and arguments
- Adds a regression test for subprocess stdio blocking/non-blocking
fixes#712
This is to prevent job names being truncated in the Github Actions web UI. So
it is obvious at a glance which jobs have failed. Previously one had to click
into the details to know which job was which, leading to confusion and wasted
time.
This also
- removes braced ranges in `testenv.setenv`. They appear not to be supported
by tox (see https://github.com/tox-dev/tox/issues/3571)
- fixes the env var `DEFAULT_STDOUT_CALLBACK` -> `ANSIBLE_STDOUT_CALLBACK`
as a result of these test output format was previously not as intended for
some Ansible versions.
The arguments ansible_ssh_host_key_checking and ansible_host_key_checking were missing in the documentation, while being introduced with the commit "5749845324"
In vanilla Ansible >= 12 (ansible-core 2.19)
- ssh connection plugin `verbosity` controls `ssh [-v[v[v]]]`
- config option `DEFAULT_VERBOSITY` controls whether that output is displayed
In vanilla Ansible <= 11 (ansible-core <= 2.18)
- `DEFAULT_VERBOSITY` controls both `ssh` verbosity & display verbositty
As of this change
- Mitogen + Ansible >= 12 behaviour matches vanilla Ansible >= 12.
- Mitogen + Ansible <= 11 behaviour remains unchanged
- `DEFAULT_VERBOSITY` only controls display verbosity.
- Mitogen + Ansible respect the Ansible variable `mitogen_ssh_debug_level`
I've chosen not to retroactively replicate the old vanilla Ansible behaviour
in Mitogen + Ansible <= 11 cases. I'm pretty sure it was an oversight,
rather than a design choice, but Ansible+Mitogen with `ANSIBLE_VERBOSITY=3`
is already very verbose.
fixes#1282
See
- https://docs.ansible.com/ansible/latest/reference_appendices/config.html#default-verbosity
- https://docs.ansible.com/ansible/devel/collections/ansible/builtin/ssh_connection.html#parameter-verbosity
Ansible 12 (ansible-core 2.19) has gained support for specifying an SSH
password, without requiring `sshpass`. It specifies the environment variable
`SSH_ASKPASS` such that `ansible` itself is called.
Mitogen is already able to support this. This change provides test coverage of
the new feature by not installing `sshpass` on macOS runners. when Ansible 12
is under test. Ubuntu runners come with `sshpass` pre-installed.
Required Ansible is also bumped to the latest pre-releases, for relevant
fixes.
Note that tests/ansible/integration/ssh/templated_by_play_taskvar.yml was
previously erroniously being skipped with ansible-core 2.19.0a<N> and
2.19.0b<N>.
fixes#1293
refs #1175
Ansible >= 12 (ansible-core >= 2.19) deprecates `stdout_callback=yaml`,
superceded by `callback_result_format=yaml`. There is a change in behaviour:
`callback_result_format` applies to output of both `ansible-playbook` _and_
`ansible`.
Tests that run `ansible` in a subprocess are now explicitly configured to use
json (even if they don't inspect that output yet) for more assert-able output
across all versions of Ansible.
Modern versions of `setuptools` emit a warning when the `universal = 1` option of `bdist_wheel` is used. This warning will turn into an error on Aug 30, 2025.
The only function of `universal = 1` is assigning the dual `py2.py3` tag to the wheels. It does not perform any content or metadata compatibility validation that might be related to this.
It is possible to keep producing same-tagged wheels by setting the non-deprecated `python_tag` option instead, which is what this PR does.
Fixes#1283
Ref https://github.com/pypa/setuptools/pull/4939
The function is Ansible >= 12 (ansible-core >= 2.19). See #1274 for analysis
of `json.dumps()` vs `jsonify()` differences. This change is a middle ground
between full backward compatibility and using `json.dumps()` unadorned.
- if `data` is `None`, then it will still be transferred as `{}` on older
versions of Ansible, but 'null' in newer releases. Cases where 'null'
caused a problem are suspected/reported, but no reproducers are available.
- `ensure_ascii=True` will be still be tried, with fallback. I believe this
is only relevant on Python 2.x.
- `sort_keys=True` will no longer be used.
- No indentation/pretty printing will be applied, this remains unchanged
fixes#1274
Some tests have been seen deadlocked. They continued running for an hour+,
until the default Github timeout. Linux timeout higher than macOS because the
Linux jobs run more tests.
The Van_* GitHub Actions jobs (corresponding to Tox factor strategy_linear,
environment variable ANSIBLE_STRATEGY=linear) were failing inside Mitogen
modules, which they should not touch. The jobs are intended as a cross
validation of the test suite, they should only fail if Ansible itself has a
problem.
Python 2.7 (distro package) and 3.6 (pyenv managed) jobs run on Ubuntu 22.04.
More recent Pythons (distro or Github provided) run on 24.04.
fixes#1256
Ansible tasks that run locally (e.g. `connection: local`, `delegate_to:
localhost`) must now specify their `ansible_python_interpreter`, typically as
`{{ ansible_playbook_python }}`; otherwise the system Python on the controller
(e.g. `/usr/bin/python`) is likely to be used and this is often outside the
version range supported by the Ansible verison under test. If this occurs then
the symptom is often a failure to import a builtin from
`ansible.module_utils.six.moves`, e.g.
```
fatal: [target-centos6-1]: FAILED! => changed=true
cmd:
- ansible
- -m
- shell
- -c
- local
- -a
- whoami
- -i
- /tmp/mitogen_ci_ansibled3llejls/hosts
- test-targets
delta: '0:00:02.076385'
end: '2025-04-17 17:27:02.561500'
msg: non-zero return code
rc: 8
start: '2025-04-17 17:27:00.485115'
stderr: |-
stderr_lines: <omitted>
stdout: |-
An exception occurred during task execution. To see the full traceback,
use -vvv. The error was: from ansible.module_utils.six.moves import
map, reduce, shlex_quote
```
Previously (and implicitly) used "latest". The tag 2021 is new today, the
image contents have not changed since they were generated in 2021.
They have moved container registry twice since 2021
- #791 Docker -> Amazon Elastic Container Registry (public.ecr.aws/n5z0e8q)
- #1128 Amazon ECR -> GitHub Container Registry (ghcr.io/mitogen-hq)
This commit also removes the last references to ECR.
Each grouping gets an independant dir, e.g.
- ansible -> /tmp/mitogen_ci_ansible
- debops -> /tmp/mitogen_ci_debops
Importing ci_lib no longer creates a temporary directory as a side effect.
This adapts PR #740 by @extmind (afe0026890),
which augmented the call to `Connection.get_task_var()` with
`C.config.get_config_value('INTERPRETER_PYTHON'` as a default. Instead this
*replaces* the call to `Connection.get_task_var()`. The aim is greater
simplicity by disentangling templating of a configured interpreter path and
discovery of an interpreter when none is configured. I think this also reduces
the number of times `Connection._get_task_vars()` is called, so reducing the
number of times we do the ugly stack frame inspection.
I've also added test cases.
Co-authored-by: Lars Beckers <lars@extmind.de>
Ansible >= 4 (ansible-core >= 2.11) the SSH plugin has a `timeout` option and
with variable `ansible_ssh_timeout`, but not a `ansible_timeout` variable.
The local plugin has no such option or variable(s). However `ansible_timeout`
is backfilled for all conection plugins, by legacy mechanisms that populate
the play context attribute:
- `ansible.constants.COMMON_CONNECTION_VARS`
- `ansible.constants.MAGIC_VARIABLE_MAPPING`
The `timeout` keyword is for task completion timeout, not connection timeout.
This will mitigate the impact of #1185 a little, which causes the job to
continue running without making progress, until it hits this timeout.
Successful jobs are typically completing in 8 - 12 minutes.
refs #1185
The previous commit (53b4881628 in PR 1200) was
not intended to change these values, but some WIP slipped through. This
partially reverts that commit so the two changes (moving the monkey patch,
making the monkey patch more capable) exist in distinct commits.
This tightens up our monkey patching `Connection._action` so it's only applied
during `meta: reset_connection` & promptly removed. This fixes "'int' object
has no attribute 'template'" when `ansible.plugins.action.wait_for_connection`
or other code calls `ansible.plugins.connection.ConnectionBase.reset()`.
This could also have switched to `templar=templar` on the temporary action,
rather than `templar=0`, but it's not strictly necessary to fix this bug. I
anticipate other changes doing so soon, to improve interpreter discovery &
templated python interpreter path support.
The code change to support this was already made in transport_config.py, as
part of templated become_user support (commit bf6607e27e, PR #1148). This
commit adds tests to confirm the functionality.
A twist - for the connection option "host" the corresponding legacy
PlayContext attribute is PlayContext.remote_addr. This may be the only case
where a connection option name and the PlayContext attribute name differ.
This is ground work for adding/testing templated hostnames and python
interpreters. The extreme wideness will hopefully be temporary, e.g. by
switching to YAML inventories. The INI inventory plugin doesn't support
multiline host entries.
> 640 K(olumns) should be enough for anyone
> -- Apocryphal, not Bill Gates
The wrong base was used when calculating the mode. So the file became world
readable and writable on a CI runner, until
ansible/integration/ssh/variables.yml happened to correct it near the end of
the integration tests.
I believe this was the only instance.
```console
mitogen git:(issue1182) ✗ ag --python 'int\(.+7\)' . .ci | wc -l
0
```
fixes#1182
Adding a the tt-ssh-executable test target uncovered an Ansible bug during
`meta: reset_connection` tasks. So this commit includes a workaround for
affected versions of Ansible.
Uses the same fallback for (mitogen_sudo et al) as become_exe (see #1173).
The new `Spec.become_flags()` is not yet explicitly tested. Note that it
returns a string (matching the Ansible option of the same name), whereas
`Spec.sudo_args()` returns a list.
refs #1083
Some ansible_mitogen connection plugins look more like become plugins (e.g.
mitogen_sudo) & use become plugin options. For now there's special handling in
PlayContextSpec._become_option(). Further design/discussion can go in #1173.
Refs #1087.
The tasks in tests/imageprep/_user_accounts.yml that create users did not
specify a primary group for those users - this left the decision to Ansible's
user module, and/or the underlying OS. In Ansible 9+ (ansible-core 2.16+ the
user module defaults to primary group "staff." Earlier don't supply a default,
which releases probably results in a primary group nameed "None" (due to
stringifying the Python singleton of the same name), or whatever the macOS
Directory Services has for no data/NULL.
The invalid GID 4294967295 (MAX_UINT32 == 2**32-1) in the sudo error probably
enters the mix via something similar to sudo CVE-2019-14287.
Fixes#692
See
- https://github.com/ansible/ansible/pull/79999
- https://github.com/ansible/ansible/commit/c69c83c962f987c78af98da0746527df
- https://www.sudo.ws/security/advisories/minus_1_uid/
> Bruce Wayne : [confused] Am I meant to understand any of that?
> Lucius Fox : Not at all, I just wanted you to know how hard it was.
> -- Batman Begins
This reads the become username from the `become_user` attribute of the play
context, to the `"become_user"` option of the loaded become plugin. This has
been supported by vanilla Ansible since Ansible 2.10 (ansible-base 2.10).
To support this I've also switched from using the `play_context.become` (a
bool), to `connection.become` (an instance of the appropriate) become plugin.
New tests have been added, modelled on those for templated connection
parameters (see #1147, #1153, #1159).
See
- 480b106d65
refs #1083
Co-authored-by: mordek <m.pirog@bonasoft.pl>
This reduces the number of jobs from 48 to 24. The Mitogen part of the test
suite has been parameterized on the Linux container targets to be run against.
Both the Ansible tests & Mitogen tests now use the same source of truth to
control which targets to use: environment variable MITOGEN_TEST_DISTRO_SPECS.
This replaces the two mutually exclusive env vars DISTRO and DISTROS. I've
also removed vestgial traces of an unused env var MITOGEN_TEST_DISTRO.
Parameterization adapted from
https://eli.thegreenplace.net/2014/04/02/dynamically-generating-python-test-cases
refs #1058, #1059
The password is provided as a variable because there is no corresponding
keyword. I get the impression that keywords are considered a legacy mechanism,
so most (new) options are only overridable by variables.
The port is proved as a variable for now, to test remote_name in isolation.
This will allow a single job to be required in the GitHub branch protection
web UI; regardless of which jobs are added to or removed from the matrix of
platform specific, Ansible specific jobs.
These targets are not used by any active tests, and the large numbers of hosts
multiply the size of the taskvars disctionary in memory to many (10s) MiB.
refs #1058
By switching to block style (`|`) with clip (no `-` or `+`) the failure
messages don't require quoting and gain a single trailing newline. This causes
Ansible to print them as block style, when using the yaml stdout callback
plugin. As a result the values have one less layer of quoting and quote
escaping, making them much easier to read.
This switches `ansible_mitogen.transport_config.PlayContextSpec.password()` to
Ansible's plugin option framework. As a result
- The relatively recent `ansible_ssh_password` variable is now respected.
- The SSH connection password can be templated and specified as a play
variable. Task variables will probably also work, but testing was blocked
by #1132.
There is a chance this change will cause a regression in another connection
plugin (e.g. mitogen_docker), but nothing turned up in the test suite.
I intend ot migrate other connection configuration to
`ansible_mitogen.transport_config.PlayContextSpec._connect_option()`, the next
candidate is the remote port.
fixes#1106
This replicate the existing Azure DevOps workflow, and adds a couple of new
jobs (Python 2.7 on macOS, Python + vanilla Ansible on Linux).
The GitHub Actions use container images hosted on GitHub Container Registry
(GHCR, ghcr.io/mitogen-hq). These images have been copied straight from the
existing Amazon Elastic Cloud Registry (AWS ECR, public.ecr.aws/n5z0e8q9).
A short period of parallel running is planned. Then a second PR will remove
the Azure DevOps workflow.
This replaces the use of `os.path.realpath()` which gave incorrect results on
macOS - depending on the exact Python build, Python version, macOS version,
installation method, and phase of the moon.
realpath information kept around to aid debugging.
CI containers lack the necessary `setfacl` command. This has not previously
been noticed because no vanilla Ansible jobs were being run on Linux, only on
macOS.
refs #1118
Preperation for migrating from Azure DevOps with Amazon Elastic Container
Registry (AWS ECR), to GitHub Actions with GitHub Container Registry (GHCR).
DebOps tests are not currently being run, the updates to .ci/debops*.py are
best effort only.
Rough guidelines, in decending preference:
- Use mitogen.core if possible
- Use ansible.module_utils.six if possible
- Embed a getattr() or try/except
viewkeys() et al can't be brought into mitogen.core because that package still
targets Python 2.4. dict.viewkeys() were introduced in Python 2.7.
This fixes compatibility with Solaris/Illumos/SmartOS, addressing an issue that shows up most frequently with become. The issue was mostly due to differences in how the TTY driver is handled and the pty driver not supporting echo on both sides of the pipe (as designed, from a Solaris point of view).
Fixes#950
Co-authored-by: Alex Willmer <alex@moreati.org.uk>
Python 3.x emits `ResourceWarning`s if certains resources aren't correctly
closed. Due to the way Mitogen has been terminating child processes this has
been occurring.
```
test_dev_tty_open_succeeds
(create_child_test.TtyCreateChildTest.test_dev_tty_open_succeeds) ...
/opt/hostedtoolcache/Python/3.12.5/x64/lib/python3.12/subprocess.py:1127:
ResourceWarning: subprocess 3313 is still running
_warn("subprocess %s is still running" % self.pid,
ResourceWarning: Enable tracemalloc to get the object allocation traceback
ok
```
During garbage collection subprocess.Popen() objects emit
ResourceWarning("subprocess 123 is still running")
if proc.returncode hasn't been set. Typically calling proc.wait() does so,
once the sub-process has exited. Calling os.waitpid(proc.pid, 0) also waits
for the sub-process to exit, but it doesn't update proc.returncode, so the
ResourceWarning is still emitted.
This change exposes `subprocess.Popen` methods on
`mitogen.parent.PopenProcess`, so that the returncode can be set.
See https://gist.github.com/moreati/b8d157ff82cb15234bece4033accc5e5
This
- Removes the indirection of calling ansible in a sub-shell
- Includes vanilla Ansible, which was previously skipped
- Tests whether ansible_ssh_pass overrides ansible_password, as it should
As a one off I've the new tests against vanilla Ansible 2.10 through Ansible
10, to confirm the baseline priorities have remained unchanged all releases
currently supported by Mitogen 0.3.x.
The bug was fixed in a previous commit by Jonathan Rosser. This adds testing.
The bug is only triggered when the copy module is used inside a `with_items:`
loop and the destination filename has an extension. A `loop:` loop is not
sufficient.
refs #1110
Small files are carried in-band in the communication between
controller and remote, with larger files being copied by falling back
to a more traditional ansible put_file mechanism. This large
file code path was missed in b822f20.
This is in anticipation of #1110, which only exhibits inside a with_items:
loop. For this refactor `loop:` is used, to confirm the refactored tests are
still correct. A subsequent commit will change them to with_items.
The content of the files and their SHA1 checksums are unchanged.
Relying on the virtualenv default or hardcoding "python" results in a Python
2.x virtualenv on some targets (e.g. debian10-test). This caused a failure
when testing with Ansible >= 10 (ansible-core >= 2.17), which have dropped
Python 2.x support.
refs #1074
By setting ansible_python_interpreter for these fictious hosts we avoid
Ansible trying and failing to connect to them in a attempt to populate
ansible_facts.discovered_interpreter_python. This speeds up these tests by
avoiding a timeout.
It is also a necessary pre-requisite for Ansible 10 (ansible-core 2.17). In
that release no hardcoded fallback is used, failure to determine a valid
Python interpreter is a fatal error.
refs #1074
To avoid rate limiting errors, CI (currently Azure Devops) logs into the
container registry (currently AWS ECR). Outside CI this is unnnecessary and
makes it harder to run the tests, because very few people have access to a
suitable AWS secret token.
Following this change `aws ecr-public get-login-password` will only be run if
the environment variable $TF_BUILD==True. This is set by Azure Pipelines
jobs. If the CI platform is changed then another indicator should be used.
https://adamj.eu/tech/2020/03/09/detect-if-your-tests-are-running-on-ci/
Pip 72 was released yesterday (2024-07-28), dropping `setup.py test` support.
hdrhistogram 0.6.1 requires it to install.
For now constrain Pip to earlier releases, so our tests can be run.
refs #1090
macOS 11 is not longer an available runner on Azure Devops. The minimum is now
macOS 12. This runner does not have Python 2.7 installed, so running them
would require a custom install - which I'm declaring too much effort for too
little gain.
refs #1090
CentOS 8 has reached EOL. Packages are no longer mirrored or maintained. A
historic snapshot of the packages is kept on vault.centos.org.
refs #1088, #1090
`mitogen.parent.POLLER_LIGHTWEIGHT` will normally be `PollPoller`, falling
back to `EpollPoller`, `KqueuePoller`, or `Poller`.
Fixes#957
Co-authored-by: Luca Berruti <nadirio@gmail.com>
Co-authored-by: Philippe Kueck <bqobccy6ejnq2bqvmebqiwqha4cs4@protected32.unixadm.org>
Follwing fixes in Ansible 7-9 for CVE-2023-5764 cating `AnsibleUnsafeBytes` &
`AnsibleUnsafeText` to `bytes()` or `str()` requires special handling. The
handling is Ansible specific, so it shouldn't go in the mitogen package but
rather the ansible_mitogen package.
`ansible_mitogen.utils.unsafe.cast()` is most like `mitogen.utils.cast()`.
During development it began as `ansible_mitogen.utils.unsafe.unwrap_var()`,
closer to an inverse of `ansible.utils.unsafe_procy.wrap_var()`. Future
enhancements may move in this direction.
refs #977, refs #1046
See also
- https://github.com/advisories/GHSA-7j69-qfc3-2fq9
- https://github.com/ansible/ansible/pull/82293
- https://github.com/mitogen-hq/mitogen/wiki/AnsibleUnsafe-notes
If casting a string fails then raise a TypeError. This is potentially an API
breaking change; chosen as the lesser evil vs. allowing silent errors.
`cast()` relies on `bytes(obj)` & `str(obj)` returning the respective
supertype. That's no longer the case for `AnsibleUnsafeBytes` &
`AnsibleUnsafeText`; since fixes/mitigations for CVE-2023-5764.
fixes#1046, refs #977
See also
- https://github.com/advisories/GHSA-7j69-qfc3-2fq9
- https://github.com/ansible/ansible/pull/82293
Most of the necessary changes were made in recent PEP 451 commits. This bumps
the CI jobs, and declares the support. Test dependendancies are bumped to
latest supportted/available versions.
refs #1033
importlib.machinery.ModuleSpec and find_spec() were introduced in Python 3.4
under PEP 451. They replace the find_module() API of PEP 302, which was
deprecated from Python 3.4. They were removed in Python 3.12 along with the
imp module.
This change adds support for the PEP 451 APIs. Mitogen should no longer import
imp on Python versions that support ModuleSpec. Tests have been added to cover
the new APIs.
CI jobs have been added to cover Python 3.x on macOS.
Refs #1033
Co-authored-by: Witold Baryluk <witold.baryluk@gmail.com>
Using https:// requires certificate store management and additional parameter
passing that changed across Ansible and Python versions. Using http:// allows
the same tests to be used across wider spans of Python version on the
controller, and Python verison on the targets.
Python 3.12 on a target + get_uri needs Ansible >= 8 (ansible-core >= 2.15).
Python 3.12 removed deprecated httplib.HTTPSConnection() arguments.
https://github.com/ansible/ansible/pull/80751
With current macOS 11 runner images (20231216.1) the `python` on `$PATH` is
Python 3.12 and setuptools isn't installed by default. E.g.
```
python -mtox -e "py27-mode_localhost-ansible4"
========================== Starting Command Output ===========================
/bin/bash --noprofile --norc
/Users/runner/work/_temp/93a29c4c-f606-45e4-8dbd-a4a5f51b8730.sh
GLOB sdist-make: /Users/runner/work/1/s/setup.py
ERROR: invocation failed (exit code 1), logfile:
/Users/runner/work/1/s/.tox/log/GLOB-0.log
================================== log start
===================================
Traceback (most recent call last):
File "/Users/runner/work/1/s/setup.py", line 32, in <module>
from setuptools import find_packages, setup
ModuleNotFoundError: No module named 'setuptools'
```
Installing setuptools under Python 3.12 chooses package versions incompatible
with Python 2.7. Additionally Mitogen isn't yet compatible with Python 3.12
(#1033), so tests that call a local context with `python` fail.
This prevents unreleased versions appearing on the website (e.g. 0.3.5.dev0),
but introduces the risk of forgetting to update the website after a release.
A better fix requires deeper design/workflow thought.
refs #1028
This should address the warning in Azure Pipelines
> You should provide GitHub token if you want to download a python release.
> Otherwise you may hit the GitHub anonymous download limit.
The token is provided from a secret variable in the pipeline.
Fixes
```
======================================================================
ERROR: setUpClass (ssh_test.BannerTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/vsts/work/1/s/tests/testlib.py", line 625, in setUpClass
cls.dockerized_ssh = DockerizedSshDaemon(**daemon_args)
File "/home/vsts/work/1/s/tests/testlib.py", line 553, in __init__
self.start_container()
File "/home/vsts/work/1/s/tests/testlib.py", line 533, in start_container
self._get_container_port()
File "/home/vsts/work/1/s/tests/testlib.py", line 510, in _get_container_port
self.port = int(bport)
ValueError: invalid literal for int() with base 10: ':]:32770'
```
To do so the test suite allows a weak cryptographic alogorithm (SHA1) to be
used, principally for CentOS 6 targets. This can be removed if/when support
for older (legacy) targets is dropped.
Only the test suite enables this known weak alogorithm. Mitogen as-shipped
doesn't enable or disable algorithms.
I'm abandoning tox-factor because having any [tox] requires = ... causes tox
3.x to create an isolated virtualenv for running tox itself. Since Tox 4.x was
released that virtualenv gets it, which is incompatible with the tox-factor
plugin.
e.g.
```
Traceback (most recent call last):
File
"/Users/runner/work/1/s/.tox/.tox/lib/python3.10/site-packages/tox_factor/compat.py",
line 2, in <module>
from tox.config.parallel import ENV_VAR_KEY_PUBLIC as TOX_PARALLEL_ENV
ModuleNotFoundError: No module named 'tox.config.parallel'
```
faulthandler is a stdlib module in Python 3.3+. For a long time a PyPI package
of the same name was available for earlier Python releases. That package has
since been removed from PyPI, and the source respoitory archived. So we should
not rely on it.
fixes#983 refs #970
Refs #925#969
I'm not 100% confident that merely removing this is the full fix,
without substituting something else. I am sure keeping it would be
the greater of two evils. __del__() should be avoided on general
principal, and it's associated with multiple intermittant CI
failures, plus multiple user reported issues.
Until Ansible 2.9 it looks like ansible_become_password had higher priority.
From Ansible 2.10 ansible_become_pass has higher priority [1]. Mitogen was not
respecting this.
I may need to rework this further, instatiating the become plugin may have
slowed down execution.
[1] Based on testing with
```
[ubuntus]
become-pass-pass ansible_become_pass=1234
become-pass-password ansible_become_password=1234
become-pass-both ansible_become_password=wrong ansible_become_pass=1234
[ubuntus:vars]
ansible_host=ubuntu2004.local
ansible_user=ubuntu
```
```
- hosts: ubuntus
gather_facts: false
become: true
tasks:
- ping:
```
e.g. in Ansible 6, ansible-core 2.13
```
[mux 2717] 23:39:11.342416 D mitogen: PkgutilMethod(): _AnsibleCollectionLoader(path=None).get_file_name('ansible.plugins') failed: ValueError('_AnsibleCollectionLoader(path=None) cannot find files for ansible.plugins, only ansible_collections.ansible.builtin.plugins')
```
This reapplies an earlier change, when this plugin was first introduced to
Mitogen. The plugin was updated to fix
[DEPRECATION WARNING]: The '_remote_checksum()' method is deprecated.
I've elected to short-circuit the if statemtn logic, rather than
deleting/unindenting, to make the code delta much smaller. This should make it
easier to maintain/update.
Fixes#915
Co-authored-by: Stefano Rivera <stefano@rivera.za.net>
When the requested module (e.g. ansible.module_utils.distro)
- is provided by another module *e.g. distro)
- that itself was a package (e.g. distro 1.7.0)
At runtime
- ansible/module_utils/distro/__init__.py executes
- if https://pypi.org/project/distro/ is present, it's loaded as
ansible.module_utils.distro
- otherwise ansible/module_utils/distro/_distro.py is loaded
ParentEnumerationMethod would wrongly use whatever was in
sys.modules['ansible.module_utils.distro]. Instead we should ascend to
the first parent that has fullname == sys.modules[fullname].__name__.
Then descend to the appropriate .py file on disk.
This bug didn't show up before because until distro 1.7.0 (Feb 2022) the
top-level distro module was a module (distro.py) not a package
(distro/__init__.py)
fixes#906
<aclass="github-button"href="https://github.com/dw/mitogen/" data-size="large"data-show-count="true"aria-label="Star dw/mitogen on GitHub">Star</a>
<aclass="github-button"href="https://github.com/mitogen-hq/mitogen/" data-size="large"data-show-count="true"aria-label="Star mitogen on GitHub">Star</a>
* :gh:issue:`874` Support for Ansible 5 (ansible-core 2.12)
* :gh:issue:`774` Fix bootstrap failures on macOS 11.x and 12.x, involving Python 2.7 wrapper
* :gh:issue:`834` Support for Ansible 3 and 4 (ansible-core 2.11)
@ -37,7 +498,7 @@ v0.3.0 (2021-11-24)
-------------------
This release separates itself from the v0.2.X releases. Ansible's API changed too much to support backwards compatibility so from now on, v0.2.X releases will be for Ansible < 2.10 and v0.3.X will be for Ansible 2.10+.
`See here for details <https://github.com/dw/mitogen pull/715#issuecomment-750697248>`_.
`See here for details <https://github.com/mitogen-hq/mitogen/pull/715#issuecomment-750697248>`_.
<li><a href="https://underwhelm.net/">Dan Dorman</a> — - <em>When I truly understand my enemy … then in that very moment I also love him.</em></li>