Skip to content

Commit ce0c596

Browse files
authored
Merge pull request #1338 from moreati/release-v0.3.28
Release v0.3.28
2 parents ced63f9 + 2791abe commit ce0c596

File tree

14 files changed

+175
-50
lines changed

14 files changed

+175
-50
lines changed

.ci/ci_lib.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
'MITOGEN_TEST_IMAGE_TEMPLATE',
4242
'ghcr.io/mitogen-hq/%(distro)s-test:2021',
4343
)
44+
SUDOERS_DEFAULTS_SRC = './tests/image_prep/files/sudoers_defaults'
45+
SUDOERS_DEFAULTS_DEST = '/etc/sudoers.d/mitogen_test_defaults'
4446
TESTS_SSH_PRIVATE_KEY_FILE = os.path.join(GIT_ROOT, 'tests/data/docker/mitogen__has_sudo_pubkey.key')
4547

4648

@@ -58,6 +60,7 @@ def _have_cmd(args):
5860
try:
5961
subprocess.run(
6062
args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
63+
check=True,
6164
)
6265
except OSError as exc:
6366
if exc.errno == errno.ENOENT:

.ci/mitogen_tests.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Run the Mitogen tests.
33

44
import os
5+
import subprocess
56

67
import ci_lib
78

@@ -13,6 +14,14 @@
1314
if not ci_lib.have_docker():
1415
os.environ['SKIP_DOCKER_TESTS'] = '1'
1516

17+
subprocess.check_call(
18+
"umask 0022; sudo cp '%s' '%s'"
19+
% (ci_lib.SUDOERS_DEFAULTS_SRC, ci_lib.SUDOERS_DEFAULTS_DEST),
20+
shell=True,
21+
)
22+
subprocess.check_call(['sudo', 'visudo', '-cf', ci_lib.SUDOERS_DEFAULTS_DEST])
23+
subprocess.check_call(['sudo', '-l'])
24+
1625
interesting = ci_lib.get_interesting_procs()
1726
ci_lib.run('./run_tests -v')
1827
ci_lib.check_stray_processes(interesting)

ansible_mitogen/planner.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,25 @@ def get_module_deps(self):
347347
'freeipa.ansible_freeipa.ipaautomountlocation',
348348
'freeipa.ansible_freeipa.ipaautomountmap',
349349
'freeipa.ansible_freeipa.ipacert',
350+
'freeipa.ansible_freeipa.ipaclient_api',
351+
'freeipa.ansible_freeipa.ipaclient_fix_ca',
352+
'freeipa.ansible_freeipa.ipaclient_fstore',
353+
'freeipa.ansible_freeipa.ipaclient_get_otp',
354+
'freeipa.ansible_freeipa.ipaclient_ipa_conf',
355+
'freeipa.ansible_freeipa.ipaclient_join',
356+
'freeipa.ansible_freeipa.ipaclient_set_hostname',
357+
'freeipa.ansible_freeipa.ipaclient_setup_automount',
358+
'freeipa.ansible_freeipa.ipaclient_setup_certmonger',
359+
'freeipa.ansible_freeipa.ipaclient_setup_firefox',
360+
'freeipa.ansible_freeipa.ipaclient_setup_krb5',
361+
'freeipa.ansible_freeipa.ipaclient_setup_nis',
362+
'freeipa.ansible_freeipa.ipaclient_setup_nss',
363+
'freeipa.ansible_freeipa.ipaclient_setup_ntp',
364+
'freeipa.ansible_freeipa.ipaclient_setup_ssh',
365+
'freeipa.ansible_freeipa.ipaclient_setup_sshd',
366+
'freeipa.ansible_freeipa.ipaclient_temp_krb5',
367+
'freeipa.ansible_freeipa.ipaclient_test',
368+
'freeipa.ansible_freeipa.ipaclient_test_keytab',
350369
'freeipa.ansible_freeipa.ipaconfig',
351370
'freeipa.ansible_freeipa.ipadelegation',
352371
'freeipa.ansible_freeipa.ipadnsconfig',

docs/changelog.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,20 @@ To avail of fixes in an unreleased version, please download a ZIP file
1818
`directly from GitHub <https://github.com/mitogen-hq/mitogen/>`_.
1919

2020

