Commit Graph

1136 Commits (4fdd2e38f90f03a9abbdd745d8ef8b0267e4bbd9)

Author SHA1 Message Date
Alex Willmer f966b3e5c6 CI: Remove lingering stdout_callback=yaml in macOS jobs
Support removed in Ansible 13 (ansible-core 2.20).
refs #1285, #1291
1 month ago
Alex Willmer 83c5ab1900 mitogen: Send first stage parameters as argv (796 bytes -> 822)
Benefit: The base64 lump is now static for a given Mitogen version, and the
variable parts are more visible. This will make debugging, auditting, and
allow-listing a bit easier.
Potential benefit: generate the base64 once, at build time or startup. Rather
than once per connection.
Cost: Bootstrap command is 26 bytes longer.

```
➜  mitogen git:(boot-cmd--argv) ✗ ./preamble_size.py
SSH command size: 822
Preamble (mitogen.core + econtext) size: 18230 (17.80KiB)

                        Original           Minimized           Compressed
mitogen.core         152237 148.7KiB  68453 66.8KiB 45.0%  18130 17.7KiB 11.9%
mitogen.parent        98746  96.4KiB  51215 50.0KiB 51.9%  12922 12.6KiB 13.1%
mitogen.fork           8445   8.2KiB   4139  4.0KiB 49.0%   1652  1.6KiB 19.6%
mitogen.ssh           10847  10.6KiB   6913  6.8KiB 63.7%   2102  2.1KiB 19.4%
mitogen.sudo          12089  11.8KiB   5924  5.8KiB 49.0%   2249  2.2KiB 18.6%
mitogen.select        12325  12.0KiB   2929  2.9KiB 23.8%    964  0.9KiB  7.8%
mitogen.service       41581  40.6KiB  22398 21.9KiB 53.9%   5847  5.7KiB 14.1%
mitogen.fakessh       15753  15.4KiB   8135  7.9KiB 51.6%   2672  2.6KiB 17.0%
mitogen.master        52891  51.7KiB  27586 26.9KiB 52.2%   7129  7.0KiB 13.5%
```
2 months ago
Marc Hartmayer dad28e8b4a tests: Add a test case that verifies behavior when the log record factory is modified
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>
2 months ago
Alex Willmer 7d5f63ccbf Cleanup unused and missing imports 2 months ago
Alex Willmer 090952a987 Python 3.14 support 3 months ago
Alex Willmer d27275ad46 ci: Set global max failed logins on macOS 3 months ago
Alex Willmer 1b00ca2581 tests: Bump dependency versions 3 months ago
Alex Willmer 85d6046f2f mitogen: Fix non-blocking IO errors in first stage of bootstrap
When /etc/sudoers has log_output (or similar) enabled the process spawned by
`ctx.sudo()` via `mitogen.parent.Connection.start_child()` receives a stdin
that is in non-blocking mode. The immediate symptom is that `os.openfd(0,
...).read(n)` sometimes returns `None`, causing the first stage to raise an
unhandled TypeError.

The fix (for now) is to use `select.select()` in a while loop to read stdin.
This increases the command size slightly, but I think it's a reasonable
tradeoff until/unless the cause is more fully understood.

All CI tests are now run with sudoers log_output enabled, in order to catch
regressions. `first_stage_test.CommandLineTest` has been amended, because it
relied on implementation details of the bootstrap process that are no longer
true.

Before
```
SSH command size: 755
Preamble (mitogen.core + econtext) size: 18227 (17.80KiB)

                        Original           Minimized           Compressed
mitogen.core         152218 148.7KiB  68437 66.8KiB 45.0%  18124 17.7KiB 11.9%
mitogen.parent        98853  96.5KiB  51103 49.9KiB 51.7%  12881 12.6KiB 13.0%
mitogen.fork           8445   8.2KiB   4139  4.0KiB 49.0%   1652  1.6KiB 19.6%
mitogen.ssh           10827  10.6KiB   6893  6.7KiB 63.7%   2099  2.0KiB 19.4%
mitogen.sudo          12089  11.8KiB   5924  5.8KiB 49.0%   2249  2.2KiB 18.6%
mitogen.select        12325  12.0KiB   2929  2.9KiB 23.8%    964  0.9KiB  7.8%
mitogen.service       41581  40.6KiB  22398 21.9KiB 53.9%   5847  5.7KiB 14.1%
mitogen.fakessh       15767  15.4KiB   8149  8.0KiB 51.7%   2676  2.6KiB 17.0%
mitogen.master        55317  54.0KiB  28846 28.2KiB 52.1%   7528  7.4KiB 13.6%
```

