Skip to content

Commit de12097

Browse files
committed
Merge remote-tracking branch 'origin/ci280'
* origin/ci280: ci: Another round of fixes for random Ansible UI breakage in 2.7/2.8 ci: work around various broken aspects of Travis VM image Use virtualenv Python for stub connections to workaround problem ci: Ansible 2.8 requires Python 2.7. tests: add 2.8 format async error timeout message ansible: prevent tempfile.mkstemp() leaks. update gitignore again ci: try bumping more Travis jobs to Ansible 2.8. add .*.pid to gitignore tests: allow running without hdrhistograms library. issue #578: update Changelog. travis: exclude docs-master from CI issue #589: ensure real FileService/PushFileService are in the docs issue #589: ensure real FileService/PushFileService are in the docs docs: add new contributor entry issue #589: remove outdated/incomplete examples issue #589: split services example out and make it run.
2 parents 3616385 + ee7dae7 commit de12097

25 files changed

+170
-114
lines changed

.ci/ansible_tests.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ def pause_if_interactive():
6363
run("sudo apt-get update")
6464
run("sudo apt-get install -y sshpass")
6565

66+
run("bash -c 'sudo ln -vfs /usr/lib/python2.7/plat-x86_64-linux-gnu/_sysconfigdata_nd.py /usr/lib/python2.7 || true'")
67+
run("bash -c 'sudo ln -vfs /usr/lib/python2.7/plat-x86_64-linux-gnu/_sysconfigdata_nd.py $VIRTUAL_ENV/lib/python2.7 || true'")
6668

6769
with ci_lib.Fold('ansible'):
6870
playbook = os.environ.get('PLAYBOOK', 'all.yml')

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ htmlcov/
1414
*.egg-info
1515
__pycache__/
1616
extra
17+
**/.*.pid

.travis.yml

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,17 @@ notifications:
66

77
language: python
88

9+
branches:
10+
except:
11+
- docs-master
12+
913
cache:
1014
- pip
1115
- directories:
1216
- /home/travis/virtualenv
1317

1418
install:
19+
- grep -Erl git-lfs\|couchdb /etc/apt | sudo xargs rm -v
1520
- .ci/${MODE}_install.py
1621

1722
script:
@@ -43,9 +48,9 @@ matrix:
4348
# 2.4.6.0; 2.7 -> 2.7
4449
- python: "2.7"
4550
env: MODE=debops_common VER=2.4.6.0
46-
# 2.5.7; 3.6 -> 2.7
51+
# 2.8.0; 3.6 -> 2.7
4752
- python: "3.6"
48-
env: MODE=debops_common VER=2.6.2
53+
env: MODE=debops_common VER=2.8.0
4954

5055
# ansible_mitogen tests.
5156

@@ -56,15 +61,16 @@ matrix:
5661
# 2.6 -> {debian, centos6, centos7}
5762
- python: "2.6"
5863
env: MODE=ansible VER=2.4.6.0
59-
- python: "2.6"
60-
env: MODE=ansible VER=2.6.2
64+
# 2.7 -> {debian, centos6, centos7}
65+
- python: "2.7"
66+
env: MODE=ansible VER=2.8.0
6167

6268
# 3.6 -> {debian, centos6, centos7}
6369
- python: "3.6"
6470
env: MODE=ansible VER=2.4.6.0
6571
- python: "3.6"
66-
env: MODE=ansible VER=2.6.2
72+
env: MODE=ansible VER=2.8.0
6773

6874
# Sanity check against vanilla Ansible. One job suffices.
6975
- python: "2.7"
70-
env: MODE=ansible VER=2.6.2 DISTROS=debian STRATEGY=linear
76+
env: MODE=ansible VER=2.8.0 DISTROS=debian STRATEGY=linear

ansible_mitogen/runner.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,45 @@ def shlex_split(s, comments=False):
112112
for token in shlex.split(str(s), comments=comments)]
113113

114114