21+
v0.3.28 (2025-09-17)
22+
--------------------
23+
24+
* :gh:issue:`1306` :mod:`ansible_mitogen`: Fix non-blocking IO errors in
25+
first stage of bootstrap
26+
* :gh:issue:`1306` CI: Report sudo version on Ansible targets
27+
* :gh:issue:`1306` CI: Move sudo test users defaults into ``/etc/sudoers.d``
28+
* :gh:issue:`1306` preamble_size: Fix variability of measured command size
29+
* :gh:issue:`1306` tests: Count bytes written in ``stdio_test.StdIOTest``
30+
* :gh:issue:`1306` tests: Check stdio is blocking in sudo contexts
31+
* :gh:issue:`1327` :mod:`ansible_mitogen`: Add FreeIPA client modules to the
32+
always-fork list
33+
34+
2135
v0.3.27 (2025-08-20)
2236
--------------------
2337

mitogen/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636

3737
#: Library version as a tuple.
38-
__version__ = (0, 3, 27)
38+
__version__ = (0, 3, 28)
3939

4040

4141
#: This is :data:`False` in slave contexts. Previously it was used to prevent

mitogen/parent.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,9 +1440,9 @@ def _first_stage():
14401440
os.environ['ARGV0']=sys.executable
14411441
os.execl(sys.executable,sys.executable+'(mitogen:CONTEXT_NAME)')
14421442
os.write(1,'MITO000\n'.encode())
1443-
fp=os.fdopen(0,'rb')
1444-
C=zlib.decompress(fp.read(PREAMBLE_COMPRESSED_LEN))
1445-
fp.close()
1443+
C=''.encode()
1444+
while PREAMBLE_COMPRESSED_LEN-len(C)and select.select([0],[],[]):C+=os.read(0,PREAMBLE_COMPRESSED_LEN-len(C))
1445+
C=zlib.decompress(C)
14461446
fp=os.fdopen(W,'wb',0)
14471447
fp.write(C)
14481448
fp.close()
@@ -1478,11 +1478,12 @@ def get_boot_command(self):
14781478

14791479
# Just enough to decode, decompress, and exec the first stage.
14801480
# Priorities: wider compatibility, faster startup, shorter length.
1481-
# `import os` here, instead of stage 1, to save a few bytes.
14821481
# `sys.path=...` for https://github.com/python/cpython/issues/115911.
1482+
# `import os,select` here (not stage 1) to save a few bytes overall.
14831483
return self.get_python_argv() + [
14841484
'-c',
1485-
'import sys;sys.path=[p for p in sys.path if p];import binascii,os,zlib;'
1485+
'import sys;sys.path=[p for p in sys.path if p];'
1486+
'import binascii,os,select,zlib;'
14861487
'exec(zlib.decompress(binascii.a2b_base64("%s")))' % (encoded.decode(),),
14871488
]
14881489

preamble_size.py

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import sys
99
import zlib
1010

11+
import mitogen.core
1112
import mitogen.fakessh
1213
import mitogen.fork
1314
import mitogen.master
@@ -18,14 +19,28 @@
1819
import mitogen.ssh
1920
import mitogen.sudo
2021

22+
23+
class Table(object):
24+
HEADERS = (' ', 'Original', 'Minimized', 'Compressed')
25+
HEAD_FMT = '{:20} {:^15} {:^19} {:^19}'
26+
ROW_FMT = '%-20s %6i %5.1fKiB %5i %4.1fKiB %4.1f%% %5i %4.1fKiB %4.1f%%'
27+
28+
def header(self):
29+
return self.HEAD_FMT.format(*self.HEADERS)
30+
31+
2132
router = mitogen.master.Router()
2233
context = mitogen.parent.Context(router, 0)
23-
options = mitogen.ssh.Options(max_message_size=0, hostname='foo')
34+
options = mitogen.ssh.Options(
35+
hostname='foo',
36+
max_message_size=0,
37+
remote_name='alice@host:1234',
38+
)
2439
conn = mitogen.ssh.Connection(options, router)
2540
conn.context = context
2641