After
```
SSH command size: 798
Preamble (mitogen.core + econtext) size: 18227 (17.80KiB)

                        Original           Minimized           Compressed
mitogen.core         152218 148.7KiB  68437 66.8KiB 45.0%  18124 17.7KiB 11.9%
mitogen.parent        98944  96.6KiB  51180 50.0KiB 51.7%  12910 12.6KiB 13.0%
mitogen.fork           8445   8.2KiB   4139  4.0KiB 49.0%   1652  1.6KiB 19.6%
mitogen.ssh           10827  10.6KiB   6893  6.7KiB 63.7%   2099  2.0KiB 19.4%
mitogen.sudo          12089  11.8KiB   5924  5.8KiB 49.0%   2249  2.2KiB 18.6%
mitogen.select        12325  12.0KiB   2929  2.9KiB 23.8%    964  0.9KiB  7.8%
mitogen.service       41581  40.6KiB  22398 21.9KiB 53.9%   5847  5.7KiB 14.1%
mitogen.fakessh       15767  15.4KiB   8149  8.0KiB 51.7%   2676  2.6KiB 17.0%
mitogen.master        55317  54.0KiB  28846 28.2KiB 52.1%   7528  7.4KiB 13.6%
```
4 months ago
Alex Willmer c508bfb58b tests: Check stdio is blocking in sudo contexts
refs #712
4 months ago
Alex Willmer 76f6eb741d tests: Count bytes written in stdio_test.StdIOTest
This is mainly for peace of mind. With all this non-blocking IO investigation
I'm getting a bit paranoid wrt file objects.

refs #712
4 months ago
Alex Willmer e4e2c6caaf CI: Move sudo test users defaults into /etc/sudoers.d
Prep for reusing it in non-Ansible tests
4 months ago
Alex Willmer 5abdde1117 CI: Report sudo version on Ansible targets 4 months ago
Alex Willmer dc7fae973b CI: Fix ci_lib and test_lib have_<cmd>() when <cmd> exits abnormally
We were not raising CalledProcessError when exit status != 0.
4 months ago
Alex Willmer 0e5f47f145 mitogen: Refactor scan_code_imports() as mitogen.imports.codeobj_imports()
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
```
4 months ago
Alex Willmer 3093d0bb2d tests: Add scan_code_imports benchmark
```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
```
4 months ago
Alex Willmer 2fd88298ae tests: Improve master_test.ScanCodeImportsTest coverage
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)
4 months ago
Alex Willmer 618eccc0f3 CI: Set macOS failed logins limit of mitogen test users to 1000
refs #1315
4 months ago
Alex Willmer 17bee70dc2 mitogen: Fix BlockingIOError & EAGAIN in subprocess stdio
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
5 months ago
Alex Willmer 868d77a402 ansible_mitogen: Fix become_method=doas, add tests 5 months ago
Alex Willmer 573303ac73 CI: Switch to archived Debian 10 (buster) apt repository
The Debian project recently removed this EOL version from the live mirrors.
5 months ago
Alex Willmer c1296b5d75 ansible_mitogen: Support ANSIBLE_SSH_VERBOSITY with Ansible >= 12
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
6 months ago
Alex Willmer 55b0ece0e7 CI: Test SSH password authentication without sshpass command
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.
6 months ago
Alex Willmer 3cba11a126 CI: Fix ansible_version comparison with ansible-core 2.19.0rc1
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
6 months ago
Alex Willmer f330c2b158 CI: replace stdout=yaml with result_format=yaml for Ansible >= 6 tests
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.
6 months ago
Alex Willmer 2598941384 tests: Add Debian 11/bullseye security archive signing key
Tests that install packages are failing due to repos/packages that are signed
with this key.

```console
$ wget https://ftp-master.debian.org/keys/archive-key-11-security.asc
--2025-06-17 14:36:04--  https://ftp-master.debian.org/keys/archive-key-11-security.asc
Resolving ftp-master.debian.org (ftp-master.debian.org)... 192.91.235.231
Connecting to ftp-master.debian.org (ftp-master.debian.org)|192.91.235.231|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 11873 (12K) [application/pgp-keys]
Saving to: ‘archive-key-11-security.asc’