115+
class TempFileWatcher(object):
116+
"""
117+
Since Ansible 2.7.0, lineinfile leaks file descriptors returned by
118+
:func:`tempfile.mkstemp` (ansible/ansible#57327). Handle this and all
119+
similar cases by recording descriptors produced by mkstemp during module
120+
execution, and cleaning up any leaked descriptors on completion.
121+
"""
122+
def __init__(self):
123+
self._real_mkstemp = tempfile.mkstemp
124+
# (fd, st.st_dev, st.st_ino)
125+
self._fd_dev_inode = []
126+
tempfile.mkstemp = self._wrap_mkstemp
127+
128+
def _wrap_mkstemp(self, *args, **kwargs):
129+
fd, path = self._real_mkstemp(*args, **kwargs)
130+
st = os.fstat(fd)
131+
self._fd_dev_inode.append((fd, st.st_dev, st.st_ino))
132+
return fd, path
133+
134+
def revert(self):
135+
tempfile.mkstemp = self._real_mkstemp
136+
for tup in self._fd_dev_inode:
137+
self._revert_one(*tup)
138+
139+
def _revert_one(self, fd, st_dev, st_ino):
140+
try:
141+
st = os.fstat(fd)
142+
except OSError:
143+
# FD no longer exists.
144+
return
145+
146+
if not (st.st_dev == st_dev and st.st_ino == st_ino):
147+
# FD reused.
148+
return
149+
150+
LOG.info("a tempfile.mkstemp() FD was leaked during the last task")
151+
os.close(fd)
152+
153+
115154
class EnvironmentFileWatcher(object):
116155
"""
117156
Usually Ansible edits to /etc/environment and ~/.pam_environment are
@@ -803,6 +842,7 @@ def setup(self):
803842
# module, but this has never been a bug report. Instead act like an
804843
# interpreter that had its script piped on stdin.
805844
self._argv = TemporaryArgv([''])
845+
self._temp_watcher = TempFileWatcher()
806846
self._importer = ModuleUtilsImporter(
807847
context=self.service_context,
808848
module_utils=self.module_map['custom'],
@@ -818,6 +858,7 @@ def _revert_excepthook(self):
818858

819859
def revert(self):
820860
self.atexit_wrapper.revert()
861+
self._temp_watcher.revert()
821862
self._argv.revert()
822863
self._stdio.revert()
823864
self._revert_excepthook()

docs/changelog.rst

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ Enhancements
3434
Fixes
3535
^^^^^
3636

37+
* `#578 <https://github.com/dw/mitogen/issues/578>`_: the extension could crash
38+
while rendering an error message, due to an incorrect format string.
39+
3740
* `#590 <https://github.com/dw/mitogen/issues/590>`_: the importer can handle
3841
modules that replace themselves in :mod:`sys.modules` during import.
3942

@@ -59,8 +62,10 @@ bug reports, testing, features and fixes in this release contributed by
5962
`Anton Markelov <https://github.com/strangeman>`_,
6063
`Nigel Metheringham <https://github.com/nigelm>`_,
6164
`Orion Poplawski <https://github.com/opoplawski>`_,
62-
`Ulrich Schreiner <https://github.com/ulrichSchreiner>`_, and
63-
`Yuki Nishida <https://github.com/yuki-nishida-exa>`_.
65+
`Szabó Dániel Ernő <https://github.com/r3ap3rpy>`_,
66+
`Ulrich Schreiner <https://github.com/ulrichSchreiner>`_,
67+
`Yuki Nishida <https://github.com/yuki-nishida-exa>`_, and
68+
`@ghp-rr <https://github.com/ghp-rr>`_.
6469

6570

6671
v0.2.7 (2019-05-19)

docs/contributors.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ sponsorship and outstanding future-thinking of its early adopters.
8888
<h3>Private Sponsors</h3>
8989

9090
<ul style="line-height: 120% !important;">
91+
<li><a href="https://skunkwerks.at/">SkunkWerks</a> &mdash;
92+
<em>Mitogen on FreeBSD runs like a kid in a candy store: fast &amp;
93+
sweet.</em></li>
9194
<li>Donald Clark Jackson &mdash;
9295
<em>Mitogen is an exciting project, and I am happy to support its
9396
development.</em></li>

docs/services.rst

Lines changed: 10 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -61,55 +61,7 @@ Pool
6161
Example
6262
-------
6363

64-
.. code-block:: python
65-
66-
import mitogen
67-
import mitogen.service
68-
69-
70-
class FileService(mitogen.service.Service):
71-
"""
72-
Simple file server, for demonstration purposes only! Use of this in
73-
real code would be a security vulnerability as it would permit children
74-
to read arbitrary files from the master's disk.
75-
"""
76-
handle = 500
77-
required_args = {
78-
'path': str
79-
}
80-
81-
def dispatch(self, args, msg):
82-
with open(args['path'], 'r') as fp:
83-
return fp.read()
84-
85-
86-
def download_file(context, path):
87-
s = mitogen.service.call(context, FileService.handle, {
88-
'path': path
89-
})
90-
91-
with open(path, 'w') as fp:
92-
fp.write(s)
93-
94-
95-
@mitogen.core.takes_econtext
96-
def download_some_files(paths, econtext):
97-
for path in paths:
98-
download_file(econtext.master, path)
99-
100-
101-
@mitogen.main()
102-
def main(router):
103-
pool = mitogen.service.Pool(router, size=1, services=[
104-
FileService(router),
105-
])
106-
107-
remote = router.ssh(hostname='k3')
108-
remote.call(download_some_files, [
109-
'/etc/passwd',
110-
'/etc/hosts',
111-
])
112-
pool.stop()
64+
.. literalinclude:: ../examples/service/self_contained.py
11365

11466

11567
Reference
@@ -134,3 +86,12 @@ Reference
13486
.. autoclass:: mitogen.service.Pool
13587
:members:
13688

89+
90+
Built-in Services
91+
-----------------
92+
93+
.. autoclass:: mitogen.service.FileService
94+
:members:
95+
96+
.. autoclass:: mitogen.service.PushFileService
97+
:members:

examples/service/client.py

Lines changed: 0 additions & 15 deletions
This file was deleted.

examples/service/self_contained.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import mitogen
2+
import mitogen.service
3+
4+
5+
class FileService(mitogen.service.Service):
6+
"""
7+
Simple file server, for demonstration purposes only! Use of this in
8+
real code would be a security vulnerability as it would permit children
9+
to read any file from the master's disk.
10+
"""
11+
12+
@mitogen.service.expose(policy=mitogen.service.AllowAny())
13+
@mitogen.service.arg_spec(spec={
14+
'path': str
15+
})
16+
def read_file(self, path):
17+
with open(path, 'rb') as fp:
18+
return fp.read()
19+
20+
21+
def download_file(source_context, path):
22+
s = source_context.call_service(
23+
service_name=FileService, # may also be string 'pkg.mod.FileService'
24+
method_name='read_file',
25+
path=path,
26+
)
27+
28+
with open(path, 'w') as fp:
29+
fp.write(s)
30+
31+
32+
def download_some_files(source_context, paths):
33+
for path in paths:
34+
download_file(source_context, path)
35+
36+
37+
@mitogen.main()
38+
def main(router):
39+
pool = mitogen.service.Pool(router, services=[
40+
FileService(router),
41+
])
42+
43+
remote = router.ssh(hostname='k3')
44+
remote.call(download_some_files,
45+
source_context=router.myself(),
46+
paths=[
47+
'/etc/passwd',
48+
'/etc/hosts',
49+
]
50+
)
51+
pool.stop()
52+

examples/service/server.py

Lines changed: 0 additions & 20 deletions
This file was deleted.

0 commit comments

Comments
 (0)