From 5ae6f921772a419f06547e4a7cd58f0b3ad355ae Mon Sep 17 00:00:00 2001 From: David Wilson Date: Thu, 8 Aug 2019 18:17:42 +0000 Subject: [PATCH 01/16] issue #612: update Changelog. --- docs/changelog.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 707099a8..0622762b 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -167,6 +167,9 @@ Core Library * `#606 `_: fix example code on the documentation front page. +* `#612 `_: fix various errors + introduced by stream refactoring. + * `a5536c35 `_: avoid quadratic buffer management when logging lines received from a child's redirected standard IO. @@ -195,6 +198,7 @@ bug reports, testing, features and fixes in this release contributed by `Dave Cottlehuber `_, `El Mehdi CHAOUKI `_, `James Hogarth `_, +`Marc Hartmayer `_, `Nigel Metheringham `_, `Orion Poplawski `_, `Pieter Voet `_, From 436a4b3b3cfc6f901c66fe79b4f0828149a4312e Mon Sep 17 00:00:00 2001 From: David Wilson Date: Thu, 8 Aug 2019 20:08:51 +0000 Subject: [PATCH 02/16] docs: tidy up Select.all() --- mitogen/select.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mitogen/select.py b/mitogen/select.py index f880fcc3..3875042b 100644 --- a/mitogen/select.py +++ b/mitogen/select.py @@ -122,9 +122,10 @@ class Select(object): @classmethod def all(cls, receivers): """ - Take an iterable of receivers and retrieve a :class:`Message` from - each, returning the result of calling `msg.unpickle()` on each in turn. - Results are returned in the order they arrived. + Take an iterable of receivers and retrieve a :class:`Message + ` from each, returning the result of calling + :meth:`Message.unpickle() ` on each in + turn. Results are returned in the order they arrived. This is sugar for handling batch :meth:`Context.call_async ` invocations: From 5924af1566763e48c42028399ea0cd95c457b3dc Mon Sep 17 00:00:00 2001 From: David Wilson Date: Thu, 8 Aug 2019 23:03:43 +0100 Subject: [PATCH 03/16] [security] core: undirectional routing wasn't respected in some cases When creating a context using Router.method(via=somechild), unidirectional mode was set on the new child correctly, however if the child were to call Router.method(), due to a typing mistake the new child would start without it. This doesn't impact the Ansible extension, as only forked tasks are started directly by children, and they are not responsible for routing messages. Add test so it can't happen again. --- mitogen/core.py | 2 +- tests/router_test.py | 48 ++++++++++++++++++++++++++++++++------------ 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/mitogen/core.py b/mitogen/core.py index 8428a479..c228079d 100644 --- a/mitogen/core.py +++ b/mitogen/core.py @@ -3623,7 +3623,7 @@ class ExternalContext(object): self.broker = Broker(activate_compat=False) self.router = Router(self.broker) self.router.debug = self.config.get('debug', False) - self.router.undirectional = self.config['unidirectional'] + self.router.unidirectional = self.config['unidirectional'] self.router.add_handler( fn=self._on_shutdown_msg, handle=SHUTDOWN, diff --git a/tests/router_test.py b/tests/router_test.py index 1bd6c26a..2b4a9d78 100644 --- a/tests/router_test.py +++ b/tests/router_test.py @@ -1,9 +1,11 @@ +import sys import time import zlib import unittest2 import testlib +import mitogen.core import mitogen.master import mitogen.parent import mitogen.utils @@ -341,22 +343,42 @@ class NoRouteTest(testlib.RouterMixin, testlib.TestCase): )) +def test_siblings_cant_talk(router): + l1 = router.local() + l2 = router.local() + logs = testlib.LogCapturer() + logs.start() + + try: + l2.call(ping_context, l1) + except mitogen.core.CallError: + e = sys.exc_info()[1] + + msg = mitogen.core.Router.unidirectional_msg % ( + l2.context_id, + l1.context_id, + ) + assert msg in str(e) + assert 'routing mode prevents forward of ' in logs.stop() + + +@mitogen.core.takes_econtext +def test_siblings_cant_talk_remote(econtext): + mitogen.parent.upgrade_router(econtext) + test_siblings_cant_talk(econtext.router) + + class UnidirectionalTest(testlib.RouterMixin, testlib.TestCase): - def test_siblings_cant_talk(self): + def test_siblings_cant_talk_master(self): self.router.unidirectional = True - l1 = self.router.local() - l2 = self.router.local() - logs = testlib.LogCapturer() - logs.start() - e = self.assertRaises(mitogen.core.CallError, - lambda: l2.call(ping_context, l1)) + test_siblings_cant_talk(self.router) - msg = self.router.unidirectional_msg % ( - l2.context_id, - l1.context_id, - ) - self.assertTrue(msg in str(e)) - self.assertTrue('routing mode prevents forward of ' in logs.stop()) + def test_siblings_cant_talk_parent(self): + # ensure 'unidirectional' attribute is respected for contexts started + # by children. + self.router.unidirectional = True + parent = self.router.local() + parent.call(test_siblings_cant_talk_remote) def test_auth_id_can_talk(self): self.router.unidirectional = True From f0138072f10f89694b54f2833f7e5450d41393b3 Mon Sep 17 00:00:00 2001 From: David Wilson Date: Thu, 8 Aug 2019 23:09:41 +0100 Subject: [PATCH 04/16] docs: update Changelog. --- docs/changelog.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 0622762b..8f63aa74 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -186,6 +186,16 @@ Core Library buffered items, causing future :meth:`get` calls to block or fail even though data existed that could be returned. +* `5924af15 `_: *[security]* the + unidirectional routing mode, in which contexts may only communicate with + their parents and never siblings (such that a program cannot be used as a + bridge for air-gapped networks) was not inherited when a new child context + was initiated directly from an existing child. + + The bug did not effect the Ansible extension since the top-level controller + process initiates any new context that could be used for routing. Only + forked tasks are started directly from children. + Thanks! ~~~~~~~ From 6fa69955c46d70bfb3c97ec8a3aa346763da7028 Mon Sep 17 00:00:00 2001 From: David Wilson Date: Fri, 9 Aug 2019 04:42:23 +0100 Subject: [PATCH 05/16] issue #586: update Changelog. --- docs/changelog.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 8f63aa74..08d581e1 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -164,6 +164,10 @@ Core Library `closed` flag, preventing historical bugs where a double close could destroy descriptors belonging to unrelated streams. +* `#586 `_: fix import of the + `__main__` module on later versions of Python 3 when running from the + interactive console. + * `#606 `_: fix example code on the documentation front page. @@ -206,6 +210,7 @@ bug reports, testing, features and fixes in this release contributed by `Anton Markelov `_, `Dan `_, `Dave Cottlehuber `_, +`Denis Krienbühl `_, `El Mehdi CHAOUKI `_, `James Hogarth `_, `Marc Hartmayer `_, From e352b9e5fdeb67de0449060d1b5afad1f9cb4f19 Mon Sep 17 00:00:00 2001 From: David Wilson Date: Fri, 9 Aug 2019 04:48:24 +0100 Subject: [PATCH 06/16] docs: update Changelog. --- docs/changelog.rst | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 08d581e1..26b2c316 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -164,8 +164,8 @@ Core Library `closed` flag, preventing historical bugs where a double close could destroy descriptors belonging to unrelated streams. -* `#586 `_: fix import of the - `__main__` module on later versions of Python 3 when running from the +* `#586 `_: fix import of + :mod:`__main__` on later versions of Python 3 when running from the interactive console. * `#606 `_: fix example code on the @@ -192,13 +192,11 @@ Core Library * `5924af15 `_: *[security]* the unidirectional routing mode, in which contexts may only communicate with - their parents and never siblings (such that a program cannot be used as a - bridge for air-gapped networks) was not inherited when a new child context - was initiated directly from an existing child. - - The bug did not effect the Ansible extension since the top-level controller - process initiates any new context that could be used for routing. Only - forked tasks are started directly from children. + parents and never siblings (so a program cannot accidentally bridge + air-gapped networks) was not inherited when a child context was initiated + directly from an existing child. This did not effect the Ansible extension, + since the controller initiates any new context used for routing, only forked + tasks are initiated by children. Thanks! From 83a86a2ce15e1737f508db746b20d6349de8cad0 Mon Sep 17 00:00:00 2001 From: David Wilson Date: Fri, 9 Aug 2019 14:58:03 +0100 Subject: [PATCH 07/16] issue #482: tests: fail DockerMixin tests if stray processes exist --- tests/testlib.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/testlib.py b/tests/testlib.py index 3d641892..672d677b 100644 --- a/tests/testlib.py +++ b/tests/testlib.py @@ -454,6 +454,22 @@ class DockerizedSshDaemon(object): def wait_for_sshd(self): wait_for_port(self.get_host(), self.port, pattern='OpenSSH') + def check_processes(self): + args = ['docker', 'exec', self.container_name, 'ps', '-o', 'comm='] + counts = {} + for comm in subprocess__check_output(args).splitlines(): + comm = comm.strip() + counts[comm] = counts.get(comm, 0) + 1 + + if counts != {'ps': 1, 'sshd': 1}: + assert 0, ( + 'Docker container %r contained extra running processes ' + 'after test completed: %r' % ( + self.container_name, + counts + ) + ) + def close(self): args = ['docker', 'rm', '-f', self.container_name] subprocess__check_output(args) @@ -501,6 +517,7 @@ class DockerMixin(RouterMixin): @classmethod def tearDownClass(cls): + cls.dockerized_ssh.check_processes() cls.dockerized_ssh.close() super(DockerMixin, cls).tearDownClass() From 2ee0e070374982dc13b0bd3a759ec35a102e629d Mon Sep 17 00:00:00 2001 From: David Wilson Date: Fri, 9 Aug 2019 17:10:04 +0100 Subject: [PATCH 08/16] core: MitogenProtocol.is_privileged was not set in children Follow the previous unidirectional routing fix, now errors are occurring where they should not. --- mitogen/core.py | 35 ++++++++++++++++++++++++++--------- mitogen/unix.py | 3 +-- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/mitogen/core.py b/mitogen/core.py index c228079d..a14286f9 100644 --- a/mitogen/core.py +++ b/mitogen/core.py @@ -1937,18 +1937,28 @@ class MitogenProtocol(Protocol): :class:`Protocol` implementing mitogen's :ref:`stream protocol `. """ - #: If not :data:`None`, :class:`Router` stamps this into - #: :attr:`Message.auth_id` of every message received on this stream. - auth_id = None - #: If not :data:`False`, indicates the stream has :attr:`auth_id` set and #: its value is the same as :data:`mitogen.context_id` or appears in #: :data:`mitogen.parent_ids`. is_privileged = False - def __init__(self, router, remote_id): + def __init__(self, router, remote_id, auth_id=None, + local_id=None, parent_ids=None): self._router = router self.remote_id = remote_id + #: If not :data:`None`, :class:`Router` stamps this into + #: :attr:`Message.auth_id` of every message received on this stream. + self.auth_id = auth_id + + if parent_ids is None: + parent_ids = mitogen.parent_ids + if local_id is None: + local_id = mitogen.context_id + + self.is_privileged = ( + (remote_id in parent_ids) or + auth_id in ([local_id] + parent_ids) + ) self.sent_modules = set(['mitogen', 'mitogen.core']) self._input_buf = collections.deque() self._input_buf_len = 0 @@ -2800,8 +2810,8 @@ class Router(object): broker_exit_msg = 'Broker has exitted' no_route_msg = 'no route to %r, my ID is %r' unidirectional_msg = ( - 'routing mode prevents forward of message from context %d via ' - 'context %d' + 'routing mode prevents forward of message from context %d to ' + 'context %d via context %d' ) def __init__(self, broker): @@ -3152,7 +3162,9 @@ class Router(object): (in_stream.protocol.is_privileged or out_stream.protocol.is_privileged): self._maybe_send_dead(msg, self.unidirectional_msg, - in_stream.protocol.remote_id, out_stream.protocol.remote_id) + in_stream.protocol.remote_id, + out_stream.protocol.remote_id, + mitogen.context_id) return out_stream.protocol._send(msg) @@ -3641,7 +3653,12 @@ class ExternalContext(object): os.close(in_fd) out_fp = os.fdopen(os.dup(self.config.get('out_fd', 1)), 'wb', 0) - self.stream = MitogenProtocol.build_stream(self.router, parent_id) + self.stream = MitogenProtocol.build_stream( + self.router, + parent_id, + local_id=self.config['context_id'], + parent_ids=self.config['parent_ids'] + ) self.stream.accept(in_fp, out_fp) self.stream.name = 'parent' self.stream.receive_side.keep_alive = False diff --git a/mitogen/unix.py b/mitogen/unix.py index 645b061d..1af1c0ec 100644 --- a/mitogen/unix.py +++ b/mitogen/unix.py @@ -162,10 +162,9 @@ class Listener(mitogen.core.Protocol): stream = mitogen.core.MitogenProtocol.build_stream( router=self._router, remote_id=context_id, + auth_id=mitogen.context_id, ) stream.name = u'unix_client.%d' % (pid,) - stream.protocol.auth_id = mitogen.context_id - stream.protocol.is_privileged = True stream.accept(sock, sock) LOG.debug('listener: accepted connection from PID %d: %s', pid, stream.name) From 1e3621a88bc38e8256a02ef1425e5889815d47c3 Mon Sep 17 00:00:00 2001 From: David Wilson Date: Fri, 9 Aug 2019 19:26:52 +0100 Subject: [PATCH 09/16] tests: fix format string error --- tests/router_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/router_test.py b/tests/router_test.py index 2b4a9d78..e42a065a 100644 --- a/tests/router_test.py +++ b/tests/router_test.py @@ -357,6 +357,7 @@ def test_siblings_cant_talk(router): msg = mitogen.core.Router.unidirectional_msg % ( l2.context_id, l1.context_id, + mitogen.context_id, ) assert msg in str(e) assert 'routing mode prevents forward of ' in logs.stop() From 7ca073cdf8b65c690db428ecb51c667fd84bb4a7 Mon Sep 17 00:00:00 2001 From: David Wilson Date: Fri, 9 Aug 2019 20:19:16 +0100 Subject: [PATCH 10/16] issue #482: ci: add stray process checks to all jobs List of interesting processes can probably expand more over time. --- .ci/ansible_tests.py | 9 +++++ .ci/ci_lib.py | 73 ++++++++++++++++++++++++++++++++++++++ .ci/debops_common_tests.py | 6 ++++ .ci/mitogen_install.py | 1 + .ci/mitogen_tests.py | 2 ++ 5 files changed, 91 insertions(+) diff --git a/.ci/ansible_tests.py b/.ci/ansible_tests.py index 51eab874..4df2dc70 100755 --- a/.ci/ansible_tests.py +++ b/.ci/ansible_tests.py @@ -20,11 +20,17 @@ def pause_if_interactive(): signal.pause() +interesting = ci_lib.get_interesting_procs() + + with ci_lib.Fold('unit_tests'): os.environ['SKIP_MITOGEN'] = '1' ci_lib.run('./run_tests -v') +ci_lib.check_stray_processes(interesting) + + with ci_lib.Fold('docker_setup'): containers = ci_lib.make_containers() ci_lib.start_containers(containers) @@ -75,4 +81,7 @@ with ci_lib.Fold('ansible'): pause_if_interactive() raise + +ci_lib.check_stray_processes(interesting, containers) + pause_if_interactive() diff --git a/.ci/ci_lib.py b/.ci/ci_lib.py index 34a6faba..9225723e 100644 --- a/.ci/ci_lib.py +++ b/.ci/ci_lib.py @@ -215,6 +215,44 @@ def make_containers(name_prefix='', port_offset=0): return lst +INTERESTING_COMMS = ('python', 'ssh', 'sudo', 'su', 'doas') + + +def proc_is_docker(pid): + try: + fp = open('/proc/%s/cgroup' % (pid,), 'rb') + except IOError: + return False + + try: + return 'docker' in fp.read() + finally: + fp.close() + + +def get_interesting_procs(container_name=None): + args = ['ps', '-a', '-x', '-oppid=', '-opid=', '-ocomm=', '-ocommand='] + if container_name is not None: + args = ['docker', 'exec', container_name] + args + + out = [] + for line in subprocess__check_output(args).splitlines(): + ppid, pid, comm, rest = line.split(None, 3) + if ( + ( + any(comm.startswith(s) for s in INTERESTING_COMMS) or + 'mitogen:' in rest + ) and + ( + container_name is not None or + (not proc_is_docker(pid)) + ) + ): + out.append((int(pid), line)) + + return sorted(out) + + def start_containers(containers): if os.environ.get('KEEP'): return @@ -236,9 +274,44 @@ def start_containers(containers): ] for container in containers ]) + + for container in containers: + container['interesting'] = get_interesting_procs(container['name']) + return containers +def verify_procs(hostname, old, new): + oldpids = set(pid for pid, _ in old) + if any(pid not in oldpids for pid, _ in new): + print('%r had stray processes running:' % (hostname,)) + for pid, line in new: + if pid not in oldpids: + print('New process:', line) + + print() + return False + + return True + + +def check_stray_processes(old, containers=None): + ok = True + + new = get_interesting_procs() + if old is not None: + ok &= verify_procs('test host machine', old, new) + + for container in containers or (): + ok &= verify_procs( + container['name'], + container['interesting'], + get_interesting_procs(container['name']) + ) + + assert ok, 'stray processes were found' + + def dump_file(path): print() print('--- %s ---' % (path,)) diff --git a/.ci/debops_common_tests.py b/.ci/debops_common_tests.py index 8b35de1e..e8f2907b 100755 --- a/.ci/debops_common_tests.py +++ b/.ci/debops_common_tests.py @@ -68,9 +68,15 @@ with ci_lib.Fold('job_setup'): os.environ['ANSIBLE_HOST_KEY_CHECKING'] = 'False' +interesting = ci_lib.get_interesting_procs() + with ci_lib.Fold('first_run'): ci_lib.run('debops common %s', ' '.join(sys.argv[1:])) +ci_lib.check_stray_processes(interesting, containers) + with ci_lib.Fold('second_run'): ci_lib.run('debops common %s', ' '.join(sys.argv[1:])) + +ci_lib.check_stray_processes(interesting, containers) diff --git a/.ci/mitogen_install.py b/.ci/mitogen_install.py index 72bc75e3..b8862f89 100755 --- a/.ci/mitogen_install.py +++ b/.ci/mitogen_install.py @@ -14,4 +14,5 @@ if ci_lib.have_docker(): 'docker pull %s' % (ci_lib.image_for_distro(ci_lib.DISTRO),), ]) + ci_lib.run_batches(batches) diff --git a/.ci/mitogen_tests.py b/.ci/mitogen_tests.py index 36928ac9..4de94b4c 100755 --- a/.ci/mitogen_tests.py +++ b/.ci/mitogen_tests.py @@ -14,4 +14,6 @@ os.environ.update({ if not ci_lib.have_docker(): os.environ['SKIP_DOCKER_TESTS'] = '1' +interesting = ci_lib.get_interesting_procs() ci_lib.run('./run_tests -v') +ci_lib.check_stray_processes(interesting) From cf23d0dee6f40e5a19b633fd8c4e6744f96a7f72 Mon Sep 17 00:00:00 2001 From: David Wilson Date: Fri, 9 Aug 2019 20:33:36 +0100 Subject: [PATCH 11/16] issue #279: add one more test for max_message_size --- tests/router_test.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/router_test.py b/tests/router_test.py index e42a065a..dba56c9b 100644 --- a/tests/router_test.py +++ b/tests/router_test.py @@ -262,6 +262,13 @@ class MessageSizeTest(testlib.BrokerMixin, testlib.TestCase): size = remote.call(return_router_max_message_size) self.assertEquals(size, 64*1024) + def test_remote_of_remote_configured(self): + router = self.klass(broker=self.broker, max_message_size=64*1024) + remote = router.local() + remote2 = router.local(via=remote) + size = remote2.call(return_router_max_message_size) + self.assertEquals(size, 64*1024) + def test_remote_exceeded(self): # Ensure new contexts receive a router with the same value. router = self.klass(broker=self.broker, max_message_size=64*1024) From faec0158d9ce9b2955867116b02984a5da301c81 Mon Sep 17 00:00:00 2001 From: David Wilson Date: Fri, 9 Aug 2019 21:21:00 +0100 Subject: [PATCH 12/16] ci: Py3 fix --- .ci/ci_lib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/ci_lib.py b/.ci/ci_lib.py index 9225723e..251c826b 100644 --- a/.ci/ci_lib.py +++ b/.ci/ci_lib.py @@ -220,7 +220,7 @@ INTERESTING_COMMS = ('python', 'ssh', 'sudo', 'su', 'doas') def proc_is_docker(pid): try: - fp = open('/proc/%s/cgroup' % (pid,), 'rb') + fp = open('/proc/%s/cgroup' % (pid,), 'r') except IOError: return False @@ -236,7 +236,7 @@ def get_interesting_procs(container_name=None): args = ['docker', 'exec', container_name] + args out = [] - for line in subprocess__check_output(args).splitlines(): + for line in subprocess__check_output(args).decode().splitlines(): ppid, pid, comm, rest = line.split(None, 3) if ( ( From f2e35be143b1ad97a42bae6de8c446b817d20f4e Mon Sep 17 00:00:00 2001 From: David Wilson Date: Fri, 9 Aug 2019 21:58:54 +0100 Subject: [PATCH 13/16] issue #482: remove 'ssh' from checked processes Can't be used due to regular Ansible behaviour --- .ci/ci_lib.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.ci/ci_lib.py b/.ci/ci_lib.py index 251c826b..971ae5d8 100644 --- a/.ci/ci_lib.py +++ b/.ci/ci_lib.py @@ -215,7 +215,9 @@ def make_containers(name_prefix='', port_offset=0): return lst -INTERESTING_COMMS = ('python', 'ssh', 'sudo', 'su', 'doas') +# ssh removed from here because 'linear' strategy relies on processes that hang +# around after the Ansible run completes +INTERESTING_COMMS = ('python', 'sudo', 'su', 'doas') def proc_is_docker(pid): From 30ae3d85cbffe166e8779b57eab75ec549a6324b Mon Sep 17 00:00:00 2001 From: David Wilson Date: Fri, 9 Aug 2019 22:17:31 +0100 Subject: [PATCH 14/16] compat: fix Py2.4 SyntaxError --- mitogen/compat/pkgutil.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mitogen/compat/pkgutil.py b/mitogen/compat/pkgutil.py index df3983a2..15eb2afa 100644 --- a/mitogen/compat/pkgutil.py +++ b/mitogen/compat/pkgutil.py @@ -542,7 +542,8 @@ def extend_path(path, name): if os.path.isfile(pkgfile): try: f = open(pkgfile) - except IOError as msg: + except IOError: + msg = sys.exc_info()[1] sys.stderr.write("Can't open %s: %s\n" % (pkgfile, msg)) else: From 1cad04185b943afe01c81d96f6333f3b3827c3e0 Mon Sep 17 00:00:00 2001 From: David Wilson Date: Fri, 9 Aug 2019 22:21:11 +0100 Subject: [PATCH 15/16] ci: try removing exclude: to make Azure jobs work again --- .ci/azure-pipelines.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index 6d0832da..920e82a1 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -3,11 +3,6 @@ # Add steps that analyze code, save the dist with the build record, publish to a PyPI-compatible index, and more: # https://docs.microsoft.com/azure/devops/pipelines/languages/python -trigger: - branches: - exclude: - - docs-master - jobs: - job: Mac From 8bac1cf368fb39a0ddace504e952af90a37cd16a Mon Sep 17 00:00:00 2001 From: David Wilson Date: Fri, 9 Aug 2019 22:45:00 +0100 Subject: [PATCH 16/16] issue #482: another Py3 fix --- tests/testlib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testlib.py b/tests/testlib.py index 672d677b..255fba88 100644 --- a/tests/testlib.py +++ b/tests/testlib.py @@ -457,7 +457,7 @@ class DockerizedSshDaemon(object): def check_processes(self): args = ['docker', 'exec', self.container_name, 'ps', '-o', 'comm='] counts = {} - for comm in subprocess__check_output(args).splitlines(): + for comm in subprocess__check_output(args).decode().splitlines(): comm = comm.strip() counts[comm] = counts.get(comm, 0) + 1