archive-key-11-security.asc 100%[=================>]  11.59K  --.-KB/s    in 0.002s

2025-06-17 14:36:05 (6.64 MB/s) - ‘archive-key-11-security.asc’ saved [11873/11873]

$ sha256sum archive-key-11-security.asc
716e79393c724d14ecba8be46e99ecbe1b689f67ceff3cb3cab28f6e69e8b8b8  archive-key-11-security.asc
$ cp archive-key-11-security.asc \
     ~/src/mitogen/tests/image_prep/roles/package_manager/files/debian-archive-bullseye-security-automatic.asc
```
6 months ago
Alex Willmer 0187418697 ansible_mitogen: alpha datatag handling & CI for Ansible 12 (ansible-core 2.19)
refs #1258
7 months ago
Alex Willmer a00263d963 CI: Fix cosmetic typos in get_with_context() regression test 7 months ago
Alex Willmer 68f17420e2 CI: Name Ansible stack construction plays
Simplifies matching a failed test to the play/task.
7 months ago
Alex Willmer c31d902dc8 CI: Skip tests that require Mitogen when Ansible strategy is linear
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.
7 months ago
Alex Willmer 4c41bf02f1 CI: Specify ANSIBLE_STRATEGY in tasks that run ansible or ansible-playbook
This makes the behaviour more consistent across jobs that run with
`mitogen_linear` or plain `linear`.
7 months ago
Alex Willmer a9048f0f7d CI: Use Ansible finished test (`result.finished` -> `result is finished`)
Required by Ansible 12 (ansible-core-2.19).

refs #1298
7 months ago
Alex Willmer 491d438427 CI: Add is_macos_controller Ansible variable for become_unpriv_available
This eliminates the need for ansible_facts to be gathered before
become_unpriv_available can be referenced.
7 months ago
Alex Willmer 27b4b77bba CI: Upgrade Github jobs from Ubuntu 20.04 to 22.04 & 24.04
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
```
8 months ago
Alex Willmer fdbd1a8c9b ci: Configure package managers using a role
This allows code sharing between integration tests and test image prep.
10 months ago
Alex Willmer 1e12edbf95 ci: Use file module to set mitogen__readonly_homedir permissions 10 months ago
Alex Willmer f2e0e552ac ci: Fix sshd configuration during image prep
This will allow image preparation using Ansible versions that
- predate ansible_facts.*
- predate loop keyword
- predate collections
10 months ago
Alex Willmer 20e23b5bd9 ci: Name all image prep plays 10 months ago
Alex Willmer 8e58c4a759 ci: Decouple image prep from Ansible controller reporting
This will allow image preparation using Ansible versions that predate
import_playbook.
10 months ago
Alex Willmer 67ececc804 ci: Use GitHub Container Registery images tagged 2021
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.
10 months ago
Alex Willmer 78b440104e CI: Validate sudoers file 11 months ago
Alex Willmer c92df356e6 CI: Consolidate sudoers config tasks 11 months ago
Alex Willmer 11d2d70fd8 CI: Use native Ansible support to hide macOS users 11 months ago
Alex Willmer 5283e6756b CI: Statically specify test usernames and group names
This makes it easier to grep for a username and to discover how the user was
create. Hence it should be easier to understand/debug tests.
11 months ago
Alex Willmer f82c72f539 tests: Name transport_config tests that involve mitogen_via
This should make it much easier to find a (failed) test, based on test output.
11 months ago
Alex Willmer 1b8b2c8b1a ansible_mitogen: Rename Mitogen interpreter discovery attributes
This makes their nature and ownership/responsibility much more explicit.
11 months ago
Alex Willmer 9342186b22 tests: Fix unclosed file in fd_check script 11 months ago
Alex Willmer 6698f4bcd9 tests: Remove unused tasks fragment 11 months ago
Alex Willmer e564944c5b tests: Stricter playbook and inventory parsing 11 months ago
Jarl Gullberg 211079f130
Add regression tests for the new connection logic.
Co-authored-by: Alex Willmer <alex@moreati.org.uk>
Co-authored-by: Mark Farrell <mark.a.farrell@team.telstra.com>
11 months ago
Alex Willmer 945e360363 ansible_mitogen: Respect interpreter_python and ANSIBLE_PYTHON_INTERPRETER
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>
12 months ago