Merge pull request #932 from moreati/release-0.3.3

Release 0.3.3
pull/1003/head v0.3.3
Alex Willmer 2 years ago committed by GitHub
commit 660d3e0885
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -7,7 +7,6 @@ import signal
import sys
import ci_lib
from ci_lib import run
TESTS_DIR = os.path.join(ci_lib.GIT_ROOT, 'tests/ansible')
@ -40,10 +39,10 @@ with ci_lib.Fold('job_setup'):
os.chdir(TESTS_DIR)
os.chmod('../data/docker/mitogen__has_sudo_pubkey.key', int('0600', 7))
run("mkdir %s", HOSTS_DIR)
ci_lib.run("mkdir %s", HOSTS_DIR)
for path in glob.glob(TESTS_DIR + '/hosts/*'):
if not path.endswith('default.hosts'):
run("ln -s %s %s", path, HOSTS_DIR)
ci_lib.run("ln -s %s %s", path, HOSTS_DIR)
inventory_path = os.path.join(HOSTS_DIR, 'target')
with open(inventory_path, 'w') as fp:
@ -63,14 +62,14 @@ with ci_lib.Fold('job_setup'):
ci_lib.dump_file(inventory_path)
if not ci_lib.exists_in_path('sshpass'):
run("sudo apt-get update")
run("sudo apt-get install -y sshpass")
ci_lib.run("sudo apt-get update")
ci_lib.run("sudo apt-get install -y sshpass")
with ci_lib.Fold('ansible'):
playbook = os.environ.get('PLAYBOOK', 'all.yml')
try:
run('./run_ansible_playbook.py %s -i "%s" %s',
ci_lib.run('./run_ansible_playbook.py %s -i "%s" %s',
playbook, HOSTS_DIR, ' '.join(sys.argv[1:]))
except:
pause_if_interactive()

@ -25,9 +25,9 @@ jobs:
Mito_36:
python.version: '3.6'
tox.env: py36-mode_mitogen
Mito_39:
python.version: '3.9'
tox.env: py39-mode_mitogen
Mito_310:
python.version: '3.10'
tox.env: py310-mode_mitogen
# TODO: test python3, python3 tests are broken
Loc_27_210:
@ -72,9 +72,9 @@ jobs:
Mito_37:
python.version: '3.7'
tox.env: py37-mode_mitogen
Mito_39:
python.version: '3.9'
tox.env: py39-mode_mitogen
Mito_310:
python.version: '3.10'
tox.env: py310-mode_mitogen
# TODO: test python3, python3 tests are broken
Loc_27_210:
@ -162,33 +162,33 @@ jobs:
python.version: '3.6'
tox.env: py36-mode_mitogen-distro_ubuntu2004
Mito_39_centos6:
python.version: '3.9'
tox.env: py39-mode_mitogen-distro_centos6
Mito_39_centos7:
python.version: '3.9'
tox.env: py39-mode_mitogen-distro_centos7
Mito_39_centos8:
python.version: '3.9'
tox.env: py39-mode_mitogen-distro_centos8
Mito_39_debian9:
python.version: '3.9'
tox.env: py39-mode_mitogen-distro_debian9
Mito_39_debian10:
python.version: '3.9'
tox.env: py39-mode_mitogen-distro_debian10
Mito_39_debian11:
python.version: '3.9'
tox.env: py39-mode_mitogen-distro_debian11
Mito_39_ubuntu1604:
python.version: '3.9'
tox.env: py39-mode_mitogen-distro_ubuntu1604
Mito_39_ubuntu1804:
python.version: '3.9'
tox.env: py39-mode_mitogen-distro_ubuntu1804
Mito_39_ubuntu2004:
python.version: '3.9'
tox.env: py39-mode_mitogen-distro_ubuntu2004
Mito_310_centos6:
python.version: '3.10'
tox.env: py310-mode_mitogen-distro_centos6
Mito_310_centos7:
python.version: '3.10'
tox.env: py310-mode_mitogen-distro_centos7
Mito_310_centos8:
python.version: '3.10'
tox.env: py310-mode_mitogen-distro_centos8
Mito_310_debian9:
python.version: '3.10'
tox.env: py310-mode_mitogen-distro_debian9
Mito_310_debian10:
python.version: '3.10'
tox.env: py310-mode_mitogen-distro_debian10
Mito_310_debian11:
python.version: '3.10'
tox.env: py310-mode_mitogen-distro_debian11
Mito_310_ubuntu1604:
python.version: '3.10'
tox.env: py310-mode_mitogen-distro_ubuntu1604
Mito_310_ubuntu1804:
python.version: '3.10'
tox.env: py310-mode_mitogen-distro_ubuntu1804
Mito_310_ubuntu2004:
python.version: '3.10'
tox.env: py310-mode_mitogen-distro_ubuntu2004
#DebOps_2460_27_27:
#python.version: '2.7'
@ -247,15 +247,15 @@ jobs:
python.version: '3.6'
tox.env: py36-mode_ansible-ansible4
Ans_39_210:
python.version: '3.9'
tox.env: py39-mode_ansible-ansible2.10
Ans_39_3:
python.version: '3.9'
tox.env: py39-mode_ansible-ansible3
Ans_39_4:
python.version: '3.9'
tox.env: py39-mode_ansible-ansible4
Ans_39_5:
python.version: '3.9'
tox.env: py39-mode_ansible-ansible5
Ans_310_210:
python.version: '3.10'
tox.env: py310-mode_ansible-ansible2.10
Ans_310_3:
python.version: '3.10'
tox.env: py310-mode_ansible-ansible3
Ans_310_4:
python.version: '3.10'
tox.env: py310-mode_ansible-ansible4
Ans_310_5:
python.version: '3.10'
tox.env: py310-mode_ansible-ansible5

@ -1,4 +1,3 @@
from __future__ import absolute_import
from __future__ import print_function
@ -193,8 +192,6 @@ class Fold(object):
def __exit__(self, _1, _2, _3): pass
os.environ.setdefault('ANSIBLE_STRATEGY',
os.environ.get('STRATEGY', 'mitogen_linear'))
GIT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
# Used only when MODE=mitogen
DISTRO = os.environ.get('DISTRO', 'debian9')

@ -7,7 +7,7 @@ ci_lib.DISTROS = ['debian']
ci_lib.run_batches([
[
'pip install -qqq "debops[ansible]==2.1.2"',
'python -m pip --no-python-version-warning --disable-pip-version-check "debops[ansible]==2.1.2"',
],
[
'aws ecr-public get-login-password | docker login --username AWS --password-stdin public.ecr.aws',

@ -2,10 +2,10 @@
# Run tests/ansible/all.yml under Ansible and Ansible-Mitogen
import os
import subprocess
import sys
import ci_lib
from ci_lib import run
TESTS_DIR = os.path.join(ci_lib.GIT_ROOT, 'tests/ansible')
@ -24,37 +24,38 @@ with ci_lib.Fold('job_setup'):
# NOTE: sshpass v1.06 causes errors so pegging to 1.05 -> "msg": "Error when changing password","out": "passwd: DS error: eDSAuthFailed\n",
# there's a checksum error with "brew install http://git.io/sshpass.rb" though, so installing manually
if not ci_lib.exists_in_path('sshpass'):
os.system("curl -O -L https://sourceforge.net/projects/sshpass/files/sshpass/1.05/sshpass-1.05.tar.gz && \
subprocess.check_call(
"curl -O -L https://sourceforge.net/projects/sshpass/files/sshpass/1.05/sshpass-1.05.tar.gz && \
tar xvf sshpass-1.05.tar.gz && \
cd sshpass-1.05 && \
./configure && \
sudo make install")
sudo make install",
shell=True,
)
with ci_lib.Fold('machine_prep'):
# generate a new ssh key for localhost ssh
if not os.path.exists(os.path.expanduser("~/.ssh/id_rsa")):
os.system("ssh-keygen -P '' -m pem -f ~/.ssh/id_rsa")
os.system("cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys")
# also generate it for the sudo user
if os.system("sudo [ -f /var/root/.ssh/id_rsa ]") != 0:
os.system("sudo ssh-keygen -P '' -m pem -f /var/root/.ssh/id_rsa")
os.system("sudo cat /var/root/.ssh/id_rsa.pub | sudo tee -a /var/root/.ssh/authorized_keys")
subprocess.check_call("ssh-keygen -P '' -m pem -f ~/.ssh/id_rsa", shell=True)
subprocess.check_call("cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys", shell=True)
os.chmod(os.path.expanduser('~/.ssh'), int('0700', 8))
os.chmod(os.path.expanduser('~/.ssh/authorized_keys'), int('0600', 8))
# run chmod through sudo since it's owned by root
os.system('sudo chmod 700 /var/root/.ssh')
os.system('sudo chmod 600 /var/root/.ssh/authorized_keys')
# also generate it for the sudo user
if os.system("sudo [ -f ~root/.ssh/id_rsa ]") != 0:
subprocess.check_call("sudo ssh-keygen -P '' -m pem -f ~root/.ssh/id_rsa", shell=True)
subprocess.check_call("sudo cat ~root/.ssh/id_rsa.pub | sudo tee -a ~root/.ssh/authorized_keys", shell=True)
subprocess.check_call('sudo chmod 700 ~root/.ssh', shell=True)
subprocess.check_call('sudo chmod 600 ~root/.ssh/authorized_keys', shell=True)
if os.path.expanduser('~mitogen__user1') == '~mitogen__user1':
os.chdir(IMAGE_PREP_DIR)
run("ansible-playbook -c local -i localhost, _user_accounts.yml")
ci_lib.run("ansible-playbook -c local -i localhost, _user_accounts.yml")
with ci_lib.Fold('ansible'):
os.chdir(TESTS_DIR)
playbook = os.environ.get('PLAYBOOK', 'all.yml')
run('./run_ansible_playbook.py %s -l target %s',
ci_lib.run('./run_ansible_playbook.py %s -l target %s',
playbook, ' '.join(sys.argv[1:]))

@ -73,7 +73,9 @@ necessarily involves preventing the scheduler from making load balancing
decisions.
"""
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import ctypes
import logging
import mmap

@ -26,8 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
from __future__ import unicode_literals
__metaclass__ = type
import errno
import logging
@ -40,10 +41,8 @@ import time
import ansible.constants as C
import ansible.errors
import ansible.plugins.connection
import ansible.utils.shlex
import mitogen.core
import mitogen.fork
import mitogen.utils
import ansible_mitogen.mixins
@ -262,6 +261,21 @@ def _connect_machinectl(spec):
return _connect_setns(spec, kind='machinectl')
def _connect_podman(spec):
"""
Return ContextService arguments for a Docker connection.
"""
return {
'method': 'podman',
'kwargs': {
'username': spec.remote_user(),
'container': spec.remote_addr(),
'python_path': spec.python_path(rediscover_python=True),
'connect_timeout': spec.ansible_ssh_timeout() or spec.timeout(),
'remote_name': get_remote_name(spec),
}
}
def _connect_setns(spec, kind=None):
"""
Return ContextService arguments for a mitogen_setns connection.
@ -400,6 +414,7 @@ CONNECTION_METHOD = {
'lxc': _connect_lxc,
'lxd': _connect_lxd,
'machinectl': _connect_machinectl,
'podman': _connect_podman,
'setns': _connect_setns,
'ssh': _connect_ssh,
'smart': _connect_ssh, # issue #548.
@ -1081,7 +1096,7 @@ class Connection(ansible.plugins.connection.ConnectionBase):
s = fp.read(self.SMALL_FILE_LIMIT + 1)
finally:
fp.close()
except OSError:
except OSError as e:
self._throw_io_error(e, in_path)
raise

@ -30,7 +30,10 @@
Stable names for PluginLoader instances across Ansible versions.
"""
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import ansible.errors
import ansible_mitogen.utils

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import logging
import os
@ -36,8 +38,8 @@ import mitogen.utils
try:
from __main__ import display
except ImportError:
from ansible.utils.display import Display
display = Display()
import ansible.utils.display
display = ansible.utils.display.Display()
#: The process name set via :func:`set_process_name`.

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import logging
import os
import pwd

@ -26,8 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
from __future__ import unicode_literals
__metaclass__ = type
import collections
import imp

@ -26,8 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
from __future__ import unicode_literals
__metaclass__ = type
import mitogen.core

@ -34,8 +34,9 @@ files/modules known missing.
[0] "Ansible Module Architecture", developing_program_flow_modules.html
"""
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
from __future__ import unicode_literals
__metaclass__ = type
import json
import logging
@ -43,11 +44,10 @@ import os
import random
import re
from ansible.executor import module_common
from ansible.collections.list import list_collection_dirs
import ansible.collections.list
import ansible.errors
import ansible.module_utils
import ansible.release
import ansible.executor.module_common
import mitogen.core
import mitogen.select
@ -192,7 +192,7 @@ class BinaryPlanner(Planner):
@classmethod
def detect(cls, path, source):
return module_common._is_binary(source)
return ansible.executor.module_common._is_binary(source)
def get_push_files(self):
return [mitogen.core.to_text(self._inv.module_path)]
@ -269,7 +269,7 @@ class JsonArgsPlanner(ScriptPlanner):
@classmethod
def detect(cls, path, source):
return module_common.REPLACER_JSONARGS in source
return ansible.executor.module_common.REPLACER_JSONARGS in source
class WantJsonPlanner(ScriptPlanner):
@ -298,11 +298,11 @@ class NewStylePlanner(ScriptPlanner):
preprocessing the module.
"""
runner_name = 'NewStyleRunner'
MARKER = re.compile(b'from ansible(?:_collections|\.module_utils)\.')
MARKER = re.compile(br'from ansible(?:_collections|\.module_utils)\.')
@classmethod
def detect(cls, path, source):
return cls.MARKER.search(source) != None
return cls.MARKER.search(source) is not None
def _get_interpreter(self):
return None, None
@ -362,7 +362,7 @@ class NewStylePlanner(ScriptPlanner):
module_name='ansible_module_%s' % (self._inv.module_name,),
module_path=self._inv.module_path,
search_path=self.get_search_path(),
builtin_path=module_common._MODULE_UTILS_PATH,
builtin_path=ansible.executor.module_common._MODULE_UTILS_PATH,
context=self._inv.connection.context,
)
return self._module_map
@ -405,7 +405,7 @@ class ReplacerPlanner(NewStylePlanner):
@classmethod
def detect(cls, path, source):
return module_common.REPLACER in source
return ansible.executor.module_common.REPLACER in source
class OldStylePlanner(ScriptPlanner):
@ -427,12 +427,6 @@ _planners = [
]
try:
_get_ansible_module_fqn = module_common._get_ansible_module_fqn
except AttributeError:
_get_ansible_module_fqn = None
def py_modname_from_path(name, path):
"""
Fetch the logical name of a new-style module as it might appear in
@ -442,9 +436,10 @@ def py_modname_from_path(name, path):
package hierarchy approximated on the target, enabling relative imports
to function correctly. For example, "ansible.modules.system.setup".
"""
if _get_ansible_module_fqn:
try:
return _get_ansible_module_fqn(path)
return ansible.executor.module_common._get_ansible_module_fqn(path)
except AttributeError:
pass
except ValueError:
pass
@ -528,12 +523,15 @@ def _invoke_isolated_task(invocation, planner):
context.shutdown()
def _get_planner(name, path, source):
def _get_planner(invocation, source):
for klass in _planners:
if klass.detect(path, source):
LOG.debug('%r accepted %r (filename %r)', klass, name, path)
if klass.detect(invocation.module_path, source):
LOG.debug(
'%r accepted %r (filename %r)',
klass, invocation.module_name, invocation.module_path,
)
return klass
LOG.debug('%r rejected %r', klass, name)
LOG.debug('%r rejected %r', klass, invocation.module_name)
raise ansible.errors.AnsibleError(NO_METHOD_MSG + repr(invocation))
@ -564,7 +562,7 @@ def _load_collections(invocation):
Goes through all collection path possibilities and stores paths to installed collections
Stores them on the current invocation to later be passed to the master service
"""
for collection_path in list_collection_dirs():
for collection_path in ansible.collections.list.list_collection_dirs():
invocation._extra_sys_paths.add(collection_path.decode('utf-8'))
@ -596,8 +594,7 @@ def invoke(invocation):
module_source = invocation.get_module_source()
_fix_py35(invocation, module_source)
_planner_by_path[invocation.module_path] = _get_planner(
invocation.module_name,
invocation.module_path,
invocation,
module_source
)

@ -18,23 +18,17 @@ from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import os
from ansible.module_utils._text import to_bytes
import base64
from ansible.errors import AnsibleError, AnsibleActionFail, AnsibleActionSkip
from ansible.module_utils.common.text.converters import to_bytes, to_text
from ansible.module_utils.six import string_types
from ansible.module_utils.parsing.convert_bool import boolean
from ansible.plugins.action import ActionBase
from ansible.utils.hashing import checksum, md5, secure_hash
from ansible.utils.path import makedirs_safe
from ansible.utils.display import Display
from ansible.utils.hashing import checksum, checksum_s, md5, secure_hash
from ansible.utils.path import makedirs_safe, is_subpath
REMOTE_CHECKSUM_ERRORS = {
'0': "unable to calculate the checksum of the remote file",
'1': "the remote file does not exist",
'2': "no read permission on remote file",
'3': "remote file is a directory, fetch cannot work on directories",
'4': "python isn't present on the system. Unable to compute checksum",
'5': "stdlib json was not found on the remote machine. Only the raw module can work without those installed",
}
display = Display()
class ActionModule(ActionBase):
@ -45,36 +39,94 @@ class ActionModule(ActionBase):
task_vars = dict()
result = super(ActionModule, self).run(tmp, task_vars)
del tmp # tmp no longer has any effect
try:
if self._play_context.check_mode:
result['skipped'] = True
result['msg'] = 'check mode not (yet) supported for this module'
return result
raise AnsibleActionSkip('check mode not (yet) supported for this module')
source = self._task.args.get('src', None)
original_dest = dest = self._task.args.get('dest', None)
flat = boolean(self._task.args.get('flat'), strict=False)
fail_on_missing = boolean(self._task.args.get('fail_on_missing', True), strict=False)
validate_checksum = boolean(self._task.args.get('validate_checksum', True), strict=False)
msg = ''
# validate source and dest are strings FIXME: use basic.py and module specs
source = self._task.args.get('src')
if not isinstance(source, string_types):
result['msg'] = "Invalid type supplied for source option, it must be a string"
msg = "Invalid type supplied for source option, it must be a string"
dest = self._task.args.get('dest')
if not isinstance(dest, string_types):
result['msg'] = "Invalid type supplied for dest option, it must be a string"
msg = "Invalid type supplied for dest option, it must be a string"
if result.get('msg'):
result['failed'] = True
return result
if source is None or dest is None:
msg = "src and dest are required"
if msg:
raise AnsibleActionFail(msg)
source = self._connection._shell.join_path(source)
source = self._remote_expand_user(source)
# calculate checksum for the remote file, don't bother if using
# become as slurp will be used Force remote_checksum to follow
# symlinks because fetch always follows symlinks
remote_checksum = self._remote_checksum(source, all_vars=task_vars, follow=True)
remote_stat = {}
remote_checksum = None
if True:
# Get checksum for the remote file even using become. Mitogen doesn't need slurp.
# Follow symlinks because fetch always follows symlinks
try:
remote_stat = self._execute_remote_stat(source, all_vars=task_vars, follow=True)
except AnsibleError as ae:
result['changed'] = False
result['file'] = source
if fail_on_missing:
result['failed'] = True
result['msg'] = to_text(ae)
else:
result['msg'] = "%s, ignored" % to_text(ae, errors='surrogate_or_replace')
return result
remote_checksum = remote_stat.get('checksum')
if remote_stat.get('exists'):
if remote_stat.get('isdir'):
result['failed'] = True
result['changed'] = False
result['msg'] = "remote file is a directory, fetch cannot work on directories"
# Historically, these don't fail because you may want to transfer
# a log file that possibly MAY exist but keep going to fetch other
# log files. Today, this is better achieved by adding
# ignore_errors or failed_when to the task. Control the behaviour
# via fail_when_missing
if not fail_on_missing:
result['msg'] += ", not transferring, ignored"
del result['changed']
del result['failed']
return result
# use slurp if permissions are lacking or privilege escalation is needed
remote_data = None
if remote_checksum in (None, '1', ''):
slurpres = self._execute_module(module_name='ansible.legacy.slurp', module_args=dict(src=source), task_vars=task_vars)
if slurpres.get('failed'):
if not fail_on_missing:
result['file'] = source
result['changed'] = False
else:
result.update(slurpres)
if 'not found' in slurpres.get('msg', ''):
result['msg'] = "the remote file does not exist, not transferring, ignored"
elif slurpres.get('msg', '').startswith('source is a directory'):
result['msg'] = "remote file is a directory, fetch cannot work on directories"
return result
else:
if slurpres['encoding'] == 'base64':
remote_data = base64.b64decode(slurpres['content'])
if remote_data is not None:
remote_checksum = checksum_s(remote_data)
# calculate the destination name
if os.path.sep not in self._connection._shell.join_path('a', ''):
@ -83,13 +135,14 @@ class ActionModule(ActionBase):
else:
source_local = source
dest = os.path.expanduser(dest)
# ensure we only use file name, avoid relative paths
if not is_subpath(dest, original_dest):
# TODO: ? dest = os.path.expanduser(dest.replace(('../','')))
raise AnsibleActionFail("Detected directory traversal, expected to be contained in '%s' but got '%s'" % (original_dest, dest))
if flat:
if os.path.isdir(to_bytes(dest, errors='surrogate_or_strict')) and not dest.endswith(os.sep):
result['msg'] = "dest is an existing directory, use a trailing slash if you want to fetch src into that directory"
result['file'] = dest
result['failed'] = True
return result
raise AnsibleActionFail("dest is an existing directory, use a trailing slash if you want to fetch src into that directory")
if dest.endswith(os.sep):
# if the path ends with "/", we'll use the source filename as the
# destination filename
@ -106,23 +159,7 @@ class ActionModule(ActionBase):
target_name = self._play_context.remote_addr
dest = "%s/%s/%s" % (self._loader.path_dwim(dest), target_name, source_local)
dest = dest.replace("//", "/")
if remote_checksum in REMOTE_CHECKSUM_ERRORS:
result['changed'] = False
result['file'] = source
result['msg'] = REMOTE_CHECKSUM_ERRORS[remote_checksum]
# Historically, these don't fail because you may want to transfer
# a log file that possibly MAY exist but keep going to fetch other
# log files. Today, this is better achieved by adding
# ignore_errors or failed_when to the task. Control the behaviour
# via fail_when_missing
if fail_on_missing:
result['failed'] = True
del result['changed']
else:
result['msg'] += ", not transferring, ignored"
return result
dest = os.path.normpath(dest)
# calculate checksum for the local file
local_checksum = checksum(dest)
@ -132,7 +169,15 @@ class ActionModule(ActionBase):
makedirs_safe(os.path.dirname(dest))
# fetch the file and check for changes
if remote_data is None:
self._connection.fetch_file(source, dest)
else:
try:
f = open(to_bytes(dest, errors='surrogate_or_strict'), 'wb')
f.write(remote_data)
f.close()
except (IOError, OSError) as e:
raise AnsibleActionFail("Failed to fetch the file: %s" % e)
new_checksum = secure_hash(dest)
# For backwards compatibility. We'll return None on FIPS enabled systems
try:
@ -157,10 +202,6 @@ class ActionModule(ActionBase):
result.update(dict(changed=False, md5sum=local_md5, file=source, dest=dest, checksum=local_checksum))
finally:
try:
self._remove_tmp_path(self._connection._shell.tmpdir)
except AttributeError:
# .tmpdir was added to ShellModule in v2.6.0, so old versions don't have it
pass
return result

@ -26,14 +26,15 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import unicode_literals
"""
Fetch the connection configuration stack that would be used to connect to a
target, without actually connecting to it.
"""
from __future__ import absolute_import, division, print_function
from __future__ import unicode_literals
__metaclass__ = type
import ansible_mitogen.connection
from ansible.plugins.action import ActionBase

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path
import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path
import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path
import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path
import sys

@ -27,12 +27,13 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path
import sys
from ansible.errors import AnsibleConnectionFailure
from ansible.module_utils.six import iteritems
import ansible.errors
try:
import ansible_mitogen
@ -61,7 +62,7 @@ class Connection(ansible_mitogen.connection.Connection):
def __init__(self, *args, **kwargs):
if not _get_result:
raise AnsibleConnectionFailure(self.not_supported_msg)
raise ansible.errors.AnsibleConnectionFailure(self.not_supported_msg)
super(Connection, self).__init__(*args, **kwargs)
def get_extra_args(self):
@ -72,8 +73,10 @@ class Connection(ansible_mitogen.connection.Connection):
# Ansible >= 2.10, _get_result is a get_with_context_result
connection_options = _get_result.object.connection_options
parameters = []
for key, option in iteritems(connection_options):
if self.get_task_var('ansible_' + key) is not None:
parameters += [ option, self.get_task_var('ansible_' + key) ]
for key in connection_options:
task_var_name = 'ansible_%s' % key
task_var = self.get_task_var(task_var_name)
if task_var is not None:
parameters += [connection_options[key], task_var]
return parameters

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path
import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path
import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path
import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path
import sys

@ -0,0 +1,46 @@
# Copyright 2022, Mitogen contributers
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path
import sys
try:
import ansible_mitogen
except ImportError:
base_dir = os.path.dirname(__file__)
sys.path.insert(0, os.path.abspath(os.path.join(base_dir, '../../..')))
del base_dir
import ansible_mitogen.connection
class Connection(ansible_mitogen.connection.Connection):
transport = 'podman'

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path
import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path
import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path
import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path
import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path
import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path
import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path
import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path
import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import atexit
import logging
import multiprocessing

@ -36,6 +36,9 @@ Each class in here has a corresponding Planner class in planners.py that knows
how to build arguments for it, preseed related data, etc.
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import atexit
import imp
import os

@ -39,18 +39,18 @@ connections, grant access to files by children, and register for notification
when a child has completed a job.
"""
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
from __future__ import unicode_literals
__metaclass__ = type
import logging
import os
import os.path
import sys
import threading
import ansible.constants
import mitogen
import mitogen.core
import mitogen.service
import mitogen.utils
import ansible_mitogen.loaders

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os
import signal
import threading
@ -43,7 +45,7 @@ import ansible_mitogen.mixins
import ansible_mitogen.process
import ansible.executor.process.worker
from ansible.utils.sentinel import Sentinel
import ansible.utils.sentinel
def _patch_awx_callback():
@ -54,12 +56,11 @@ def _patch_awx_callback():
# AWX uses sitecustomize.py to force-load this package. If it exists, we're
# running under AWX.
try:
from awx_display_callback.events import EventContext
from awx_display_callback.events import event_context
import awx_display_callback.events
except ImportError:
return
if hasattr(EventContext(), '_local'):
if hasattr(awx_display_callback.events.EventContext(), '_local'):
# Patched version.
return
@ -68,8 +69,8 @@ def _patch_awx_callback():
ctx = tls.setdefault('_ctx', {})
ctx.update(kwargs)
EventContext._local = threading.local()
EventContext.add_local = patch_add_local
awx_display_callback.events.EventContext._local = threading.local()
awx_display_callback.events.EventContext.add_local = patch_add_local
_patch_awx_callback()
@ -107,6 +108,7 @@ REDIRECTED_CONNECTION_PLUGINS = (
'lxc',
'lxd',
'machinectl',
'podman',
'setns',
'ssh',
)
@ -278,7 +280,7 @@ class StrategyMixin(object):
name=task.action,
class_only=True,
)
if play_context.connection is not Sentinel:
if play_context.connection is not ansible.utils.sentinel.Sentinel:
# 2.8 appears to defer computing this until inside the worker.
# TODO: figure out where it has moved.
ansible_mitogen.loaders.connection_loader.get(

@ -33,6 +33,9 @@ Helper functions intended to be executed on the target. These are entrypoints
for file transfer, module execution and sundry bits like changing file modes.
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import errno
import grp
import operator
@ -51,7 +54,6 @@ import types
logging = __import__('logging')
import mitogen.core
import mitogen.fork
import mitogen.parent
import mitogen.service
from mitogen.core import b
@ -652,7 +654,8 @@ def read_path(path):
"""
Fetch the contents of a filesystem `path` as bytes.
"""
return open(path, 'rb').read()
with open(path, 'rb') as f:
return f.read()
def set_file_owner(path, owner, group=None, fd=None):

@ -26,9 +26,6 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import unicode_literals
"""
Mitogen extends Ansible's target configuration mechanism in several ways that
require some care:
@ -60,6 +57,10 @@ information from PlayContext, and another that takes (almost) all information
from HostVars.
"""
from __future__ import absolute_import, division, print_function
from __future__ import unicode_literals
__metaclass__ = type
import abc
import os
import ansible.utils.shlex
@ -354,6 +355,12 @@ class Spec(with_metaclass(abc.ABCMeta, object)):
The path to the "machinectl" program for the 'setns' transport.
"""
@abc.abstractmethod
def mitogen_podman_path(self):
"""
The path to the "podman" program for the 'podman' transport.
"""
@abc.abstractmethod
def mitogen_ssh_keepalive_interval(self):
"""
@ -527,6 +534,9 @@ class PlayContextSpec(Spec):
def mitogen_lxc_info_path(self):
return self._connection.get_task_var('mitogen_lxc_info_path')
def mitogen_podman_path(self):
return self._connection.get_task_var('mitogen_podman_path')
def mitogen_ssh_keepalive_interval(self):
return self._connection.get_task_var('mitogen_ssh_keepalive_interval')
@ -747,6 +757,9 @@ class MitogenViaSpec(Spec):
def mitogen_lxc_info_path(self):
return self._host_vars.get('mitogen_lxc_info_path')
def mitogen_podman_path(self):
return self._host_vars.get('mitogen_podman_path')
def mitogen_ssh_keepalive_interval(self):
return self._host_vars.get('mitogen_ssh_keepalive_interval')

@ -1,4 +1,5 @@
from __future__ import absolute_import
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import distutils.version

@ -147,8 +147,8 @@ Noteworthy Differences
* Mitogen 0.2.x supports Ansible 2.3-2.9; with Python 2.6, 2.7, or 3.6.
Mitogen 0.3.1+ supports
- Ansible 2.10, 3, and 4; with Python 2.7, or 3.6-3.9
- Ansible 5; with Python 3.8-3.9
- Ansible 2.10, 3, and 4; with Python 2.7, or 3.6-3.10
- Ansible 5; with Python 3.8-3.10
Verify your installation is running one of these versions by checking
``ansible --version`` output.
@ -188,9 +188,9 @@ Noteworthy Differences
your_ssh_username = (ALL) NOPASSWD:/usr/bin/python -c*
* The :ans:conn:`~buildah`, :ans:conn:`~docker`, :ans:conn:`~jail`,
:ans:conn:`~kubectl`, :ans:conn:`~local`, :ans:conn:`~lxd`, and
:ans:conn:`~ssh` built-in connection types are supported, along with
Mitogen-specific :ref:`machinectl <machinectl>`, :ref:`mitogen_doas <doas>`,
:ans:conn:`~kubectl`, :ans:conn:`~local`, :ans:conn:`~lxd`,
:ans:conn:`~podman`, & :ans:conn:`~ssh` connection types are supported; also
Mitogen-specific :ref:`mitogen_doas <doas>`, :ref:`machinectl <machinectl>`,
:ref:`mitogen_su <su>`, :ref:`mitogen_sudo <sudo>`, and :ref:`setns <setns>`
types. File bugs to register interest in others.
@ -819,6 +819,20 @@ Like the :ans:conn:`local` except connection delegation is supported.
* ``ansible_python_interpreter``
Podman
~~~~~~
Like :ans:conn:`podman` except connection delegation is supported.
* ``ansible_host``: Name of container (default: inventory hostname).
* ``ansible_user``: Name of user within the container to execute as.
* ``mitogen_mask_remote_name``: if :data:`True`, mask the identity of the
Ansible controller process on remote machines. To simplify diagnostics,
Mitogen produces remote processes named like
`"mitogen:user@controller.name:1234"`, however this may be a privacy issue in
some circumstances.
Process Model
^^^^^^^^^^^^^

@ -18,6 +18,16 @@ To avail of fixes in an unreleased version, please download a ZIP file
`directly from GitHub <https://github.com/dw/mitogen/>`_.
v0.3.3 (2022-06-03)
-------------------
* :gh:issue:`906` Support packages dynamically inserted into sys.modules, e.g. `distro` >= 1.7.0 as `ansible.module_utils.distro`.
* :gh:issue:`918` Support Python 3.10
* :gh:issue:`920` Support Ansible :ans:conn:`~podman` connection plugin
* :gh:issue:`836` :func:`mitogen.utils.with_router` decorator preserves the docstring in addition to the name.
* :gh:issue:`936` :ans:mod:`fetch` no longer emits `[DEPRECATION WARNING]: The '_remote_checksum()' method is deprecated.`
v0.3.2 (2022-01-12)
-------------------

@ -201,7 +201,7 @@ nested.py:
print('Connect local%d via %s' % (x, context))
context = router.local(via=context, name='local%d' % x)
context.call(os.system, 'pstree -s python -s mitogen')
context.call(subprocess.check_call, ['pstree', '-s', 'python', '-s', 'mitogen'])
Output:

@ -101,7 +101,7 @@ to your network topology**.
container='billing0',
)
internal_box.call(os.system, './run-nightly-billing.py')
internal_box.call(subprocess.check_call, ['./run-nightly-billing.py'])
The multiplexer also ensures the remote process is terminated if your Python
program crashes, communication is lost, or the application code running in the
@ -250,7 +250,7 @@ After:
"""
Install our application.
"""
os.system('tar zxvf app.tar.gz')
subprocess.check_call(['tar', 'zxvf', 'app.tar.gz'])
context.call(install_app)
@ -258,7 +258,7 @@ Or even:
.. code-block:: python
context.call(os.system, 'tar zxvf app.tar.gz')
context.call(subprocess.check_call, ['tar', 'zxvf', 'app.tar.gz'])
Exceptions raised by function calls are propagated back to the parent program,
and timeouts can be configured to ensure failed calls do not block progress of

@ -8,14 +8,14 @@ Usage:
Where:
<hostname> Hostname to install to.
"""
import os
import subprocess
import sys
import mitogen
def install_app():
os.system('tar zxvf my_app.tar.gz')
subprocess.check_call(['tar', 'zxvf', 'my_app.tar.gz'])
@mitogen.main()

@ -35,7 +35,7 @@ be expected. On the slave, it is built dynamically during startup.
#: Library version as a tuple.
__version__ = (0, 3, 2)
__version__ = (0, 3, 3)
#: This is :data:`False` in slave contexts. Previously it was used to prevent

@ -30,7 +30,6 @@
import logging
import mitogen.core
import mitogen.parent

@ -386,6 +386,20 @@ def _partition(s, sep, find):
return left, sep, s[len(left)+len(sep):]
def threading__current_thread():
try:
return threading.current_thread() # Added in Python 2.6+
except AttributeError:
return threading.currentThread() # Deprecated in Python 3.10+
def threading__thread_name(thread):
try:
return thread.name # Added in Python 2.6+
except AttributeError:
return thread.getName() # Deprecated in Python 3.10+
if hasattr(UnicodeType, 'rpartition'):
str_partition = UnicodeType.partition
str_rpartition = UnicodeType.rpartition
@ -1357,6 +1371,16 @@ class Importer(object):
fp.close()
def find_module(self, fullname, path=None):
"""
Return a loader (ourself) or None, for the module with fullname.
Implements importlib.abc.MetaPathFinder.find_module().
Deprecrated in Python 3.4+, replaced by find_spec().
Raises ImportWarning in Python 3.10+.
fullname A (fully qualified?) module name, e.g. "os.path".
path __path__ of parent packge. None for a top level module.
"""
if hasattr(_tls, 'running'):
return None
@ -1478,6 +1502,12 @@ class Importer(object):
callback()
def load_module(self, fullname):
"""
Return the loaded module specified by fullname.
Implements importlib.abc.Loader.load_module().
Deprecated in Python 3.4+, replaced by create_module() & exec_module().
"""
fullname = to_text(fullname)
_v and self._log.debug('requesting %s', fullname)
self._refuse_imports(fullname)
@ -2687,7 +2717,7 @@ class Latch(object):
raise e
assert cookie == got_cookie, (
"Cookie incorrect; got %r, expected %r" \
"Cookie incorrect; got %r, expected %r"
% (binascii.hexlify(got_cookie),
binascii.hexlify(cookie))
)
@ -2742,7 +2772,7 @@ class Latch(object):
return 'Latch(%#x, size=%d, t=%r)' % (
id(self),
len(self._queue),
threading.currentThread().getName(),
threading__thread_name(threading__current_thread()),
)
@ -3642,7 +3672,6 @@ class Dispatcher(object):
self._service_recv.notify = None
self.recv.close()
@classmethod
@takes_econtext
def forget_chain(cls, chain_id, econtext):

@ -103,7 +103,6 @@ import tempfile
import threading
import mitogen.core
import mitogen.master
import mitogen.parent
from mitogen.core import LOG, IOLOG
@ -200,7 +199,7 @@ class Process(object):
def _on_stdin(self, msg):
if msg.is_dead:
IOLOG.debug('%r._on_stdin() -> %r', self, data)
IOLOG.debug('%r._on_stdin() -> %r', self, msg)
self.pump.protocol.close()
return
@ -437,7 +436,7 @@ def run(dest, router, args, deadline=None, econtext=None):
fp.write(inspect.getsource(mitogen.core))
fp.write('\n')
fp.write('ExternalContext(%r).main()\n' % (
_get_econtext_config(context, sock2),
_get_econtext_config(econtext, sock2),
))
finally:
fp.close()

@ -28,7 +28,6 @@
# !mitogen: minify_safe
import mitogen.core
import mitogen.parent

@ -28,7 +28,6 @@
# !mitogen: minify_safe
import mitogen.core
import mitogen.parent

@ -28,7 +28,6 @@
# !mitogen: minify_safe
import mitogen.core
import mitogen.parent

@ -122,6 +122,13 @@ def is_stdlib_name(modname):
"""
Return :data:`True` if `modname` appears to come from the standard library.
"""
# `imp.is_builtin()` isn't a documented as part of Python's stdlib API.
#
# """
# Main is a little special - imp.is_builtin("__main__") will return False,
# but BuiltinImporter is still the most appropriate initial setting for
# its __loader__ attribute.
# """ -- comment in CPython pylifecycle.c:add_main_module()
if imp.is_builtin(modname) != 0:
return True
@ -512,42 +519,57 @@ class PkgutilMethod(FinderMethod):
Find `fullname` using :func:`pkgutil.find_loader`.
"""
try:
# If fullname refers to a submodule that's not already imported
# then the containing package is imported.
# Pre-'import spec' this returned None, in Python3.6 it raises
# ImportError.
loader = pkgutil.find_loader(fullname)
except ImportError:
e = sys.exc_info()[1]
LOG.debug('%r._get_module_via_pkgutil(%r): %s',
self, fullname, e)
LOG.debug('%r: find_loader(%r) failed: %s', self, fullname, e)
return None
IOLOG.debug('%r._get_module_via_pkgutil(%r) -> %r',
self, fullname, loader)
if not loader:
LOG.debug('%r: find_loader(%r) returned %r, aborting',
self, fullname, loader)
return
try:
path, is_special = _py_filename(loader.get_filename(fullname))
source = loader.get_source(fullname)
is_pkg = loader.is_package(fullname)
# workaround for special python modules that might only exist in memory
if is_special and is_pkg and not source:
source = '\n'
path = loader.get_filename(fullname)
except (AttributeError, ImportError):
# - Per PEP-302, get_source() and is_package() are optional,
# calling them may throw AttributeError.
# - get_filename() may throw ImportError if pkgutil.find_loader()
# picks a "parent" package's loader for some crap that's been
# stuffed in sys.modules, for example in the case of urllib3:
# "loader for urllib3.contrib.pyopenssl cannot handle
# requests.packages.urllib3.contrib.pyopenssl"
e = sys.exc_info()[1]
LOG.debug('%r: loading %r using %r failed: %s',
self, fullname, loader, e)
LOG.debug('%r: %r.get_file_name(%r) failed: %r', self, loader, fullname, e)
return
path, is_special = _py_filename(path)
try:
source = loader.get_source(fullname)
except AttributeError:
# Per PEP-302, get_source() is optional,
e = sys.exc_info()[1]
LOG.debug('%r: %r.get_source() failed: %r', self, loader, fullname, e)
return
try:
is_pkg = loader.is_package(fullname)
except AttributeError:
# Per PEP-302, is_package() is optional,
e = sys.exc_info()[1]
LOG.debug('%r: %r.is_package(%r) failed: %r', self, loader, fullname, e)
return
# workaround for special python modules that might only exist in memory
if is_special and is_pkg and not source:
source = '\n'
if path is None or source is None:
LOG.debug('%r: path=%r, source=%r, aborting', self, path, source)
return
if isinstance(source, mitogen.core.UnicodeType):
@ -567,23 +589,37 @@ class SysModulesMethod(FinderMethod):
"""
Find `fullname` using its :data:`__file__` attribute.
"""
module = sys.modules.get(fullname)
try:
module = sys.modules[fullname]
except KeyError:
LOG.debug('%r: sys.modules[%r] absent, aborting', self, fullname)
return
if not isinstance(module, types.ModuleType):
LOG.debug('%r: sys.modules[%r] absent or not a regular module',
self, fullname)
LOG.debug('%r: sys.modules[%r] is %r, aborting',
self, fullname, module)
return
LOG.debug('_get_module_via_sys_modules(%r) -> %r', fullname, module)
alleged_name = getattr(module, '__name__', None)
if alleged_name != fullname:
LOG.debug('sys.modules[%r].__name__ is incorrect, assuming '
'this is a hacky module alias and ignoring it. '
'Got %r, module object: %r',
fullname, alleged_name, module)
try:
resolved_name = module.__name__
except AttributeError:
LOG.debug('%r: %r has no __name__, aborting', self, module)
return
path, _ = _py_filename(getattr(module, '__file__', ''))
if resolved_name != fullname:
LOG.debug('%r: %r.__name__ is %r, aborting',
self, module, resolved_name)
return
try:
path = module.__file__
except AttributeError:
LOG.debug('%r: %r has no __file__, aborting', self, module)
return
path, _ = _py_filename(path)
if not path:
LOG.debug('%r: %r.__file__ is %r, aborting', self, module, path)
return
LOG.debug('%r: sys.modules[%r]: found %s', self, fullname, path)
@ -628,10 +664,24 @@ class ParentEnumerationMethod(FinderMethod):
module object or any parent package's :data:`__path__`, since they have all
been overwritten. Some men just want to watch the world burn.
"""
@staticmethod
def _iter_parents(fullname):
"""
>>> list(ParentEnumerationMethod._iter_parents('a'))
[('', 'a')]
>>> list(ParentEnumerationMethod._iter_parents('a.b.c'))
[('a.b', 'c'), ('a', 'b'), ('', 'a')]
"""
while fullname:
fullname, _, modname = str_rpartition(fullname, u'.')
yield fullname, modname
def _find_sane_parent(self, fullname):
"""
Iteratively search :data:`sys.modules` for the least indirect parent of
`fullname` that is loaded and contains a :data:`__path__` attribute.
`fullname` that's from the same package and has a :data:`__path__`
attribute.
:return:
`(parent_name, path, modpath)` tuple, where:
@ -644,21 +694,40 @@ class ParentEnumerationMethod(FinderMethod):
* `modpath`: list of module name components leading from `path`
to the target module.
"""
path = None
modpath = []
while True:
pkgname, _, modname = str_rpartition(to_text(fullname), u'.')
for pkgname, modname in self._iter_parents(fullname):
modpath.insert(0, modname)
if not pkgname:
return [], None, modpath
pkg = sys.modules.get(pkgname)
path = getattr(pkg, '__path__', None)
if pkg and path:
return pkgname.split('.'), path, modpath
try:
pkg = sys.modules[pkgname]
except KeyError:
LOG.debug('%r: sys.modules[%r] absent, skipping', self, pkgname)
continue
try:
resolved_pkgname = pkg.__name__
except AttributeError:
LOG.debug('%r: %r has no __name__, skipping', self, pkg)
continue
LOG.debug('%r: %r lacks __path__ attribute', self, pkgname)
fullname = pkgname
if resolved_pkgname != pkgname:
LOG.debug('%r: %r.__name__ is %r, skipping',
self, pkg, resolved_pkgname)
continue
try:
path = pkg.__path__
except AttributeError:
LOG.debug('%r: %r has no __path__, skipping', self, pkg)
continue
if not path:
LOG.debug('%r: %r.__path__ is %r, skipping', self, pkg, path)
continue
return pkgname.split('.'), path, modpath
def _found_package(self, fullname, path):
path = os.path.join(path, '__init__.py')
@ -1167,7 +1236,7 @@ class Broker(mitogen.core.Broker):
def __init__(self, install_watcher=True):
if install_watcher:
self._watcher = ThreadWatcher.watch(
target=threading.currentThread(),
target=mitogen.core.threading__current_thread(),
on_join=self.shutdown,
)
super(Broker, self).__init__()

@ -35,7 +35,6 @@ Support for operating in a mixed threading/forking environment.
import os
import socket
import sys
import threading
import weakref
import mitogen.core
@ -158,7 +157,7 @@ class Corker(object):
held. This will not return until each thread acknowledges it has ceased
execution.
"""
current = threading.currentThread()
current = mitogen.core.threading__current_thread()
s = mitogen.core.b('CORK') * ((128 // 4) * 1024)
self._rsocks = []

@ -1508,7 +1508,7 @@ class Connection(object):
def get_preamble(self):
suffix = (
'\nExternalContext(%r).main()\n' %\
'\nExternalContext(%r).main()\n' %
(self.get_econtext_config(),)
)
partial = get_core_source_partial()

@ -31,7 +31,6 @@
import logging
import mitogen.core
import mitogen.parent

@ -31,7 +31,6 @@
import grp
import logging
import os
import os.path
import pprint
import pwd
import stat
@ -109,7 +108,8 @@ def get_or_create_pool(size=None, router=None, context=None):
def get_thread_name():
return threading.currentThread().getName()
thread = mitogen.core.threading__current_thread()
return mitogen.core.threading__thread_name(thread)
def call(service_name, method_name, call_context=None, **kwargs):

@ -29,14 +29,13 @@
# !mitogen: minify_safe
import datetime
import functools
import logging
import os
import sys
import mitogen
import mitogen.core
import mitogen.master
import mitogen.parent
iteritems = getattr(dict, 'iteritems', dict.items)
@ -173,12 +172,9 @@ def with_router(func):
do_stuff(blah, 123)
"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
return run_with_router(func, *args, **kwargs)
if mitogen.core.PY3:
wrapper.func_name = func.__name__
else:
wrapper.func_name = func.func_name
return wrapper

@ -28,10 +28,6 @@ NOCOVERAGE="${NOCOVERAGE:-}"
NOCOVERAGE_ERASE="${NOCOVERAGE_ERASE:-$NOCOVERAGE}"
NOCOVERAGE_REPORT="${NOCOVERAGE_REPORT:-$NOCOVERAGE}"
if [ ! "$UNIT2" ]; then
UNIT2="$(which unit2)"
fi
if [ ! "$NOCOVERAGE_ERASE" ]; then
coverage erase
fi
@ -39,12 +35,12 @@ fi
# First run overwites coverage output.
[ "$SKIP_MITOGEN" ] || {
if [ ! "$NOCOVERAGE" ]; then
coverage run -a "${UNIT2}" discover \
coverage run -a -m unittest discover \
--start-directory "tests" \
--pattern '*_test.py' \
"$@"
else
"${UNIT2}" discover \
python -m unittest discover \
--start-directory "tests" \
--pattern '*_test.py' \
"$@"
@ -60,12 +56,12 @@ fi
[ "$SKIP_ANSIBLE" ] || {
export PYTHONPATH=`pwd`/tests:$PYTHONPATH
if [ ! "$NOCOVERAGE" ]; then
coverage run -a "${UNIT2}" discover \
coverage run -a -m unittest discover \
--start-directory "tests/ansible" \
--pattern '*_test.py' \
"$@"
else
"${UNIT2}" discover \
python -m unittest discover \
--start-directory "tests/ansible" \
--pattern '*_test.py' \
"$@"

@ -60,7 +60,7 @@ setup(
license = 'New BSD',
url = 'https://github.com/mitogen-hq/mitogen/',
packages = find_packages(exclude=['tests', 'examples']),
python_requires='>=2.4, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4',
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
zip_safe = False,
classifiers = [
'Environment :: Console',
@ -70,16 +70,13 @@ setup(
'Operating System :: MacOS :: MacOS X',
'Operating System :: POSIX',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.4',
'Programming Language :: Python :: 2.5',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: Implementation :: CPython',
'Topic :: System :: Distributed Computing',
'Topic :: System :: Systems Administration',

@ -10,7 +10,7 @@ demonstrator for what does and doesn't work.
## Preparation
See `../image_prep/README.md`.
See [`../image_prep/README.md`](../image_prep/README.md).
## `run_ansible_playbook.py`

@ -1,3 +1,2 @@
def path():
return "integration/module_utils/roles/modrole/module_utils/external2.py"

@ -1,11 +1,11 @@
#!/usr/bin/python
import json
from ansible.module_utils.basic import path
import ansible.module_utils.basic
def main():
print(json.dumps({
'path': path()
'path': ansible.module_utils.basic.path()
}))
if __name__ == '__main__':

@ -17,7 +17,7 @@
- custom_python_run_script:
script: |
import atexit, shutil
import atexit, os, shutil
path = '{{path}}'
os.mkdir(path, int('777', 8))
atexit.register(shutil.rmtree, path)

@ -1,8 +1,5 @@
import traceback
import sys
from ansible.plugins.strategy import StrategyBase
from ansible.plugins.action import ActionBase

@ -7,13 +7,11 @@
__metaclass__ = type
import inspect
import unittest2
import unittest
import ansible.template
from ansible.errors import AnsibleError
from ansible.plugins.action import ActionBase
from ansible.module_utils.six import string_types
TEMPLATE_KWARGS = {}
@ -23,7 +21,7 @@ if 'bare_deprecated' in _argspec.args:
TEMPLATE_KWARGS['bare_deprecated'] = False
class TestCase(unittest2.TestCase):
class TestCase(unittest.TestCase):
def runTest(self):
pass

@ -1,8 +1,5 @@
import traceback
import sys
from ansible.plugins.strategy import StrategyBase
from ansible.plugins.action import ActionBase

@ -1,4 +1,3 @@
import sys
from ansible.plugins.strategy import StrategyBase

@ -5,9 +5,7 @@ required for reliable LRU tests.
import ansible_mitogen.connection
import ansible_mitogen.services
import mitogen.service
from ansible.plugins.strategy import StrategyBase
from ansible.plugins.action import ActionBase

@ -1,4 +1,3 @@
# Monkey-patch os.fork() to produce a latency histogram on run completion.
# Requires 'hdrhsitograms' PyPI module.

@ -1,7 +1,6 @@
from __future__ import unicode_literals
import io
import os
import sys
from ansible import constants as C
from ansible.module_utils import six

@ -1,4 +1,3 @@
from ansible.module_utils._text import to_text

@ -1,3 +1,2 @@
def path():
return "ansible/lib/module_utils/external2.py"

@ -1,3 +1,2 @@
def path():
return 'ansible/lib/module_utils/externalpkg/extmod.py'

@ -4,7 +4,6 @@
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import get_module_path
from ansible.module_utils import six
import os
import pwd

@ -1,8 +1,6 @@
#!/usr/bin/python
# I am an Ansible Python JSONARGS module. I should receive an encoding string.
import sys
json_arguments = """<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>"""
print("{")

@ -5,9 +5,6 @@
from ansible.module_utils.basic import AnsibleModule
import os
import pwd
import socket
import sys
def main():

@ -2,13 +2,6 @@
# issue #555: I'm a module that cutpastes an old hack.
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import get_module_path
from ansible.module_utils import six
import os
import pwd
import socket
import sys
import sys
reload(sys)

@ -3,12 +3,7 @@
# parameter.
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import get_module_path
from ansible.module_utils import six
import os
import pwd
import socket
import sys

@ -1,6 +1,5 @@
#!/usr/bin/env python
import os
import subprocess
import sys
os.environ['ANSIBLE_STRATEGY'] = 'mitogen_linear'
os.execlp(

@ -1,12 +1,14 @@
# issue #615: 'fetch' with become: was internally using slurp.
- hosts: target
- hosts: test-targets
any_errors_fatal: True
gather_facts: no
# Without Mitogen this causes Ansible to use the slurp module, which is *slow*
become: true
vars:
mitogen_ssh_compression: false
tasks:
- block:
- shell: |
dd if=/dev/zero of=/tmp/512mb.zero bs=1048576 count=512;
chmod go= /tmp/512mb.zero
@ -15,9 +17,16 @@
src: /tmp/512mb.zero
dest: /tmp/fetch-out
- file:
path: /tmp/512mb.zero
state: absent
- file:
path: /tmp/fetch-out
state: absent
delegate_to: localhost
run_once: true
when:
- is_mitogen
tags:
- issue_615

@ -13,36 +13,33 @@
- name: set up test container and run tests inside it
block:
- name: install deps
block:
- name: install docker
shell: |
# NOTE: for tracking purposes: https://github.com/docker/for-mac/issues/2359
# using docker for mac CI workaround: https://github.com/drud/ddev/pull/1748/files#diff-19288f650af2dabdf1dcc5b354d1f245
DOCKER_URL=https://download.docker.com/mac/stable/31259/Docker.dmg &&
curl -O -sSL $DOCKER_URL &&
open -W Docker.dmg && cp -r /Volumes/Docker/Docker.app /Applications
sudo /Applications/Docker.app/Contents/MacOS/Docker --quit-after-install --unattended &&
ln -s /Applications/Docker.app/Contents/Resources/bin/docker /usr/local/bin/docker &&
nohup /Applications/Docker.app/Contents/MacOS/Docker --unattended &
# wait 2 min for docker to come up
counter=0 &&
while ! /usr/local/bin/docker ps 2>/dev/null ; do
if [ $counter -lt 24 ]; then
let counter=counter+1
else
exit 1
fi
sleep 5
done
homebrew:
name:
- podman
state: present
- name: start machine
command:
cmd: "{{ item.cmd }}"
loop:
- cmd: podman machine init
- cmd: podman machine start
- cmd: podman info
timeout: 300
register: podman_machine
- debug:
var: podman_machine
# python bindings (docker_container) aren't working on this host, so gonna shell out
- name: create docker container
shell: /usr/local/bin/docker run --name testMitogen -d --rm centos:8 bash -c "sleep infinity & wait"
- name: create container
command:
cmd: podman run --name testMitogen -d --rm centos:8 bash -c "sleep infinity & wait"
- name: add container to inventory
add_host:
name: testMitogen
ansible_connection: docker
ansible_connection: podman
ansible_user: root
changed_when: false
environment:
@ -82,6 +79,10 @@
PATH: /usr/local/bin/:{{ ansible_env.PATH }}
- name: remove test container
shell: /usr/local/bin/docker stop testMitogen
command:
cmd: "{{ item.cmd }}"
loop:
- cmd: podman stop testMitogen
- cmd: podman machine stop
tags:
- issue_655

@ -1,11 +1,9 @@
import multiprocessing
import os
import sys
import tempfile
import unittest
import mock
import unittest2
import testlib
import mitogen.parent
@ -18,7 +16,7 @@ class NullFixedPolicy(ansible_mitogen.affinity.FixedPolicy):
self.mask = mask
@unittest2.skipIf(
@unittest.skipIf(
reason='Linux only',
condition=(not os.uname()[0] == 'Linux')
)
@ -29,139 +27,139 @@ class FixedPolicyTest(testlib.TestCase):
# Uniprocessor .
policy = self.klass(cpu_count=1)
policy.assign_controller()
self.assertEquals(0x1, policy.mask)
self.assertEqual(0x1, policy.mask)
def test_assign_controller_2core(self):
# Small SMP gets 1.. % cpu_count
policy = self.klass(cpu_count=2)
policy.assign_controller()
self.assertEquals(0x2, policy.mask)
self.assertEqual(0x2, policy.mask)
policy.assign_controller()
self.assertEquals(0x2, policy.mask)
self.assertEqual(0x2, policy.mask)
policy.assign_controller()
def test_assign_controller_3core(self):
# Small SMP gets 1.. % cpu_count
policy = self.klass(cpu_count=3)
policy.assign_controller()
self.assertEquals(0x2, policy.mask)
self.assertEqual(0x2, policy.mask)
policy.assign_controller()
self.assertEquals(0x4, policy.mask)
self.assertEqual(0x4, policy.mask)
policy.assign_controller()
self.assertEquals(0x2, policy.mask)
self.assertEqual(0x2, policy.mask)
policy.assign_controller()
self.assertEquals(0x4, policy.mask)
self.assertEqual(0x4, policy.mask)
policy.assign_controller()
def test_assign_controller_4core(self):
# Big SMP gets a dedicated core.
policy = self.klass(cpu_count=4)
policy.assign_controller()
self.assertEquals(0x2, policy.mask)
self.assertEqual(0x2, policy.mask)
policy.assign_controller()
self.assertEquals(0x2, policy.mask)
self.assertEqual(0x2, policy.mask)
def test_assign_muxprocess_1core(self):
# Uniprocessor .
policy = self.klass(cpu_count=1)
policy.assign_muxprocess(0)
self.assertEquals(0x1, policy.mask)
self.assertEqual(0x1, policy.mask)
def test_assign_muxprocess_2core(self):
# Small SMP gets dedicated core.
policy = self.klass(cpu_count=2)
policy.assign_muxprocess(0)
self.assertEquals(0x1, policy.mask)
self.assertEqual(0x1, policy.mask)
policy.assign_muxprocess(0)
self.assertEquals(0x1, policy.mask)
self.assertEqual(0x1, policy.mask)
policy.assign_muxprocess(0)
def test_assign_muxprocess_3core(self):
# Small SMP gets a dedicated core.
policy = self.klass(cpu_count=3)
policy.assign_muxprocess(0)
self.assertEquals(0x1, policy.mask)
self.assertEqual(0x1, policy.mask)
policy.assign_muxprocess(0)
self.assertEquals(0x1, policy.mask)
self.assertEqual(0x1, policy.mask)
def test_assign_muxprocess_4core(self):
# Big SMP gets a dedicated core.
policy = self.klass(cpu_count=4)
policy.assign_muxprocess(0)
self.assertEquals(0x1, policy.mask)
self.assertEqual(0x1, policy.mask)
policy.assign_muxprocess(0)
self.assertEquals(0x1, policy.mask)
self.assertEqual(0x1, policy.mask)
def test_assign_worker_1core(self):
# Balance n % 1
policy = self.klass(cpu_count=1)
policy.assign_worker()
self.assertEquals(0x1, policy.mask)
self.assertEqual(0x1, policy.mask)
policy.assign_worker()
self.assertEquals(0x1, policy.mask)
self.assertEqual(0x1, policy.mask)
def test_assign_worker_2core(self):
# Balance n % 1
policy = self.klass(cpu_count=2)
policy.assign_worker()
self.assertEquals(0x2, policy.mask)
self.assertEqual(0x2, policy.mask)
policy.assign_worker()
self.assertEquals(0x2, policy.mask)
self.assertEqual(0x2, policy.mask)
def test_assign_worker_3core(self):
# Balance n % 1
policy = self.klass(cpu_count=3)
policy.assign_worker()
self.assertEquals(0x2, policy.mask)
self.assertEqual(0x2, policy.mask)
policy.assign_worker()
self.assertEquals(0x4, policy.mask)
self.assertEqual(0x4, policy.mask)
policy.assign_worker()
self.assertEquals(0x2, policy.mask)
self.assertEqual(0x2, policy.mask)
def test_assign_worker_4core(self):
# Balance n % 1
policy = self.klass(cpu_count=4)
policy.assign_worker()
self.assertEquals(4, policy.mask)
self.assertEqual(4, policy.mask)
policy.assign_worker()
self.assertEquals(8, policy.mask)
self.assertEqual(8, policy.mask)
policy.assign_worker()
self.assertEquals(4, policy.mask)
self.assertEqual(4, policy.mask)
def test_assign_subprocess_1core(self):
# allow all except reserved.
policy = self.klass(cpu_count=1)
policy.assign_subprocess()
self.assertEquals(0x1, policy.mask)
self.assertEqual(0x1, policy.mask)
policy.assign_subprocess()
self.assertEquals(0x1, policy.mask)
self.assertEqual(0x1, policy.mask)
def test_assign_subprocess_2core(self):
# allow all except reserved.
policy = self.klass(cpu_count=2)
policy.assign_subprocess()
self.assertEquals(0x2, policy.mask)
self.assertEqual(0x2, policy.mask)
policy.assign_subprocess()
self.assertEquals(0x2, policy.mask)
self.assertEqual(0x2, policy.mask)
def test_assign_subprocess_3core(self):
# allow all except reserved.
policy = self.klass(cpu_count=3)
policy.assign_subprocess()
self.assertEquals(0x2 + 0x4, policy.mask)
self.assertEqual(0x2 + 0x4, policy.mask)
policy.assign_subprocess()
self.assertEquals(0x2 + 0x4, policy.mask)
self.assertEqual(0x2 + 0x4, policy.mask)
def test_assign_subprocess_4core(self):
# allow all except reserved.
policy = self.klass(cpu_count=4)
policy.assign_subprocess()
self.assertEquals(0x4 + 0x8, policy.mask)
self.assertEqual(0x4 + 0x8, policy.mask)
policy.assign_subprocess()
self.assertEquals(0x4 + 0x8, policy.mask)
self.assertEqual(0x4 + 0x8, policy.mask)
@unittest2.skipIf(
@unittest.skipIf(
reason='Linux/SMP only',
condition=(not (
os.uname()[0] == 'Linux' and
@ -186,13 +184,13 @@ class LinuxPolicyTest(testlib.TestCase):
def test_set_cpu_mask(self):
self.policy._set_cpu_mask(0x1)
self.assertEquals(0x1, self._get_cpus())
self.assertEqual(0x1, self._get_cpus())
self.policy._set_cpu_mask(0x2)
self.assertEquals(0x2, self._get_cpus())
self.assertEqual(0x2, self._get_cpus())
self.policy._set_cpu_mask(0x3)
self.assertEquals(0x3, self._get_cpus())
self.assertEqual(0x3, self._get_cpus())
def test_clear_on_popen(self):
tf = tempfile.NamedTemporaryFile()
@ -223,11 +221,7 @@ class MockLinuxPolicyTest(testlib.TestCase):
for x in range(1, 4096, 32):
policy.assign_subprocess()
MockLinuxPolicyTest = unittest2.skipIf(
MockLinuxPolicyTest = unittest.skipIf(
condition=(not sys.platform.startswith('linuxPolicy')),
reason='select.select() not supported'
)(MockLinuxPolicyTest)
if __name__ == '__main__':
unittest2.main()

@ -1,19 +1,12 @@
from __future__ import absolute_import
import os
import os.path
import subprocess
import tempfile
import time
import unittest2
import mock
import ansible.errors
import ansible.playbook.play_context
import mitogen.core
import mitogen.utils
import ansible_mitogen.connection
import ansible_mitogen.plugins.connection.mitogen_local
@ -27,7 +20,6 @@ class MuxProcessMixin(object):
@classmethod
def setUpClass(cls):
#mitogen.utils.log_to_file()
cls.model = ansible_mitogen.process.get_classic_worker_model(
_init_logging=False
)
@ -93,20 +85,20 @@ class OptionalIntTest(testlib.TestCase):
func = staticmethod(ansible_mitogen.connection.optional_int)
def test_already_int(self):
self.assertEquals(0, self.func(0))
self.assertEquals(1, self.func(1))
self.assertEquals(-1, self.func(-1))
self.assertEqual(0, self.func(0))
self.assertEqual(1, self.func(1))
self.assertEqual(-1, self.func(-1))
def test_is_string(self):
self.assertEquals(0, self.func("0"))
self.assertEquals(1, self.func("1"))
self.assertEquals(-1, self.func("-1"))
self.assertEqual(0, self.func("0"))
self.assertEqual(1, self.func("1"))
self.assertEqual(-1, self.func("-1"))
def test_is_none(self):
self.assertEquals(None, self.func(None))
self.assertEqual(None, self.func(None))
def test_is_junk(self):
self.assertEquals(None, self.func({1:2}))
self.assertEqual(None, self.func({1:2}))
class FetchFileTest(ConnectionMixin, testlib.TestCase):
@ -121,7 +113,7 @@ class FetchFileTest(ConnectionMixin, testlib.TestCase):
# transfer_file() uses os.rename rather than direct data
# overwrite, so we must reopen.
with open(ofp.name, 'rb') as fp:
self.assertEquals(ifp.read(), fp.read())
self.assertEqual(ifp.read(), fp.read())
class PutDataTest(ConnectionMixin, testlib.TestCase):
@ -131,7 +123,8 @@ class PutDataTest(ConnectionMixin, testlib.TestCase):
self.conn.put_data(path, contents)
self.wait_for_completion()
self.assertEquals(contents, open(path, 'rb').read())
with open(path, 'rb') as f:
self.assertEqual(contents, f.read())
os.unlink(path)
def test_mode(self):
@ -141,7 +134,7 @@ class PutDataTest(ConnectionMixin, testlib.TestCase):
self.conn.put_data(path, contents, mode=int('0123', 8))
self.wait_for_completion()
st = os.stat(path)
self.assertEquals(int('0123', 8), st.st_mode & int('0777', 8))
self.assertEqual(int('0123', 8), st.st_mode & int('0777', 8))
os.unlink(path)
@ -165,17 +158,18 @@ class PutFileTest(ConnectionMixin, testlib.TestCase):
path = tempfile.mktemp(prefix='mitotest')
self.conn.put_file(in_path=__file__, out_path=path)
self.wait_for_completion()
self.assertEquals(open(path, 'rb').read(),
open(__file__, 'rb').read())
with open(path, 'rb') as path_f:
with open(__file__, 'rb') as __file__f:
self.assertEqual(path_f.read(), __file__f.read())
os.unlink(path)
def test_out_path_big(self):
path = tempfile.mktemp(prefix='mitotest')
self.conn.put_file(in_path=self.big_path, out_path=path)
self.wait_for_completion()
self.assertEquals(open(path, 'rb').read(),
open(self.big_path, 'rb').read())
with open(path, 'rb') as path_f:
with open(self.big_path, 'rb') as big_path_f:
self.assertEqual(path_f.read(), big_path_f.read())
#self._compare_times_modes(path, __file__)
os.unlink(path)
@ -183,7 +177,3 @@ class PutFileTest(ConnectionMixin, testlib.TestCase):
path = tempfile.mktemp(prefix='mitotest')
self.assertRaises(ansible.errors.AnsibleFileNotFound,
lambda: self.conn.put_file(in_path='/nonexistent', out_path=path))
if __name__ == '__main__':
unittest2.main()

@ -1,9 +1,6 @@
import os
import sys
import tempfile
import mock
import unittest2
import testlib
from mitogen.core import b
@ -68,7 +65,3 @@ class WatcherTest(testlib.TestCase):
self.tf.flush()
watcher.check()
self.assertEqual(environb[b('SOMEKEY')], b('\xff\xff\xff'))
if __name__ == '__main__':
unittest2.main()

@ -1,9 +1,8 @@
from __future__ import absolute_import
import os.path
import subprocess
import tempfile
import unittest2
import unittest
import mock
@ -56,18 +55,18 @@ class FindGoodTempDirTest(testlib.TestCase):
class ApplyModeSpecTest(unittest2.TestCase):
class ApplyModeSpecTest(unittest.TestCase):
func = staticmethod(ansible_mitogen.target.apply_mode_spec)
def test_simple(self):
spec = 'u+rwx,go=x'
self.assertEquals(int('0711', 8), self.func(spec, 0))
self.assertEqual(int('0711', 8), self.func(spec, 0))
spec = 'g-rw'
self.assertEquals(int('0717', 8), self.func(spec, int('0777', 8)))
self.assertEqual(int('0717', 8), self.func(spec, int('0777', 8)))
class IsGoodTempDirTest(unittest2.TestCase):
class IsGoodTempDirTest(unittest.TestCase):
func = staticmethod(ansible_mitogen.target.is_good_temp_dir)
def test_creates(self):
@ -84,9 +83,10 @@ class IsGoodTempDirTest(unittest2.TestCase):
fp.write('derp')
self.assertTrue(os.path.isfile(bleh))
self.assertFalse(self.func(bleh))
self.assertEquals(open(bleh).read(), 'derp')
with open(bleh) as fp:
self.assertEqual(fp.read(), 'derp')
@unittest2.skipIf(
@unittest.skipIf(
os.geteuid() == 0, 'writes by root ignore directory permissions')
def test_unwriteable(self):
with NamedTemporaryDirectory() as temp_path:
@ -105,8 +105,3 @@ class IsGoodTempDirTest(unittest2.TestCase):
os_access.return_value = False
with NamedTemporaryDirectory() as temp_path:
self.assertFalse(self.func(temp_path))
if __name__ == '__main__':
unittest2.main()

@ -5,6 +5,12 @@ Measure latency of .fork() setup/teardown.
import mitogen
import mitogen.core
try:
xrange
except NameError:
xrange = range
@mitogen.main()
def main(router):
t0 = mitogen.core.now()
@ -12,4 +18,4 @@ def main(router):
t = mitogen.core.now()
ctx = router.fork()
ctx.shutdown(wait=True)
print '++', 1000 * ((mitogen.core.now() - t0) / (1.0+x))
print('++ %d' % 1000 * ((mitogen.core.now() - t0) / (1.0+x)))

@ -1,10 +1,5 @@
# Verify _receive_one() quadratic behaviour fixed.
import subprocess
import time
import socket
import mitogen
import mitogen.core

@ -3,9 +3,7 @@ Measure latency of IPC between two local threads.
"""
import threading
import time
import mitogen
import mitogen.core
import mitogen.utils
import ansible_mitogen.affinity

@ -2,8 +2,6 @@
Measure latency of .local() setup.
"""
import time
import mitogen
import mitogen.core
import mitogen.utils

@ -2,7 +2,6 @@
import sys
import os
import time
import mitogen.core

@ -2,9 +2,6 @@
Measure latency of local RPC.
"""
import time
import mitogen
import mitogen.core
import mitogen.utils
import ansible_mitogen.affinity

@ -1,9 +1,6 @@
"""
Measure latency of local service RPC.
"""
import time
import mitogen
import mitogen.core
import mitogen.service

@ -3,9 +3,7 @@ Measure latency of SSH RPC.
"""
import sys
import time
import mitogen
import mitogen.core
import mitogen.utils
import ansible_mitogen.affinity

@ -1,11 +1,7 @@
# Verify throughput over sudo and SSH at various compression levels.
import os
import random
import socket
import subprocess
import tempfile
import time
import mitogen
import mitogen.core

@ -1,9 +1,4 @@
import time
import threading
import mock
import unittest2
import testlib
@ -19,7 +14,7 @@ class ShutdownTest(testlib.TestCase):
broker.poller.close = mock.Mock()
broker.shutdown()
broker.join()
self.assertEquals(1, len(broker.poller.close.mock_calls))
self.assertEqual(1, len(broker.poller.close.mock_calls))
actual_close()
@ -31,7 +26,7 @@ class DeferTest(testlib.TestCase):
broker = self.klass()
try:
broker.defer(lambda: latch.put(123))
self.assertEquals(123, latch.get())
self.assertEqual(123, latch.get())
finally:
broker.shutdown()
broker.join()
@ -44,7 +39,7 @@ class DeferTest(testlib.TestCase):
e = self.assertRaises(mitogen.core.Error,
lambda: broker.defer(lambda: latch.put(123)))
self.assertEquals(e.args[0], mitogen.core.Waker.broker_shutdown_msg)
self.assertEqual(e.args[0], mitogen.core.Waker.broker_shutdown_msg)
class DeferSyncTest(testlib.TestCase):
@ -53,8 +48,8 @@ class DeferSyncTest(testlib.TestCase):
def test_okay(self):
broker = self.klass()
try:
th = broker.defer_sync(lambda: threading.currentThread())
self.assertEquals(th, broker._thread)
th = broker.defer_sync(lambda: mitogen.core.threading__current_thread())
self.assertEqual(th, broker._thread)
finally:
broker.shutdown()
broker.join()
@ -67,7 +62,3 @@ class DeferSyncTest(testlib.TestCase):
finally:
broker.shutdown()
broker.join()
if __name__ == '__main__':
unittest2.main()

@ -1,9 +1,5 @@
import os
import mitogen
import unittest2
import testlib
@ -17,12 +13,8 @@ class ConstructorTest(testlib.RouterMixin, testlib.TestCase):
stream = self.router.stream_by_id(context.context_id)
argv = eval(context.call(os.getenv, 'ORIGINAL_ARGV'))
self.assertEquals(argv[0], buildah_path)
self.assertEquals(argv[1], 'run')
self.assertEquals(argv[2], '--')
self.assertEquals(argv[3], 'container_name')
self.assertEquals(argv[4], stream.conn.options.python_path)
if __name__ == '__main__':
unittest2.main()
self.assertEqual(argv[0], buildah_path)
self.assertEqual(argv[1], 'run')
self.assertEqual(argv[2], '--')
self.assertEqual(argv[3], 'container_name')
self.assertEqual(argv[4], stream.conn.options.python_path)

@ -1,8 +1,6 @@
import pickle
import sys
import unittest2
import mitogen.core
import testlib
@ -14,25 +12,25 @@ class ConstructorTest(testlib.TestCase):
def test_string_noargs(self):
e = self.klass('%s%s')
self.assertEquals(e.args[0], '%s%s')
self.assertEqual(e.args[0], '%s%s')
self.assertTrue(isinstance(e.args[0], mitogen.core.UnicodeType))
def test_string_args(self):
e = self.klass('%s%s', 1, 1)
self.assertEquals(e.args[0], '11')
self.assertEqual(e.args[0], '11')
self.assertTrue(isinstance(e.args[0], mitogen.core.UnicodeType))
def test_from_exc(self):
ve = plain_old_module.MyError('eek')
e = self.klass(ve)
self.assertEquals(e.args[0], 'plain_old_module.MyError: eek')
self.assertEqual(e.args[0], 'plain_old_module.MyError: eek')
self.assertTrue(isinstance(e.args[0], mitogen.core.UnicodeType))
def test_form_base_exc(self):
ve = SystemExit('eek')
e = self.klass(ve)
cls = ve.__class__
self.assertEquals(e.args[0],
self.assertEqual(e.args[0],
# varies across 2/3.
'%s.%s: eek' % (cls.__module__, cls.__name__))
self.assertTrue(isinstance(e.args[0], mitogen.core.UnicodeType))
@ -50,14 +48,14 @@ class ConstructorTest(testlib.TestCase):
def test_bytestring_conversion(self):
e = self.klass(mitogen.core.b('bytes'))
self.assertEquals(u'bytes', e.args[0])
self.assertEqual(u'bytes', e.args[0])
self.assertTrue(isinstance(e.args[0], mitogen.core.UnicodeType))
def test_reduce(self):
e = self.klass('eek')
func, (arg,) = e.__reduce__()
self.assertTrue(func is mitogen.core._unpickle_call_error)
self.assertEquals(arg, e.args[0])
self.assertEqual(arg, e.args[0])
class UnpickleCallErrorTest(testlib.TestCase):
@ -73,10 +71,10 @@ class UnpickleCallErrorTest(testlib.TestCase):
def test_reify(self):
e = self.func(u'some error')
self.assertEquals(mitogen.core.CallError, e.__class__)
self.assertEquals(1, len(e.args))
self.assertEquals(mitogen.core.UnicodeType, type(e.args[0]))
self.assertEquals(u'some error', e.args[0])
self.assertEqual(mitogen.core.CallError, e.__class__)
self.assertEqual(1, len(e.args))
self.assertEqual(mitogen.core.UnicodeType, type(e.args[0]))
self.assertEqual(u'some error', e.args[0])
class PickleTest(testlib.TestCase):
@ -85,18 +83,18 @@ class PickleTest(testlib.TestCase):
def test_string_noargs(self):
e = self.klass('%s%s')
e2 = pickle.loads(pickle.dumps(e))
self.assertEquals(e2.args[0], '%s%s')
self.assertEqual(e2.args[0], '%s%s')
def test_string_args(self):
e = self.klass('%s%s', 1, 1)
e2 = pickle.loads(pickle.dumps(e))
self.assertEquals(e2.args[0], '11')
self.assertEqual(e2.args[0], '11')
def test_from_exc(self):
ve = plain_old_module.MyError('eek')
e = self.klass(ve)
e2 = pickle.loads(pickle.dumps(e))
self.assertEquals(e2.args[0], 'plain_old_module.MyError: eek')
self.assertEqual(e2.args[0], 'plain_old_module.MyError: eek')
def test_from_exc_tb(self):
try:
@ -108,7 +106,3 @@ class PickleTest(testlib.TestCase):
e2 = pickle.loads(pickle.dumps(e))
self.assertTrue(e2.args[0].startswith('plain_old_module.MyError: eek'))
self.assertTrue('test_from_exc_tb' in e2.args[0])
if __name__ == '__main__':
unittest2.main()

@ -1,11 +1,7 @@
import logging
import time
import unittest2
import mitogen.core
import mitogen.parent
import mitogen.master
from mitogen.core import str_partition
import testlib
@ -79,7 +75,7 @@ class CallFunctionTest(testlib.RouterMixin, testlib.TestCase):
def test_bad_return_value(self):
exc = self.assertRaises(mitogen.core.StreamError,
lambda: self.local.call(func_with_bad_return_value))
self.assertEquals(
self.assertEqual(
exc.args[0],
"cannot unpickle '%s'/'CrazyType'" % (__name__,),
)
@ -91,7 +87,7 @@ class CallFunctionTest(testlib.RouterMixin, testlib.TestCase):
self.broker.defer(stream.on_disconnect, self.broker)
exc = self.assertRaises(mitogen.core.ChannelError,
lambda: recv.get())
self.assertEquals(exc.args[0], self.router.respondent_disconnect_msg)
self.assertEqual(exc.args[0], self.router.respondent_disconnect_msg)
def test_aborted_on_local_broker_shutdown(self):
stream = self.router._stream_by_id[self.local.context_id]
@ -101,7 +97,7 @@ class CallFunctionTest(testlib.RouterMixin, testlib.TestCase):
self.broker_shutdown = True
exc = self.assertRaises(mitogen.core.ChannelError,
lambda: recv.get())
self.assertEquals(exc.args[0], self.router.respondent_disconnect_msg)
self.assertEqual(exc.args[0], self.router.respondent_disconnect_msg)
def test_accepts_returns_context(self):
context = self.local.call(func_returns_arg, self.local)
@ -114,10 +110,10 @@ class CallFunctionTest(testlib.RouterMixin, testlib.TestCase):
recv = mitogen.core.Receiver(self.router)
sender = recv.to_sender()
sender2 = self.local.call(func_accepts_returns_sender, sender)
self.assertEquals(sender.context.context_id,
self.assertEqual(sender.context.context_id,
sender2.context.context_id)
self.assertEquals(sender.dst_handle, sender2.dst_handle)
self.assertEquals(123, recv.get().unpickle())
self.assertEqual(sender.dst_handle, sender2.dst_handle)
self.assertEqual(123, recv.get().unpickle())
self.assertRaises(mitogen.core.ChannelError,
lambda: recv.get().unpickle())
@ -132,19 +128,19 @@ class CallChainTest(testlib.RouterMixin, testlib.TestCase):
def test_subsequent_calls_produce_same_error(self):
chain = self.klass(self.local, pipelined=True)
self.assertEquals('xx', chain.call(func_returns_arg, 'xx'))
self.assertEqual('xx', chain.call(func_returns_arg, 'xx'))
chain.call_no_reply(function_that_fails, 'x1')
e1 = self.assertRaises(mitogen.core.CallError,
lambda: chain.call(function_that_fails, 'x2'))
e2 = self.assertRaises(mitogen.core.CallError,
lambda: chain.call(func_returns_arg, 'x3'))
self.assertEquals(str(e1), str(e2))
self.assertEqual(str(e1), str(e2))
def test_unrelated_overlapping_failed_chains(self):
c1 = self.klass(self.local, pipelined=True)
c2 = self.klass(self.local, pipelined=True)
c1.call_no_reply(function_that_fails, 'c1')
self.assertEquals('yes', c2.call(func_returns_arg, 'yes'))
self.assertEqual('yes', c2.call(func_returns_arg, 'yes'))
self.assertRaises(mitogen.core.CallError,
lambda: c1.call(func_returns_arg, 'yes'))
@ -154,7 +150,7 @@ class CallChainTest(testlib.RouterMixin, testlib.TestCase):
e1 = self.assertRaises(mitogen.core.CallError,
lambda: c1.call(function_that_fails, 'x2'))
c1.reset()
self.assertEquals('x3', c1.call(func_returns_arg, 'x3'))
self.assertEqual('x3', c1.call(func_returns_arg, 'x3'))
class UnsupportedCallablesTest(testlib.RouterMixin, testlib.TestCase):
@ -170,21 +166,17 @@ class UnsupportedCallablesTest(testlib.RouterMixin, testlib.TestCase):
closure = lambda: a
e = self.assertRaises(TypeError,
lambda: self.local.call(closure))
self.assertEquals(e.args[0], self.klass.closures_msg)
self.assertEqual(e.args[0], self.klass.closures_msg)
def test_lambda_unsupported(self):
lam = lambda: None
e = self.assertRaises(TypeError,
lambda: self.local.call(lam))
self.assertEquals(e.args[0], self.klass.lambda_msg)
self.assertEqual(e.args[0], self.klass.lambda_msg)
def test_instance_method_unsupported(self):
class X:
def x(): pass
e = self.assertRaises(TypeError,
lambda: self.local.call(X().x))
self.assertEquals(e.args[0], self.klass.method_msg)
if __name__ == '__main__':
unittest2.main()
self.assertEqual(e.args[0], self.klass.method_msg)

@ -1,5 +1,3 @@
import unittest2
import mitogen.core
import testlib
@ -14,7 +12,3 @@ class ConstructorTest(testlib.RouterMixin, testlib.TestCase):
self.assertEqual(chan.dst_handle, 123)
self.assertIsNotNone(chan.handle)
self.assertGreater(chan.handle, 0)
if __name__ == '__main__':
unittest2.main()

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save