2742
print('SSH command size: %s' % (len(' '.join(conn.get_boot_command())),))
28-
print('Bootstrap (mitogen.core) size: %s (%.2fKiB)' % (
43+
print('Preamble (mitogen.core + econtext) size: %s (%.2fKiB)' % (
2944
len(conn.get_preamble()),
3045
len(conn.get_preamble()) / 1024.0,
3146
))
@@ -36,17 +51,10 @@
3651
exit()
3752

3853

39-
print(
40-
' '
41-
' '
42-
' Original '
43-
' '
44-
' Minimized '
45-
' '
46-
' Compressed '
47-
)
48-
54+
table = Table()
55+
print(table.header())
4956
for mod in (
57+
mitogen.core,
5058
mitogen.parent,
5159
mitogen.fork,
5260
mitogen.ssh,
@@ -63,13 +71,7 @@
6371
compressed = zlib.compress(minimized.encode(), 9)
6472
compressed_size = len(compressed)
6573
print(
66-
'%-25s'
67-
' '
68-
'%5i %4.1fKiB'
69-
' '
70-
'%5i %4.1fKiB %.1f%%'
71-
' '
72-
'%5i %4.1fKiB %.1f%%'
74+
table.ROW_FMT
7375
% (
7476
mod.__name__,
7577
original_size,

tests/ansible/setup/report_targets.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,23 @@
1313
- debug: {var: ansible_facts.osversion}
1414
- debug: {var: ansible_facts.python}
1515
- debug: {var: ansible_facts.system}
16+
17+
- name: Check target versions
18+
hosts: localhost:test-targets
19+
check_mode: false
20+
tasks:
21+
- name: Get command versions
22+
command:
23+
cmd: "{{ item.cmd }}"
24+
changed_when: false
25+
check_mode: false
26+
loop:
27+
- cmd: sudo -V
28+
register: command_versions
29+
30+
- name: Show command versions
31+
debug:
32+
msg: |
33+
cmd: {{ item.item.cmd }}
34+
{{ item.stdout }}
35+
loop: "{{ command_versions.results }}"

tests/data/stdio_checks.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,24 @@
33
import sys
44

55

6+
def _shout_stdout_py3(size):
7+
nwritten = sys.stdout.write('A' * size)
8+
return nwritten
9+
10+
11+
def _shout_stdout_py2(size):
12+
shout = 'A' * size
13+
nwritten = 0
14+
while nwritten < size:
15+
nwritten += os.write(sys.stdout.fileno(), shout[-nwritten:])
16+
return nwritten
17+
18+
619
def shout_stdout(size):
7-
sys.stdout.write('A' * size)
8-
return 'success'
20+
if sys.version_info > (3, 0):
21+
return _shout_stdout_py3(size)
22+
else:
23+
return _shout_stdout_py2(size)
924

1025

1126
def file_is_blocking(fobj):

tests/first_stage_test.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import subprocess
22

3+
import mitogen.core
34
import mitogen.parent
45
from mitogen.core import b
56

@@ -21,14 +22,18 @@ def test_valid_syntax(self):
2122
conn.context = mitogen.core.Context(None, 123)
2223
args = conn.get_boot_command()
2324

24-
# Executing the boot command will print "EC0" and expect to read from
25-
# stdin, which will fail because it's pointing at /dev/null, causing
26-
# the forked child to crash with an EOFError and disconnect its write
27-
# pipe. The forked and freshly execed parent will get a 0-byte read
28-
# from the pipe, which is a valid script, and therefore exit indicating
29-
# success.
25+
# The boot command should write an ECO marker to stdout, read the
26+
# preamble from stdin, then execute it.
3027

31-
fp = open("/dev/null", "r")
28+
# This test attaches /dev/zero to stdin to create a specific failure
29+
# 1. Fork child reads PREAMBLE_COMPRESSED_LEN bytes of junk (all `\0`)
30+
# 2. Fork child crashes (trying to decompress the junk data)
31+
# 3. Fork child's file descriptors (write pipes) are closed by the OS
32+
# 4. Fork parent does `dup(<read pipe>, <stdin>)` and `exec(<python>)`
33+
# 5. Python reads `b''` (i.e. EOF) from stdin (a closed pipe)
34+
# 6. Python runs `''` (a valid script) and exits with success
35+
36+
fp = open("/dev/zero", "r")
3237
try:
3338
proc = subprocess.Popen(args,
3439
stdin=fp,
@@ -39,6 +44,9 @@ def test_valid_syntax(self):
3944
self.assertEqual(0, proc.returncode)
4045
self.assertEqual(stdout,
4146
mitogen.parent.BootstrapProtocol.EC0_MARKER+b('\n'))
42-
self.assertIn(b("Error -5 while decompressing data"), stderr)
47+
self.assertIn(
48+
b("Error -3 while decompressing data"), # Unknown compression method
49+
stderr,
50+
)
4351
finally:
4452
fp.close()

0 commit comments

Comments
 (0)