Merge remote-tracking branch 'origin/master' into issue1086

pull/1094/head
timansky 2 days ago
commit 88eb64745e

@ -2,7 +2,7 @@
# `.ci`
This directory contains scripts for Continuous Integration platforms. Currently
Azure Pipelines, but they will also happily run on any Debian-like machine.
GitHub Actions, but ideally they will also 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,14 +28,15 @@ for doing `setup.py install` while pulling a Docker container, for example.
### Environment Variables
* `DISTRO`: the `mitogen_` tests need a target Docker container distro. This
name comes from the Docker Hub `mitogen` user, i.e. `mitogen/$DISTRO-test`
* `DISTROS`: the `ansible_` tests can run against multiple targets
simultaneously, which speeds things up. This is a space-separated list of
DISTRO names, but additionally, supports:
* `MITOGEN_TEST_DISTRO_SPECS`: a space delimited list of distro specs to run
the tests against. (e.g. `centos6 ubuntu2004-py3*4`). Each spec determines
the Linux distribution, target Python interepreter & number of instances.
Only distributions with a pre-built Linux container image can be used.
* `debian-py3`: when generating Ansible inventory file, set
`ansible_python_interpreter` to `python3`, i.e. run a test where the
target interpreter is Python 3.
* `debian*16`: generate 16 Docker containers running Debian. Also works
with -py3.
* `MITOGEN_TEST_IMAGE_TEMPLATE`: specifies the Linux container image name,
and hence the container registry used for test targets.

@ -35,13 +35,13 @@ ci_lib.check_stray_processes(interesting)
with ci_lib.Fold('docker_setup'):
containers = ci_lib.container_specs(ci_lib.DISTROS)
containers = ci_lib.container_specs(ci_lib.DISTRO_SPECS.split())
ci_lib.start_containers(containers)
with ci_lib.Fold('job_setup'):
os.chdir(TESTS_DIR)
os.chmod('../data/docker/mitogen__has_sudo_pubkey.key', int('0600', 7))
os.chmod('../data/docker/mitogen__has_sudo_pubkey.key', int('0600', 8))
ci_lib.run("mkdir %s", HOSTS_DIR)
for path in glob.glob(TESTS_DIR + '/hosts/*'):

@ -1,105 +0,0 @@
# Each step entry runs a task (Azure Pipelines analog of an Ansible module).
# https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/reference/?view=azure-pipelines&viewFallbackFrom=azure-devops#tool
# `{script: ...}` is shorthand for `{task: CmdLine@<mumble>, inputs: {script: ...}}`.
# The shell is bash.
# https://learn.microsoft.com/en-us/azure/devops/pipelines/yaml-schema/steps-script?view=azure-pipelines
# https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/reference/cmd-line-v2?view=azure-pipelines
steps:
- task: UsePythonVersion@0
displayName: Install python
inputs:
githubToken: '$(GITHUB_PYVER_TOKEN)'
versionSpec: '$(python.version)'
condition: ne(variables['python.version'], '')
- script: |
set -o errexit
set -o nounset
set -o pipefail
aws ecr-public get-login-password | docker login --username AWS --password-stdin public.ecr.aws
displayName: Authenticate to container registry
condition: eq(variables['Agent.OS'], 'Linux')
env:
AWS_ACCESS_KEY_ID: $(AWS_ACCESS_KEY_ID)
AWS_SECRET_ACCESS_KEY: $(AWS_SECRET_ACCESS_KEY)
AWS_DEFAULT_REGION: $(AWS_DEFAULT_REGION)
- script: |
set -o errexit
set -o nounset
set -o pipefail
sudo apt-get update
sudo apt-get install -y python2-dev python3-pip virtualenv
displayName: Install build deps
condition: and(eq(variables['python.version'], ''), eq(variables['Agent.OS'], 'Linux'))
- script: |
set -o errexit
set -o nounset
set -o pipefail
# macOS builders lack a realpath command
type python && python -c"import os.path;print(os.path.realpath('$(type -p python)'))" && python --version
type python2 && python2 -c"import os.path;print(os.path.realpath('$(type -p python2)'))" && python2 --version
type python3 && python3 -c"import os.path;print(os.path.realpath('$(type -p python3)'))" && python3 --version
echo
if [ -e /usr/bin/python ]; then
echo "/usr/bin/python: sys.executable: $(/usr/bin/python -c 'import sys; print(sys.executable)')"
fi
if [ -e /usr/bin/python2 ]; then
echo "/usr/bin/python2: sys.executable: $(/usr/bin/python2 -c 'import sys; print(sys.executable)')"
fi
if [ -e /usr/bin/python2.7 ]; then
echo "/usr/bin/python2.7: sys.executable: $(/usr/bin/python2.7 -c 'import sys; print(sys.executable)')"
fi
displayName: Show python versions
- script: |
set -o errexit
set -o nounset
set -o pipefail
# Tox environment name (e.g. py312-mode_mitogen) -> Python executable name (e.g. python3.12)
PYTHON=$(python -c 'import re; print(re.sub(r"^py([23])([0-9]{1,2}).*", r"python\1.\2", "$(tox.env)"))')
if [[ -z $PYTHON ]]; then
echo 1>&2 "Python interpreter could not be determined"
exit 1
fi
if [[ $PYTHON == "python2.7" && $(uname) == "Darwin" ]]; then
"$PYTHON" -m ensurepip --user --altinstall --no-default-pip
"$PYTHON" -m pip install --user -r "tests/requirements-tox.txt"
elif [[ $PYTHON == "python2.7" ]]; then
curl "https://bootstrap.pypa.io/pip/2.7/get-pip.py" --output "get-pip.py"
"$PYTHON" get-pip.py --user --no-python-version-warning
# Avoid Python 2.x pip masking system pip
rm -f ~/.local/bin/{easy_install,pip,wheel}
"$PYTHON" -m pip install --user -r "tests/requirements-tox.txt"
else
"$PYTHON" -m pip install -r "tests/requirements-tox.txt"
fi
displayName: Install tooling
- script: |
set -o errexit
set -o nounset
set -o pipefail
# Tox environment name (e.g. py312-mode_mitogen) -> Python executable name (e.g. python3.12)
PYTHON=$(python -c 'import re; print(re.sub(r"^py([23])([0-9]{1,2}).*", r"python\1.\2", "$(tox.env)"))')
if [[ -z $PYTHON ]]; then
echo 1>&2 "Python interpreter could not be determined"
exit 1
fi
"$PYTHON" -m tox -e "$(tox.env)"
displayName: "Run tests"

@ -1,157 +0,0 @@
# Python package
# Create and test a Python package on multiple Python versions.
# 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
# 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
trigger:
branches:
include:
- "*"
exclude:
- docs-master
jobs:
- job: mac12
# vanilla Ansible is really slow
timeoutInMinutes: 120
steps:
- template: azure-pipelines-steps.yml
pool:
# https://github.com/actions/runner-images/blob/main/images/macos/macos-12-Readme.md
vmImage: macOS-12
strategy:
matrix:
Mito_312:
tox.env: py312-mode_mitogen
Loc_312_10:
tox.env: py312-mode_localhost-ansible10
Van_312_10:
tox.env: py312-mode_localhost-ansible10-strategy_linear
- job: Linux
pool:
# https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2004-Readme.md
vmImage: ubuntu-20.04
steps:
- template: azure-pipelines-steps.yml
strategy:
matrix:
Mito_27_centos6:
tox.env: py27-mode_mitogen-distro_centos6
Mito_27_centos7:
tox.env: py27-mode_mitogen-distro_centos7
Mito_27_centos8:
tox.env: py27-mode_mitogen-distro_centos8
Mito_27_debian9:
tox.env: py27-mode_mitogen-distro_debian9
Mito_27_debian10:
tox.env: py27-mode_mitogen-distro_debian10
Mito_27_debian11:
tox.env: py27-mode_mitogen-distro_debian11
Mito_27_ubuntu1604:
tox.env: py27-mode_mitogen-distro_ubuntu1604
Mito_27_ubuntu1804:
tox.env: py27-mode_mitogen-distro_ubuntu1804
Mito_27_ubuntu2004:
tox.env: py27-mode_mitogen-distro_ubuntu2004
Mito_36_centos6:
python.version: '3.6'
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
Mito_312_centos6:
python.version: '3.12'
tox.env: py312-mode_mitogen-distro_centos6
Mito_312_centos7:
python.version: '3.12'
tox.env: py312-mode_mitogen-distro_centos7
Mito_312_centos8:
python.version: '3.12'
tox.env: py312-mode_mitogen-distro_centos8
Mito_312_debian9:
python.version: '3.12'
tox.env: py312-mode_mitogen-distro_debian9
Mito_312_debian10:
python.version: '3.12'
tox.env: py312-mode_mitogen-distro_debian10
Mito_312_debian11:
python.version: '3.12'
tox.env: py312-mode_mitogen-distro_debian11
Mito_312_ubuntu1604:
python.version: '3.12'
tox.env: py312-mode_mitogen-distro_ubuntu1604
Mito_312_ubuntu1804:
python.version: '3.12'
tox.env: py312-mode_mitogen-distro_ubuntu1804
Mito_312_ubuntu2004:
python.version: '3.12'
tox.env: py312-mode_mitogen-distro_ubuntu2004
Ans_27_210:
tox.env: py27-mode_ansible-ansible2.10
Ans_27_4:
tox.env: py27-mode_ansible-ansible4
Ans_36_210:
python.version: '3.6'
tox.env: py36-mode_ansible-ansible2.10
Ans_36_4:
python.version: '3.6'
tox.env: py36-mode_ansible-ansible4
Ans_311_210:
python.version: '3.11'
tox.env: py311-mode_ansible-ansible2.10
Ans_311_3:
python.version: '3.11'
tox.env: py311-mode_ansible-ansible3
Ans_311_4:
python.version: '3.11'
tox.env: py311-mode_ansible-ansible4
Ans_311_5:
python.version: '3.11'
tox.env: py311-mode_ansible-ansible5
Ans_312_6:
python.version: '3.12'
tox.env: py312-mode_ansible-ansible6
Ans_312_7:
python.version: '3.12'
tox.env: py312-mode_ansible-ansible7
Ans_312_8:
python.version: '3.12'
tox.env: py312-mode_ansible-ansible8
Ans_312_9:
python.version: '3.12'
tox.env: py312-mode_ansible-ansible9
Ans_312_10:
python.version: '3.12'
tox.env: py312-mode_ansible-ansible10

@ -28,6 +28,10 @@ os.chdir(
)
DISTRO_SPECS = os.environ.get(
'MITOGEN_TEST_DISTRO_SPECS',
'centos6 centos8 debian9 debian11 ubuntu1604 ubuntu2004',
)
IMAGE_TEMPLATE = os.environ.get(
'MITOGEN_TEST_IMAGE_TEMPLATE',
'public.ecr.aws/n5z0e8q9/%(distro)s-test',
@ -196,10 +200,6 @@ class Fold(object):
GIT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
# 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()
TMP = TempDir().path

@ -3,8 +3,6 @@
from __future__ import print_function
import getpass
import io
import os
import subprocess
import sys
@ -53,22 +51,13 @@ with ci_lib.Fold('machine_prep'):
subprocess.check_call('sudo chmod 700 ~root/.ssh', shell=True)
subprocess.check_call('sudo chmod 600 ~root/.ssh/authorized_keys', shell=True)
os.chdir(IMAGE_PREP_DIR)
ci_lib.run("ansible-playbook -c local -i localhost, macos_localhost.yml")
if os.path.expanduser('~mitogen__user1') == '~mitogen__user1':
os.chdir(IMAGE_PREP_DIR)
ci_lib.run("ansible-playbook -c local -i localhost, _user_accounts.yml")
# FIXME Don't hardcode https://github.com/mitogen-hq/mitogen/issues/1022
# and os.environ['USER'] is not populated on Azure macOS runners.
os.chdir(HOSTS_DIR)
with io.open('default.hosts', 'r+', encoding='utf-8') as f:
user = getpass.getuser()
content = f.read()
content = content.replace("{{ lookup('pipe', 'whoami') }}", user)
f.seek(0)
f.write(content)
f.truncate()
ci_lib.dump_file('default.hosts')
cmd = ';'.join([
'from __future__ import print_function',
'import os, sys',

@ -8,8 +8,6 @@ import ci_lib
os.environ.update({
'NOCOVERAGE': '1',
'UNIT2': '/usr/local/python2.4.6/bin/unit2',
'MITOGEN_TEST_DISTRO': ci_lib.DISTRO,
'MITOGEN_LOG_LEVEL': 'debug',
'SKIP_ANSIBLE': '1',
})

@ -6,7 +6,6 @@ import os
import ci_lib
os.environ.update({
'MITOGEN_TEST_DISTRO': ci_lib.DISTRO,
'MITOGEN_LOG_LEVEL': 'debug',
'SKIP_ANSIBLE': '1',
})

@ -48,99 +48,33 @@ jobs:
- name: Ans_311_5
python_version: '3.11'
tox_env: py311-mode_ansible-ansible5
- name: Ans_312_6
python_version: '3.12'
tox_env: py312-mode_ansible-ansible6
- name: Ans_312_7
python_version: '3.12'
tox_env: py312-mode_ansible-ansible7
- name: Ans_312_8
python_version: '3.12'
tox_env: py312-mode_ansible-ansible8
- name: Ans_312_9
python_version: '3.12'
tox_env: py312-mode_ansible-ansible9
- name: Ans_312_10
python_version: '3.12'
tox_env: py312-mode_ansible-ansible10
- name: Van_312_10
python_version: '3.12'
tox_env: py312-mode_ansible-ansible10-strategy_linear
- name: Mito_27_centos6
tox_env: py27-mode_mitogen-distro_centos6
- name: Mito_27_centos7
tox_env: py27-mode_mitogen-distro_centos7
- name: Mito_27_centos8
tox_env: py27-mode_mitogen-distro_centos8
- name: Mito_27_debian9
tox_env: py27-mode_mitogen-distro_debian9
- name: Mito_27_debian10
tox_env: py27-mode_mitogen-distro_debian10
- name: Mito_27_debian11
tox_env: py27-mode_mitogen-distro_debian11
- name: Mito_27_ubuntu1604
tox_env: py27-mode_mitogen-distro_ubuntu1604
- name: Mito_27_ubuntu1804
tox_env: py27-mode_mitogen-distro_ubuntu1804
- name: Mito_27_ubuntu2004
tox_env: py27-mode_mitogen-distro_ubuntu2004
- name: Mito_36_centos6
python_version: '3.6'
tox_env: py36-mode_mitogen-distro_centos6
- name: Mito_36_centos7
python_version: '3.6'
tox_env: py36-mode_mitogen-distro_centos7
- name: Mito_36_centos8
python_version: '3.6'
tox_env: py36-mode_mitogen-distro_centos8
- name: Mito_36_debian9
python_version: '3.6'
tox_env: py36-mode_mitogen-distro_debian9
- name: Mito_36_debian10
python_version: '3.6'
tox_env: py36-mode_mitogen-distro_debian10
- name: Mito_36_debian11
python_version: '3.6'
tox_env: py36-mode_mitogen-distro_debian11
- name: Mito_36_ubuntu1604
python_version: '3.6'
tox_env: py36-mode_mitogen-distro_ubuntu1604
- name: Mito_36_ubuntu1804
python_version: '3.6'
tox_env: py36-mode_mitogen-distro_ubuntu1804
- name: Mito_36_ubuntu2004
- name: Ans_313_6
python_version: '3.13'
tox_env: py313-mode_ansible-ansible6
- name: Ans_313_7
python_version: '3.13'
tox_env: py313-mode_ansible-ansible7
- name: Ans_313_8
python_version: '3.13'
tox_env: py313-mode_ansible-ansible8
- name: Ans_313_9
python_version: '3.13'
tox_env: py313-mode_ansible-ansible9
- name: Ans_313_10
python_version: '3.13'
tox_env: py313-mode_ansible-ansible10
- name: Van_313_10
python_version: '3.13'
tox_env: py313-mode_ansible-ansible10-strategy_linear
- name: Mito_27
tox_env: py27-mode_mitogen
- name: Mito_36
python_version: '3.6'
tox_env: py36-mode_mitogen-distro_ubuntu2004
- name: Mito_312_centos6
python_version: '3.12'
tox_env: py312-mode_mitogen-distro_centos6
- name: Mito_312_centos7
python_version: '3.12'
tox_env: py312-mode_mitogen-distro_centos7
- name: Mito_312_centos8
python_version: '3.12'
tox_env: py312-mode_mitogen-distro_centos8
- name: Mito_312_debian9
python_version: '3.12'
tox_env: py312-mode_mitogen-distro_debian9
- name: Mito_312_debian10
python_version: '3.12'
tox_env: py312-mode_mitogen-distro_debian10
- name: Mito_312_debian11
python_version: '3.12'
tox_env: py312-mode_mitogen-distro_debian11
- name: Mito_312_ubuntu1604
python_version: '3.12'
tox_env: py312-mode_mitogen-distro_ubuntu1604
- name: Mito_312_ubuntu1804
python_version: '3.12'
tox_env: py312-mode_mitogen-distro_ubuntu1804
- name: Mito_312_ubuntu2004
python_version: '3.12'
tox_env: py312-mode_mitogen-distro_ubuntu2004
tox_env: py36-mode_mitogen
- name: Mito_313
python_version: '3.13'
tox_env: py313-mode_mitogen
steps:
- uses: actions/checkout@v4
@ -222,28 +156,22 @@ jobs:
"$PYTHON" -m tox -e "${{ matrix.tox_env }}"
macos:
# https://github.com/actions/runner-images/blob/main/images/macos/macos-12-Readme.md
runs-on: macos-12
# https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md
runs-on: macos-13
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
include:
- name: Mito_27
tox_env: py27-mode_mitogen
- name: Mito_312
tox_env: py312-mode_mitogen
- name: Mito_313
tox_env: py313-mode_mitogen
- name: Loc_27_210
tox_env: py27-mode_localhost-ansible2.10
- name: Loc_312_10
tox_env: py312-mode_localhost-ansible10
- name: Loc_313_10
tox_env: py313-mode_localhost-ansible10
- name: Van_27_210
tox_env: py27-mode_localhost-ansible2.10-strategy_linear
- name: Van_312_10
tox_env: py312-mode_localhost-ansible10-strategy_linear
- name: Van_313_10
tox_env: py313-mode_localhost-ansible10-strategy_linear
steps:
- uses: actions/checkout@v4
@ -324,3 +252,15 @@ jobs:
fi
"$PYTHON" -m tox -e "${{ matrix.tox_env }}"
# https://github.com/marketplace/actions/alls-green
check:
if: always()
needs:
- linux
- macos
runs-on: ubuntu-latest
steps:
- uses: re-actors/alls-green@release/v1
with:
jobs: ${{ toJSON(needs) }}

@ -1,9 +1,11 @@
# Mitogen
[![PyPI - Version](https://img.shields.io/pypi/v/mitogen)](https://pypi.org/project/mitogen/)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/mitogen)](https://pypi.org/project/mitogen/)
[![Build Status](https://img.shields.io/github/actions/workflow/status/mitogen-hq/mitogen/tests.yml?branch=master)](https://github.com/mitogen-hq/mitogen/actions?query=branch%3Amaster)
<a href="https://mitogen.networkgenomics.com/">Please see the documentation</a>.
![](https://i.imgur.com/eBM6LhJ.gif)
[![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://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)

@ -159,6 +159,7 @@ def _connect_ssh(spec):
}
}
def _connect_buildah(spec):
"""
Return ContextService arguments for a Buildah connection.
@ -174,6 +175,7 @@ def _connect_buildah(spec):
}
}
def _connect_docker(spec):
"""
Return ContextService arguments for a Docker connection.
@ -277,6 +279,7 @@ def _connect_podman(spec):
}
}
def _connect_setns(spec, kind=None):
"""
Return ContextService arguments for a mitogen_setns connection.
@ -812,7 +815,7 @@ class Connection(ansible.plugins.connection.ConnectionBase):
self.context = dct['context']
self.chain = CallChain(self, self.context, pipelined=True)
if self._play_context.become:
if self.become:
self.login_context = dct['via']
else:
self.login_context = self.context
@ -888,6 +891,29 @@ class Connection(ansible.plugins.connection.ConnectionBase):
self.binding.close()
self.binding = None
def _mitogen_var_options(self, templar):
# Workaround for https://github.com/ansible/ansible/issues/84238
var_names = C.config.get_plugin_vars('connection', self._load_name)
variables = templar.available_variables
var_options = {
var_name: templar.template(variables[var_name])
for var_name in var_names
if var_name in variables
}
if self.allow_extras:
extras_var_prefix = 'ansible_%s_' % self.extras_prefix
var_options['_extras'] = {
var_name: templar.template(variables[var_name])
for var_name in variables
if var_name not in var_options
and var_name.startswith(extras_var_prefix)
}
else:
var_options['_extras'] = {}
return var_options
reset_compat_msg = (
'Mitogen only supports "reset_connection" on Ansible 2.5.6 or later'
)
@ -920,11 +946,24 @@ class Connection(ansible.plugins.connection.ConnectionBase):
shared_loader_obj=0
)
# Workaround for https://github.com/ansible/ansible/issues/84238
try:
task, templar = self._play_context.vars.pop(
'_mitogen.smuggled.reset_connection',
)
except KeyError:
pass
else:
self.set_options(
task_keys=task.dump_attrs(),
var_options=self._mitogen_var_options(templar),
)
# Clear out state in case we were ever connected.
self.close()
inventory_name, stack = self._build_stack()
if self._play_context.become:
if self.become:
stack = stack[:-1]
worker_model = ansible_mitogen.process.get_worker_model()

@ -36,8 +36,6 @@ import random
import traceback
import ansible
import ansible.constants
import ansible.plugins
import ansible.plugins.action
import ansible.utils.unsafe_proxy
import ansible.vars.clean
@ -296,7 +294,7 @@ class ActionModuleMixin(ansible.plugins.action.ActionBase):
if not path.startswith('~'):
# /home/foo -> /home/foo
return path
if sudoable or not self._play_context.become:
if sudoable or not self._connection.become:
if path == '~':
# ~ -> /home/dmw
return self._connection.homedir

@ -32,35 +32,20 @@ __metaclass__ = type
import os.path
import sys
from ansible.plugins.connection.ssh import (
DOCUMENTATION as _ansible_ssh_DOCUMENTATION,
)
DOCUMENTATION = """
name: mitogen_ssh
author: David Wilson <dw@botanicus.net>
connection: mitogen_ssh
short_description: Connect over SSH via Mitogen
description:
- This connects using an OpenSSH client controlled by the Mitogen for
Ansible extension. It accepts every option the vanilla ssh plugin
accepts.
version_added: "2.5"
options:
ssh_args:
type: str
vars:
- name: ssh_args
- name: ansible_ssh_args
- name: ansible_mitogen_ssh_args
ssh_common_args:
type: str
vars:
- name: ssh_args
- name: ansible_ssh_common_args
- name: ansible_mitogen_ssh_common_args
ssh_extra_args:
type: str
vars:
- name: ssh_args
- name: ansible_ssh_extra_args
- name: ansible_mitogen_ssh_extra_args
"""
""" + _ansible_ssh_DOCUMENTATION.partition('options:\n')[2]
try:
import ansible_mitogen

@ -45,6 +45,7 @@ import ansible_mitogen.mixins
import ansible_mitogen.process
import ansible.executor.process.worker
import ansible.template
import ansible.utils.sentinel
@ -326,3 +327,24 @@ class StrategyMixin(object):
self._worker_model.on_strategy_complete()
finally:
ansible_mitogen.process.set_worker_model(None)
def _smuggle_to_connction_reset(self, task, play_context, iterator, target_host):
# Workaround for https://github.com/ansible/ansible/issues/84238
variables = self._variable_manager.get_vars(
play=iterator._play, host=target_host, task=task,
_hosts=self._hosts_cache, _hosts_all=self._hosts_cache_all,
)
templar = ansible.template.Templar(
loader=self._loader, variables=variables,
)
play_context.vars.update({
'_mitogen.smuggled.reset_connection': (task, templar),
})
def _execute_meta(self, task, play_context, iterator, target_host):
if task.args['_raw_params'] == 'reset_connection':
self._smuggle_to_connction_reset(task, play_context, iterator, target_host)
return super(StrategyMixin, self)._execute_meta(
task, play_context, iterator, target_host,
)

@ -62,7 +62,9 @@ from __future__ import unicode_literals
__metaclass__ = type
import abc
import logging
import os
import ansible.utils.shlex
import ansible.constants as C
import ansible.executor.interpreter_discovery
@ -74,6 +76,9 @@ from ansible.module_utils.parsing.convert_bool import boolean
import mitogen.core
LOG = logging.getLogger(__name__)
def run_interpreter_discovery_if_necessary(s, task_vars, action, rediscover_python):
"""
Triggers ansible python interpreter discovery if requested.
@ -208,6 +213,12 @@ class Spec(with_metaclass(abc.ABCMeta, object)):
:data:`True` if privilege escalation should be active.
"""
@abc.abstractmethod
def become_flags(self):
"""
The command line arguments passed to the become executable.
"""
@abc.abstractmethod
def become_method(self):
"""
@ -285,10 +296,9 @@ class Spec(with_metaclass(abc.ABCMeta, object)):
@abc.abstractmethod
def sudo_args(self):
"""
The list of additional arguments that should be included in a become
The list of additional arguments that should be included in a sudo
invocation.
"""
# TODO: split out into sudo_args/become_args.
@abc.abstractmethod
def mitogen_via(self):
@ -412,6 +422,43 @@ class PlayContextSpec(Spec):
# used to run interpreter discovery
self._action = connection._action
def _become_option(self, name):
plugin = self._connection.become
try:
return plugin.get_option(name, self._task_vars, self._play_context)
except AttributeError:
# A few ansible_mitogen connection plugins look more like become
# plugins. They don't quite fit Ansible's plugin.get_option() API.
# https://github.com/mitogen-hq/mitogen/issues/1173
fallback_plugins = {'mitogen_doas', 'mitogen_sudo', 'mitogen_su'}
if self._connection.transport not in fallback_plugins:
raise
fallback_options = {
'become_exe',
'become_flags',
}
if name not in fallback_options:
raise
LOG.info(
'Used fallback=PlayContext.%s for plugin=%r, option=%r',
name, self._connection, name,
)
return getattr(self._play_context, name)
def _connection_option(self, name, fallback_attr=None):
try:
return self._connection.get_option(name, hostvars=self._task_vars)
except KeyError:
if fallback_attr is None:
fallback_attr = name
LOG.info(
'Used fallback=PlayContext.%s for plugin=%r, option=%r',
fallback_attr, self._connection, name,
)
return getattr(self._play_context, fallback_attr)
def transport(self):
return self._transport
@ -419,40 +466,31 @@ class PlayContextSpec(Spec):
return self._inventory_name
def remote_addr(self):
return self._play_context.remote_addr
return self._connection_option('host', fallback_attr='remote_addr')
def remote_user(self):
return self._play_context.remote_user
return self._connection_option('remote_user')
def become(self):
return self._play_context.become
return self._connection.become
def become_flags(self):
return self._become_option('become_flags')
def become_method(self):
return self._play_context.become_method
return self._connection.become.name
def become_user(self):
return self._play_context.become_user
return self._become_option('become_user')
def become_pass(self):
# become_pass is owned/provided by the active become plugin. However
# PlayContext is intertwined with it. Known complications
# - ansible_become_password is higher priority than ansible_become_pass,
# `play_context.become_pass` doesn't obey this (atleast with Mitgeon).
# - `meta: reset_connection` runs `connection.reset()` but
# `ansible_mitogen.connection.Connection.reset()` recreates the
# connection object, setting `connection.become = None`.
become_plugin = self._connection.become
try:
become_pass = become_plugin.get_option('become_pass', playcontext=self._play_context)
except AttributeError:
become_pass = self._play_context.become_pass
return optional_secret(become_pass)
return optional_secret(self._become_option('become_pass'))
def password(self):
return optional_secret(self._play_context.password)
return optional_secret(self._connection_option('password'))
def port(self):
return self._play_context.port
return self._connection_option('port')
def python_path(self, rediscover_python=False):
s = self._connection.get_task_var('ansible_python_interpreter')
@ -466,18 +504,13 @@ class PlayContextSpec(Spec):
rediscover_python=rediscover_python)
def host_key_checking(self):
def candidates():
yield self._connection.get_task_var('ansible_ssh_host_key_checking')
yield self._connection.get_task_var('ansible_host_key_checking')
yield C.HOST_KEY_CHECKING
val = next((v for v in candidates() if v is not None), True)
return boolean(val)
return self._connection_option('host_key_checking')
def private_key_file(self):
return self._play_context.private_key_file
return self._connection_option('private_key_file')
def ssh_executable(self):
return C.config.get_config_value("ssh_executable", plugin_type="connection", plugin_name="ssh", variables=self._task_vars.get("vars", {}))
return self._connection_option('ssh_executable')
def timeout(self):
return self._play_context.timeout
@ -490,42 +523,21 @@ class PlayContextSpec(Spec):
)
def ssh_args(self):
local_vars = self._task_vars.get("hostvars", {}).get(self._inventory_name, {})
return [
mitogen.core.to_text(term)
for s in (
C.config.get_config_value("ssh_args", plugin_type="connection", plugin_name="ssh", variables=local_vars),
C.config.get_config_value("ssh_common_args", plugin_type="connection", plugin_name="ssh", variables=local_vars),
C.config.get_config_value("ssh_extra_args", plugin_type="connection", plugin_name="ssh", variables=local_vars)
self._connection_option('ssh_args'),
self._connection_option('ssh_common_args'),
self._connection_option('ssh_extra_args'),
)
for term in ansible.utils.shlex.shlex_split(s or '')
]
def become_exe(self):
# In Ansible 2.8, PlayContext.become_exe always has a default value due
# to the new options mechanism. Previously it was only set if a value
# ("somewhere") had been specified for the task.
# For consistency in the tests, here we make older Ansibles behave like
# newer Ansibles.
exe = self._play_context.become_exe
if exe is None and self._play_context.become_method == 'sudo':
exe = 'sudo'
return exe
return self._become_option('become_exe')
def sudo_args(self):
return [
mitogen.core.to_text(term)
for term in ansible.utils.shlex.shlex_split(
first_true((
self._play_context.become_flags,
# Ansible <=2.7.
getattr(self._play_context, 'sudo_flags', ''),
# Ansible <=2.3.
getattr(C, 'DEFAULT_BECOME_FLAGS', ''),
getattr(C, 'DEFAULT_SUDO_FLAGS', '')
), default='')
)
]
return ansible.utils.shlex.shlex_split(self.become_flags() or '')
def mitogen_via(self):
return self._connection.get_task_var('mitogen_via')
@ -660,6 +672,9 @@ class MitogenViaSpec(Spec):
def become(self):
return bool(self._become_user)
def become_flags(self):
return self._host_vars.get('ansible_become_flags')
def become_method(self):
return (
self._become_method or
@ -678,6 +693,7 @@ class MitogenViaSpec(Spec):
def password(self):
return optional_secret(
self._host_vars.get('ansible_ssh_password') or
self._host_vars.get('ansible_ssh_pass') or
self._host_vars.get('ansible_password')
)
@ -754,7 +770,7 @@ class MitogenViaSpec(Spec):
mitogen.core.to_text(term)
for s in (
self._host_vars.get('ansible_sudo_flags') or '',
self._host_vars.get('ansible_become_flags') or '',
self.become_flags() or '',
)
for term in ansible.utils.shlex.shlex_split(s)
]

@ -132,13 +132,13 @@ Noteworthy Differences
| 5 | 3.8 - 3.11 |
+-----------------+-----------------+
| 6 | |
+-----------------+ 3.8 - 3.12 |
+-----------------+ 3.8 - 3.13 |
| 7 | |
+-----------------+-----------------+
| 8 | 3.9 - 3.12 |
| 8 | 3.9 - 3.13 |
+-----------------+-----------------+
| 9 | |
+-----------------+ 3.10 - 3.12 |
+-----------------+ 3.10 - 3.13 |
| 10 | |
+-----------------+-----------------+
@ -306,7 +306,8 @@ container.
* Intermediary machines cannot use login and become passwords that were
supplied to Ansible interactively. If an intermediary requires a
password, it must be supplied via ``ansible_ssh_pass``,
``ansible_password``, or ``ansible_become_pass`` inventory variables.
``ansible_ssh_password``, ``ansible_password``, or
``ansible_become_pass`` inventory variables.
* Automatic tunnelling of SSH-dependent actions, such as the
``synchronize`` module, is not yet supported. This will be addressed in a
@ -1011,7 +1012,8 @@ Like the :ans:conn:`ssh` except connection delegation is supported.
* ``ansible_port``, ``ssh_port``
* ``ansible_ssh_executable``, ``ssh_executable``
* ``ansible_ssh_private_key_file``
* ``ansible_ssh_pass``, ``ansible_password`` (default: assume passwordless)
* ``ansible_ssh_pass``, ``ansible_ssh_password``, ``ansible_password``
(default: assume passwordless)
* ``ssh_args``, ``ssh_common_args``, ``ssh_extra_args``
* ``mitogen_mask_remote_name``: if :data:`True`, mask the identity of the
Ansible controller process on remote machines. To simplify diagnostics,

@ -18,7 +18,89 @@ To avail of fixes in an unreleased version, please download a ZIP file
`directly from GitHub <https://github.com/mitogen-hq/mitogen/>`_.
v0.3.11 (2024-10-30)
In progress (unreleased)
------------------------
v0.3.18 (2024-11-07)
--------------------
* :gh:issue:`1083` :mod:`ansible_mitogen`: Templated become method
(e.g. ``ansible_become_method``).
* :gh:issue:`1083` :mod:`ansible_mitogen`: Templated become flag
(e.g. ``ansible_become_method``, ``become`` keyword).
v0.3.17 (2024-11-07)
--------------------
* :gh:issue:`1182` CI: Fix incorrect world readable/writable file permissions
on SSH key ``mitogen__has_sudo_pubkey.key`` during Ansible tests.
* :gh:issue:`1083` :mod:`ansible_mitogen`: Templated SSH private key file
(e.g. ``ansible_private_key_file``).
* :gh:issue:`1083` :mod:`ansible_mitogen`: Templated SSH host key checking
(e.g. ``ansible_host_key_checking``, ``ansible_ssh_host_key_checking``).
* :gh:issue:`1083` :mod:`ansible_mitogen`: Templated host address
(e.g. ``ansible_host``, ``ansible_ssh_host``)
* :gh:issue:`1184` Test templated SSH host key checking in task vars
v0.3.16 (2024-11-05)
--------------------
* :gh:issue:`1083` :mod:`ansible_mitogen`: Templated become executable
(e.g. ``become_exe``).
* :gh:issue:`1083` :mod:`ansible_mitogen`: Templated become executable
arguments (e.g. ``become_flags``).
* :gh:issue:`1083` :mod:`ansible_mitogen`: Templated ssh executable
(``ansible_ssh_executable``).
* :gh:issue:`1083` :mod:`ansible_mitogen`: Fixed templated connection options
during a ``meta: reset_connection`` task.
* :gh:issue:`1129` CI: Migrated macOS 12 runners to macOS 13, due to EOL.
v0.3.15 (2024-10-28)
--------------------
* :gh:issue:`905` :mod:`ansible_mitogen`: Support templated SSH command
arguments (e.g. ``ansible_ssh_args``, ``ansible_ssh_extra_args``).
* :gh:issue:`692` tests: Fix and re-enable several sudo tests
* :gh:issue:`1083` :mod:`ansible_mitogen`: Support templated become password
(e.g. ``ansible_become_pass``, ``ansible_sudo_pass``)
v0.3.14 (2024-10-16)
--------------------
* :gh:issue:`1159` CI: Reduce number of Jobs by parameterizing Mitogen Docker
SSH tests
* :gh:issue:`1083` :mod:`ansible_mitogen`: Support templated become username.
v0.3.13 (2024-10-09)
--------------------
* :gh:issue:`1138` CI: Complete migration from Azure DevOps Pipelines to
GitHub Actions
* :gh:issue:`1116` :mod:`ansible_mitogen`: Support for templated variable
`ansible_ssh_user`.
* :gh:issue:`978` :mod:`ansible_mitogen`: Support templated Ansible SSH port.
* :gh:issue:`1073` Python 3.13 support
v0.3.12 (2024-10-07)
--------------------
* :gh:issue:`1106` :mod:`ansible_mitogen`: Support for `ansible_ssh_password`
connection variable, and templated SSH connection password.
* :gh:issue:`1136` tests: Improve Ansible fail_msg formatting.
* :gh:issue:`1137` tests: Ignore inventory files of inactive tests & benchmarks
* :gh:issue:`1138` CI: Add re-actors/alls-green GitHub Actions job to simplify
branch protections configuration.
v0.3.11 (2024-09-30)
--------------------
* :gh:issue:`1127` :mod:`mitogen`: Consolidate mitogen backward compatibility

@ -127,11 +127,14 @@ sponsorship and outstanding future-thinking of its early adopters.
<li>jgadling</li>
<li>John F Wall &mdash; <em>Making Ansible Great with Massive Parallelism</em></li>
<li><a href="https://github.com/jrosser">Jonathan Rosser</a></li>
<li><a href="https://github.com/jmkeyes">Joshua M. Keyes</a></li>
<li>KennethC</li>
<li><a href="https://github.com/lberruti">Luca Berruti</li>
<li>Lewis Bellwood &mdash; <em>Happy to be apart of a great project.</em></li>
<li>luto</li>
<li><a href="https://mayeu.me/">Mayeu a.k.a Matthieu Maury</a></li>
<li><a href="https://github.com/madsi1m">Michael D'Silva</a></li>
<li><a href="https://github.com/mordekasg">mordek</a></li>
<li><a href="https://twitter.com/nathanhruby">@nathanhruby</a></li>
<li><a href="https://github.com/opoplawski">Orion Poplawski</a></li>
<li><a href="https://github.com/philfry">Philippe Kueck</a></li>

@ -10,7 +10,6 @@ from __future__ import print_function
import hashlib
import io
import os
import spwd
import mitogen.core
import mitogen.master
@ -57,21 +56,6 @@ def streamy_download_file(context, path):
}
def get_password_hash(username):
"""
Fetch a user's password hash.
"""
try:
h = spwd.getspnam(username)
except KeyError:
return None
# mitogen.core.Secret() is a Unicode subclass with a repr() that hides the
# secret data. This keeps secret stuff out of logs. Like blobs, secrets can
# also be serialized.
return mitogen.core.Secret(h)
def md5sum(path):
"""
Return the MD5 checksum for a file.

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

@ -79,6 +79,7 @@ setup(
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.13',
'Programming Language :: Python :: Implementation :: CPython',
'Topic :: System :: Distributed Computing',
'Topic :: System :: Systems Administration',

@ -7,7 +7,7 @@ started in September 2017. Pull requests in this area are very welcome!
## Running The Tests
[![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)
[![Build Status](https://img.shields.io/github/actions/workflow/status/mitogen-hq/mitogen/tests.yml?branch=master)](https://github.com/mitogen-hq/mitogen/actions?query=branch%3Amaster)
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
@ -30,11 +30,19 @@ and run the tests there.
1. Run ``test``
# Selecting a target distribution
# Selecting target distributions
Docker target images exist for testing against CentOS and Debian, with the
default being Debian. To select CentOS, specify `MITOGEN_TEST_DISTRO=centos` in
the environment.
Linux container images for testing are available at
- https://github.com/orgs/mitogen-hq/packages
- https://public.ecr.aws/n5z0e8q9
The images used are determined by two environment variables
- `MITOGEN_TEST_DISTRO_SPECS`
- `MITOGEN_TEST_IMAGE_TEMPLATE`
Defaults for these can be found in `.ci/ci_lib.py` & `tests/testlib.py`
# User Accounts

@ -48,6 +48,7 @@ host_key_checking = False
[inventory]
any_unparsed_is_failed = true
host_pattern_mismatch = error
ignore_extensions = ~, .bak, .disabled
[callback_profile_tasks]
task_output_limit = 10

@ -4,8 +4,7 @@
# When running the tests outside CI, make a single 'target' host which is the
# local machine. The ansible_user override is necessary since some tests want a
# fixed ansible.cfg remote_user setting to test against.
# FIXME Hardcoded by replacement in some CI runs https://github.com/mitogen-hq/mitogen/issues/1022
# and os.environ['USER'] is not populated on Azure macOS runners.
# os.environ['USER'] is an empty string on GitHub Actions macOS runners.
target ansible_host=localhost ansible_user="{{ lookup('pipe', 'whoami') }}"
[test-targets]
@ -18,4 +17,35 @@ ssh-common-args ansible_host=localhost ansible_user="{{ lookup('pipe', 'whoami')
[issue905:vars]
ansible_ssh_common_args=-o PermitLocalCommand=yes -o LocalCommand="touch {{ ssh_args_canary_file }}"
ssh_args_canary_file=/tmp/ssh_args_{{ inventory_hostname }}
ssh_args_canary_file=/tmp/ssh_args_by_inv_{{ inventory_hostname }}
[tt_targets_bare]
tt-bare
[tt_become_bare]
tt-become-bare
[tt_become_bare:vars]
ansible_host=localhost
ansible_user="{{ lookup('pipe', 'whoami') }}"
[tt_become_by_inv]
tt-become ansible_become="{{ 'true' | trim }}" ansible_become_user=root
tt-become-exe ansible_become=true ansible_become_exe="{{ 'sudo' | trim }}" ansible_become_user=root
tt-become-flags ansible_become=true ansible_become_flags="{{ '--set-home --stdin --non-interactive' | trim }}" ansible_become_user=root
tt-become-method ansible_become=true ansible_become_method="{{ 'sudo' | trim }}" ansible_become_user=root
tt-become-pass ansible_become=true ansible_become_pass="{{ 'pw_required_password' | trim }}" ansible_become_user=mitogen__pw_required
tt-become-user ansible_become=true ansible_become_user="{{ 'root' | trim }}"
[tt_become_by_inv:vars]
ansible_host=localhost
ansible_user="{{ lookup('pipe', 'whoami') }}"
[tt_targets_inventory]
tt-host ansible_host="{{ 'localhost' | trim }}" ansible_password=has_sudo_nopw_password ansible_user=mitogen__has_sudo_nopw
tt-host-key-checking ansible_host=localhost ansible_host_key_checking="{{ 'false' | trim }}" ansible_password=has_sudo_nopw_password ansible_user=mitogen__has_sudo_nopw
tt-password ansible_host=localhost ansible_password="{{ 'has_sudo_nopw_password' | trim }}" ansible_user=mitogen__has_sudo_nopw
tt-port ansible_host=localhost ansible_password=has_sudo_nopw_password ansible_port="{{ 22 | int }}" ansible_user=mitogen__has_sudo_nopw
tt-private-key-file ansible_host=localhost ansible_private_key_file="{{ git_basedir }}/tests/data/docker/mitogen__has_sudo_pubkey.key" ansible_user=mitogen__has_sudo_pubkey
tt-remote-user ansible_host=localhost ansible_password=has_sudo_nopw_password ansible_user="{{ 'mitogen__has_sudo_nopw' | trim }}"
tt-ssh-executable ansible_host=localhost ansible_password=has_sudo_nopw_password ansible_ssh_executable="{{ 'ssh' | trim }}" ansible_user=mitogen__has_sudo_nopw

@ -0,0 +1,18 @@
# Ansible removed its default SSH port in May 2021, defering to the SSH
# implementation.
# https://github.com/ansible/ansible/commit/45618a6f3856f7332df8afe4adc40d85649a70da
# Careful templating is needed to preseve the type(s) of expected_ssh_port,
# particularly in combination with the assert_equal action plugin.
# Do: {{ expected_ssh_port }}
# Don't: {{ expected_ssh_port | any_filter }}
# Don't: {% if ...%}{{ expected_ssh_port }}{% else %}...{% endif %}
# https://stackoverflow.com/questions/66102524/ansible-set-fact-type-cast/66104814#66104814
- set_fact:
expected_ssh_port: null
when: ansible_version.full is version('2.11.1', '>=', strict=True)
- set_fact:
expected_ssh_port: 22
when: ansible_version.full is version('2.11.1', '<', strict=True)

@ -77,7 +77,8 @@
that:
- item.stat.checksum == item.item.expected_checksum
quiet: true # Avoid spamming stdout with 400 kB of item.item.content
fail_msg: item={{ item }}
fail_msg: |
item={{ item }}
with_items: "{{ stat.results }}"
loop_control:
label: "{{ item.stat.path }}"

@ -16,7 +16,8 @@
- assert:
that:
- out.stat.mode in ("0644", "0664")
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
- name: "Copy files from content: arg"
copy:
@ -29,7 +30,8 @@
- assert:
that:
- out.stat.mode == "0400"
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
- name: Cleanup local weird mode file
file:
@ -55,7 +57,8 @@
- assert:
that:
- out.stat.mode in ("0644", "0664")
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
- name: Copy file with weird mode, preserving mode
copy:
@ -69,7 +72,8 @@
- assert:
that:
- out.stat.mode == "1462"
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
- name: Copy file with weird mode, explicit mode
copy:
@ -84,7 +88,8 @@
- assert:
that:
- out.stat.mode == "1461"
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
- name: Cleanup
file:

@ -15,7 +15,8 @@
- 'raw.rc == 0'
- 'raw.stdout_lines[-1]|to_text == "2"'
- 'raw.stdout[-1]|to_text == "2"'
fail_msg: raw={{raw}}
fail_msg: |
raw={{ raw }}
- name: Run raw module with sudo
become: true
@ -39,6 +40,7 @@
["root\r\n"],
["root"],
)
fail_msg: raw={{raw}}
fail_msg: |
raw={{ raw }}
tags:
- low_level_execute_command

@ -42,13 +42,17 @@
assert:
that:
- good_temp_path == good_temp_path2
fail_msg: good_temp_path={{good_temp_path}} good_temp_path2={{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}}
fail_msg: |
tmp_path={{ tmp_path }}
tmp_path2={{ tmp_path2 }}
#
# Verify subdirectory removal.
@ -69,7 +73,9 @@
that:
- not stat1.stat.exists
- not stat2.stat.exists
fail_msg: stat1={{stat1}} stat2={{stat2}}
fail_msg: |
stat1={{ stat1 }}
stat2={{ stat2 }}
#
# Verify good directory persistence.
@ -84,7 +90,8 @@
assert:
that:
- stat.stat.exists
fail_msg: stat={{stat}}
fail_msg: |
stat={{ stat }}
#
# Write some junk into the temp path.
@ -107,7 +114,8 @@
- assert:
that:
- not out.stat.exists
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
#
# root
@ -126,23 +134,24 @@
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}}
fail_msg: |
tmp_path_root={{ tmp_path_root }}
tmp_path2={{ tmp_path2 }}
#
# readonly homedir
#
# TODO: https://github.com/dw/mitogen/issues/692
# - name: "Try writing to temp directory for the readonly_homedir user"
# become: true
# become_user: mitogen__readonly_homedir
# custom_python_run_script:
# script: |
# from ansible.module_utils.basic import get_module_path
# path = get_module_path() + '/foo.txt'
# result['path'] = path
# open(path, 'w').write("bar")
# register: tmp_path
- name: Try writing to temp directory for the readonly_homedir user
become: true
become_user: mitogen__readonly_homedir
custom_python_run_script:
script: |
from ansible.module_utils.basic import get_module_path
path = get_module_path() + '/foo.txt'
result['path'] = path
open(path, 'w').write("bar")
register: tmp_path
#
# modules get the same base dir
@ -157,7 +166,8 @@
that:
- out.module_path.startswith(good_temp_path2)
- out.module_tmpdir.startswith(good_temp_path2)
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
tags:
- make_tmp_path
- mitogen_only

@ -26,7 +26,8 @@
register: out
- assert:
that: out.result == user_facts.ansible_facts.ansible_user_dir ~ '/foo'
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
- name: "Expand ~/foo with become active. ~ is become_user's home."
action_passthrough:
@ -49,7 +50,8 @@
register: out
- assert:
that: out.result == user_facts.ansible_facts.ansible_user_dir ~ '/foo'
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
- name: "Expanding $HOME/foo has no effect."
action_passthrough:
@ -60,7 +62,8 @@
register: out
- assert:
that: out.result == '$HOME/foo'
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
# ------------------------
@ -73,7 +76,8 @@
register: out
- assert:
that: out.result == user_facts.ansible_facts.ansible_user_dir ~ '/foo'
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
- name: "sudoable; Expand ~/foo with become active. ~ is become_user's home."
action_passthrough:
@ -97,7 +101,8 @@
register: out
- assert:
that: out.result == user_facts.ansible_facts.ansible_user_dir ~ '/foo'
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
- name: "sudoable; Expanding $HOME/foo has no effect."
action_passthrough:
@ -108,6 +113,7 @@
register: out
- assert:
that: out.result == '$HOME/foo'
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
tags:
- remote_expand_user

@ -12,7 +12,8 @@
register: out
- assert:
that: out.result == False
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
- name: Ensure does-exist does
copy:
@ -24,7 +25,8 @@
register: out
- assert:
that: out.result == True
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
- name: Cleanup
file:

@ -23,7 +23,8 @@
- assert:
that:
- not out2.stat.exists
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
- stat:
path: "{{out.src|dirname}}"
@ -32,7 +33,8 @@
- assert:
that:
- not out2.stat.exists
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
- file:
path: /tmp/remove_tmp_path_test

@ -40,11 +40,11 @@
delegate_to: localhost
run_once: true
# TODO: https://github.com/dw/mitogen/issues/692
# - file:
# path: /tmp/sync-test.out
# state: absent
# become: true
- name: Ensure clean slate
become: true
file:
path: /tmp/sync-test.out
state: absent
# exception: File "/tmp/venv/lib/python2.7/site-packages/ansible/plugins/action/__init__.py", line 129, in cleanup
# exception: self._remove_tmp_path(self._connection._shell.tmpdir)
@ -66,17 +66,18 @@
- assert:
that: outout == "item!"
fail_msg: outout={{outout}}
fail_msg: |
outout={{ outout }}
when: False
# TODO: https://github.com/dw/mitogen/issues/692
# - file:
# path: "{{item}}"
# state: absent
# become: true
# with_items:
# - /tmp/synchronize-action-key
# - /tmp/sync-test
# - /tmp/sync-test.out
- name: Cleanup
become: true
file:
path: "{{ item }}"
state: absent
with_items:
- /tmp/synchronize-action-key
- /tmp/sync-test
- /tmp/sync-test.out
tags:
- synchronize

@ -22,7 +22,8 @@
- assert:
that: |
out.content|b64decode == '{"I am JSON": true}'
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
- name: Create text transfer data
action_passthrough:
@ -37,7 +38,8 @@
- assert:
that:
out.content|b64decode == 'I am text.'
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
- name: Cleanup transfer data
file:

@ -33,6 +33,7 @@
- out.results[1].stdout == 'hi-from-job-2'
- out.results[1].rc == 0
- out.results[1].delta > '0:00:05'
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
tags:
- multiple_items_loop

@ -27,7 +27,8 @@
(job.started == 1) and
(job.changed == True) and
(job.finished == 0)
fail_msg: job={{job}}
fail_msg: |
job={{ job }}
- name: busy-poll up to 100000 times
async_status:
@ -52,7 +53,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}}
fail_msg: |
async_out={{ async_out }}
vars:
async_out: "{{result.content|b64decode|from_json}}"
tags:

@ -38,7 +38,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}}
fail_msg: |
async_out={{ async_out }}
vars:
async_out: "{{result.content|b64decode|from_json}}"
tags:

@ -42,14 +42,16 @@
- async_out.start.startswith("20")
- async_out.stderr == "there"
- async_out.stdout == "hi"
fail_msg: async_out={{async_out}}
fail_msg: |
async_out={{ async_out }}
vars:
async_out: "{{result.content|b64decode|from_json}}"
- assert:
that:
- async_out.invocation.module_args.stdin == None
fail_msg: async_out={{async_out}}
fail_msg: |
async_out={{ async_out }}
when:
- ansible_version.full is version('2.4', '>=', strict=True)
vars:

@ -15,7 +15,9 @@
- assert:
that:
- sync_proc1.pid == sync_proc2.pid
fail_msg: sync_proc1={{sync_proc1}} sync_proc2={{sync_proc2}}
fail_msg: |
sync_proc1={{ sync_proc1 }}
sync_proc2={{ sync_proc2 }}
when: is_mitogen
- name: get async process ID.
@ -52,7 +54,9 @@
- 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}}
fail_msg: |
async_result1={{ async_result1 }}
async_result2={{ async_result2 }}
when: is_mitogen
tags:
- runner_new_process

@ -23,7 +23,8 @@
(job1.started == 1) and
(job1.changed == True) and
(job1.finished == 0)
fail_msg: job1={{job1}}
fail_msg: |
job1={{ job1 }}
- name: busy-poll up to 100000 times
async_status:
@ -48,7 +49,8 @@
- result1.start|length == 26
- result1.finished == 1
- result1.rc == 0
fail_msg: result1={{result1}}
fail_msg: |
result1={{ result1 }}
- assert:
that:
@ -56,14 +58,16 @@
- result1.stderr_lines == []
- result1.stdout == "alldone"
- result1.stdout_lines == ["alldone"]
fail_msg: result1={{result1}}
fail_msg: |
result1={{ result1 }}
when:
- ansible_version.full is version('2.8', '>', strict=True) # ansible#51393
- assert:
that:
- result1.failed == False
fail_msg: result1={{result1}}
fail_msg: |
result1={{ result1 }}
when:
- ansible_version.full is version('2.4', '>', strict=True)
tags:

@ -29,7 +29,8 @@
- result.failed == 1
- result.finished == 1
- result.msg == "Job reached maximum time limit of 1 seconds."
fail_msg: result={{result}}
fail_msg: |
result={{ result }}
tags:
- mitogen_only
- runner_timeout_then_polling

@ -56,12 +56,15 @@
that:
- result1.rc == 0
- result2.rc == 0
fail_msg: result1={{result1}} result2={{result2}}
fail_msg: |
result1={{ result1 }}
result2={{ result2 }}
- assert:
that:
- result2.stdout == 'im_alive'
fail_msg: result2={{result2}}
fail_msg: |
result2={{ result2 }}
when:
- ansible_version.full is version('2.8', '>=', strict=True) # ansible#51393
tags:

@ -21,6 +21,7 @@
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}}
fail_msg: |
job1={{ job1 }}
tags:
- runner_with_polling_and_timeout

@ -5,3 +5,7 @@
- import_playbook: sudo_nopassword.yml
- import_playbook: sudo_password.yml
- import_playbook: sudo_requiretty.yml
- import_playbook: templated_by_inv.yml
- import_playbook: templated_by_play_keywords.yml
- import_playbook: templated_by_play_vars.yml
- import_playbook: templated_by_task_keywords.yml

@ -20,7 +20,8 @@
('password is required' in out.msg) or
('password is required' in out.module_stderr)
)
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
when: is_mitogen
@ -40,7 +41,8 @@
('Incorrect su password' in out.msg) or
('su password is incorrect' in out.msg)
)
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
when: is_mitogen
- name: Ensure password su with chdir succeeds
@ -62,7 +64,8 @@
- assert:
that:
- out.stdout == 'mitogen__user1'
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
when:
# CI containers lack `setfacl` for unpriv -> unpriv
# https://github.com/mitogen-hq/mitogen/issues/1118
@ -87,7 +90,8 @@
- assert:
that:
- out.stdout == 'mitogen__user1'
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
when:
# CI containers lack `setfacl` for unpriv -> unpriv
# https://github.com/mitogen-hq/mitogen/issues/1118

@ -20,7 +20,8 @@
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}}
fail_msg: |
out={{ out }}
tags:
- sudo
- sudo_flags_failure

@ -23,7 +23,8 @@
- >-
(out.module_stderr | default(out.module_stdout, true) | default(out.msg, true)) is search('sudo: unknown user:? slartibartfast')
or (ansible_facts.os_family == 'RedHat' and ansible_facts.distribution_version == '6.10')
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
when:
# https://github.com/ansible/ansible/pull/70785
- ansible_facts.distribution not in ["MacOSX"]

@ -11,7 +11,8 @@
- assert:
that:
- out.stdout != 'root'
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
- name: Ensure passwordless sudo to root succeeds.
shell: whoami
@ -22,7 +23,8 @@
- assert:
that:
- out.stdout == 'root'
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
tags:
- sudo
- sudo_nopassword

@ -5,10 +5,12 @@
tasks:
- name: Ensure sudo password absent but required.
shell: whoami
become: true
become_user: mitogen__pw_required
command:
cmd: whoami
register: out
changed_when: false
ignore_errors: true
when:
# https://github.com/ansible/ansible/pull/70785
@ -23,7 +25,8 @@
('Missing sudo password' in out.msg) or
('password is required' in out.module_stderr)
)
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
when:
# https://github.com/ansible/ansible/pull/70785
- ansible_facts.distribution not in ["MacOSX"]
@ -31,10 +34,12 @@
or is_mitogen
- name: Ensure password sudo incorrect.
shell: whoami
become: true
become_user: mitogen__pw_required
command:
cmd: whoami
register: out
changed_when: false
vars:
ansible_become_pass: nopes
ignore_errors: true
@ -50,25 +55,35 @@
('Incorrect sudo password' in out.msg) or
('sudo password is incorrect' in out.msg)
)
fail_msg: out={{out}}
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.
# shell: whoami
# become: true
# become_user: mitogen__pw_required
# register: out
# vars:
# ansible_become_pass: pw_required_password
- block:
- name: Ensure password sudo succeeds
become: true
become_user: mitogen__pw_required
vars:
ansible_become_pass: pw_required_password
command:
cmd: whoami
register: sudo_password_success_whoami
changed_when: false
# - assert:
# that:
# - out.stdout == 'mitogen__pw_required'
- assert:
that:
- sudo_password_success_whoami.stdout == 'mitogen__pw_required'
fail_msg: |
sudo_password_success_whoami={{ sudo_password_success_whoami }}
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_password

@ -3,34 +3,38 @@
- name: integration/become/sudo_requiretty.yml
hosts: test-targets
tasks:
# - include_tasks: ../_mitogen_only.yml
# AIUI Vanilla Ansible cannot do sudo when requiretty configured
- include_tasks: ../_mitogen_only.yml
# TODO: https://github.com/dw/mitogen/issues/692
# - name: Verify we can login to a non-passworded requiretty account
# shell: whoami
# become: true
# become_user: mitogen__require_tty
# register: out
- name: Verify we can login to a non-passworded requiretty account
become: true
become_user: mitogen__require_tty
command:
cmd: whoami
changed_when: false
register: sudo_require_tty_whoami
# - assert:
# that:
# - out.stdout == 'mitogen__require_tty'
- assert:
that:
- sudo_require_tty_whoami.stdout == 'mitogen__require_tty'
fail_msg: |
sudo_require_tty_whoami={{ sudo_require_tty_whoami }}
- name: Verify we can login to a passworded requiretty account
become: true
become_user: mitogen__require_tty_pw_required
vars:
ansible_become_pass: require_tty_pw_required_password
command:
cmd: whoami
changed_when: false
register: sudo_require_tty_password_whoami
# ---------------
# TODO: https://github.com/dw/mitogen/issues/692
# - name: Verify we can login to a passworded requiretty account
# shell: whoami
# become: true
# become_user: mitogen__require_tty_pw_required
# vars:
# ansible_become_pass: require_tty_pw_required_password
# register: out
# - assert:
# that:
# - out.stdout == 'mitogen__require_tty_pw_required'
- assert:
that:
- sudo_require_tty_password_whoami.stdout == 'mitogen__require_tty_pw_required'
fail_msg: |
sudo_require_tty_password_whoami={{ sudo_require_tty_password_whoami }}
tags:
- mitogen_only
- sudo

@ -0,0 +1,34 @@
- name: integration/become/templated_by_inv.yml
hosts: tt_become_by_inv
gather_facts: false
tasks:
- name: Gather facts (avoiding any unprivileged become)
vars:
ansible_become: false
setup:
- meta: reset_connection
- name: Templated become in inventory
vars:
expected_become_users:
tt-become: root
tt-become-exe: root
tt-become-flags: root
tt-become-method: root
tt-become-pass: mitogen__pw_required
tt-become-user: root
command:
cmd: whoami
changed_when: false
check_mode: false
register: become_templated_by_inv_whoami
failed_when:
- become_templated_by_inv_whoami is failed
or become_templated_by_inv_whoami.stdout != expected_become_users[inventory_hostname]
when:
# https://github.com/ansible/ansible/pull/70785
- ansible_become_user in ['root']
or ansible_facts.distribution not in ["MacOSX"]
or ansible_version.full is version("2.11", ">=", strict=True)
or is_mitogen

@ -0,0 +1,53 @@
- name: integration/become/templated_by_play_keywords.yml
hosts: tt_become_bare
gather_facts: false
become: "{{ 'true' | trim }}"
become_exe: "{{ 'sudo' | trim }}"
become_flags: "{{ '--set-home --stdin --non-interactive' | trim }}"
become_method: "{{ 'sudo' | trim }}"
become_user: "{{ 'root' | trim }}"
tasks:
- meta: reset_connection
- name: Templated become by play keywords, no password
command:
cmd: whoami
changed_when: false
check_mode: false
register: become_templated_by_play_keywords_whoami
failed_when:
- become_templated_by_play_keywords_whoami is failed
or become_templated_by_play_keywords_whoami.stdout != 'root'
- name: integration/become/templated_by_play_keywords.yml
hosts: tt_become_bare
gather_facts: false
become: "{{ 'true' | trim }}"
become_exe: "{{ 'sudo' | trim }}"
become_flags: "{{ '--set-home --stdin --non-interactive' | trim }}"
become_method: "{{ 'sudo' | trim }}"
become_user: "{{ 'mitogen__pw_required' | trim }}"
vars:
ansible_become_pass: "{{ 'pw_required_password' | trim }}"
tasks:
- name: Gather facts (avoiding any unprivileged become)
vars:
ansible_become: false
setup:
- meta: reset_connection
- name: Templated become by play keywords, password
command:
cmd: whoami
changed_when: false
check_mode: false
register: become_templated_by_play_keywords_password_whoami
failed_when:
- become_templated_by_play_keywords_password_whoami is failed
or become_templated_by_play_keywords_password_whoami.stdout != 'mitogen__pw_required'
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

@ -0,0 +1,52 @@
- name: integration/become/templated_by_play_vars.yml
hosts: tt_become_bare
gather_facts: false
vars:
ansible_become: true
ansible_become_exe: "{{ 'sudo' | trim }}"
ansible_become_flags: "{{ '--set-home --stdin --non-interactive' | trim }}"
ansible_become_method: "{{ 'sudo' | trim }}"
ansible_become_user: "{{ 'root' | trim }}"
tasks:
- name: Templated become by play vars, no password
command:
cmd: whoami
changed_when: false
check_mode: false
register: become_templated_by_play_vars_whoami
failed_when:
- become_templated_by_play_vars_whoami is failed
or become_templated_by_play_vars_whoami.stdout != 'root'
- name: integration/become/templated_by_play_vars.yml
hosts: tt_become_bare
gather_facts: false
vars:
ansible_become: true
ansible_become_exe: "{{ 'sudo' | trim }}"
ansible_become_flags: "{{ '--set-home --stdin --non-interactive' | trim }}"
ansible_become_method: "{{ 'sudo' | trim }}"
ansible_become_pass: "{{ 'pw_required_password' | trim }}"
ansible_become_user: "{{ 'mitogen__pw_required' | trim }}"
tasks:
- name: Gather facts (avoiding any unprivileged become)
vars:
ansible_become: false
setup:
- meta: reset_connection
- name: Templated become by play vars, password
command:
cmd: whoami
changed_when: false
check_mode: false
register: become_templated_by_play_vars_password_whoami
failed_when:
- become_templated_by_play_vars_password_whoami is failed
or become_templated_by_play_vars_password_whoami.stdout != 'mitogen__pw_required'
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

@ -0,0 +1,83 @@
- name: integration/become/templated_by_task_keywords.yml
hosts: tt_become_bare
gather_facts: false
# FIXME Resetting the connection shouldn't require credentials
# https://github.com/mitogen-hq/mitogen/issues/1132
become: "{{ 'true' | trim }}"
become_exe: "{{ 'sudo' | trim }}"
become_flags: "{{ '--set-home --stdin --non-interactive' | trim }}"
become_method: "{{ 'sudo' | trim }}"
become_user: "{{ 'root' | trim }}"
tasks:
- name: Reset connection to target that will be delegate_to
meta: reset_connection
- name: Test connection template by task keywords, with delegate_to
hosts: test-targets[0]
gather_facts: false
tasks:
- name: Templated become by task keywords, with delegate_to
become: "{{ 'true' | trim }}"
become_exe: "{{ 'sudo' | trim }}"
become_flags: "{{ '--set-home --stdin --non-interactive' | trim }}"
become_method: "{{ 'sudo' | trim }}"
become_user: "{{ 'root' | trim }}"
delegate_to: "{{ groups.tt_become_bare[0] }}"
command:
cmd: whoami
changed_when: false
check_mode: false
register: become_templated_by_task_with_delegate_to_whoami
failed_when:
- become_templated_by_task_with_delegate_to_whoami is failed
or become_templated_by_task_with_delegate_to_whoami.stdout != 'root'
- name: integration/become/templated_by_task_keywords.yml
hosts: tt_become_bare
gather_facts: false
# FIXME Resetting the connection shouldn't require credentials
# https://github.com/mitogen-hq/mitogen/issues/1132
become: "{{ 'true' | trim }}"
become_exe: "{{ 'sudo' | trim }}"
become_flags: "{{ '--set-home --stdin --non-interactive' | trim }}"
become_method: "{{ 'sudo' | trim }}"
become_user: "{{ 'mitogen__pw_required' | trim }}"
vars:
ansible_become_pass: "{{ 'pw_required_password' | trim }}"
tasks:
- name: Reset connection to target that will be delegate_to
meta: reset_connection
- name: Test connection template by task keywords, with delegate_to
hosts: test-targets[0]
gather_facts: false
tasks:
- name: Gather facts (avoiding any unprivileged become)
delegate_to: "{{ groups.tt_become_bare[0] }}"
vars:
ansible_become: false
setup:
- name: Templated become by task keywords, with delegate_to
become: "{{ 'true' | trim }}"
become_exe: "{{ 'sudo' | trim }}"
become_flags: "{{ '--set-home --stdin --non-interactive' | trim }}"
become_method: "{{ 'sudo' | trim }}"
become_user: "{{ 'mitogen__pw_required' | trim }}"
delegate_to: "{{ groups.tt_become_bare[0] }}"
vars:
ansible_become_pass: "{{ 'pw_required_password' | trim }}"
command:
cmd: whoami
changed_when: false
check_mode: false
register: become_templated_by_task_with_delegate_to_password_whoami
failed_when:
- become_templated_by_task_with_delegate_to_password_whoami is failed
or become_templated_by_task_with_delegate_to_password_whoami.stdout != 'mitogen__pw_required'
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

@ -26,4 +26,6 @@
- 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}}
fail_msg: |
original={{ original }}
copied={{ copied }}

@ -18,7 +18,8 @@
- out.result[0].method == "ssh"
- out.result[0].kwargs.username == "joe"
- out.result|length == 1 # no sudo
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
# Now try with a different account.
@ -34,7 +35,8 @@
- out.result[1].method == "sudo"
- out.result[1].kwargs.username == "james"
- out.result|length == 2 # no sudo
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
tags:
- become_same_user
- mitogen_only

@ -29,7 +29,8 @@
that:
- out.rc == 4
- "'Mitogen was disconnected from the remote environment while a call was in-progress.' in out.stdout"
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
tags:
- disconnect
- disconnect_during_module

@ -16,6 +16,7 @@
- out.result[0] == 0
- out.result[1].decode() == "hello, world\r\n"
- out.result[2].decode().startswith("Shared connection to ")
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
tags:
- exec_command

@ -44,7 +44,11 @@
# sudo PID has changed.
- out_become.ppid != out_become2.ppid
fail_msg: out={{out}} out2={{out2}} out_become={{out_become}} out_become2={{out_become2}}
fail_msg: |
out={{ out }}
out2={{ out2 }}
out_become={{ out_become }}
out_become2={{ out_become2 }}
tags:
- mitogen_only
- reset

@ -27,7 +27,9 @@
assert:
that:
- become_acct.pid != login_acct.pid
fail_msg: become_acct={{become_acct}} login_acct={{login_acct}}
fail_msg: |
become_acct={{ become_acct }}
login_acct={{ login_acct }}
- name: reset the connection
meta: reset_connection
@ -40,7 +42,9 @@
assert:
that:
- become_acct.pid != new_become_acct.pid
fail_msg: become_acct={{become_acct}} new_become_acct={{new_become_acct}}
fail_msg: |
become_acct={{ become_acct }}
new_become_acct={{ new_become_acct }}
- name: save new pid of login acct
become: false
@ -51,6 +55,8 @@
assert:
that:
- login_acct.pid != new_login_acct.pid
fail_msg: login_acct={{login_acct}} new_login_acct={{new_login_acct}}
fail_msg: |
login_acct={{ login_acct }}
new_login_acct={{ new_login_acct }}
tags:
- reset_become

@ -11,11 +11,11 @@
- name: integration/connection_delegation/delegate_to_template.yml
vars:
physical_host: "cd-normal-alias"
physical_hosts: ["cd-normal-alias", "cd-normal-normal"]
hosts: test-targets
gather_facts: no
tasks:
- include_tasks: ../_mitogen_only.yml
- include_tasks: ../_expected_ssh_port.yml
- meta: end_play
when:
@ -67,7 +67,7 @@
'keepalive_interval': 30,
'keepalive_count': 10,
'password': null,
'port': null,
'port': '{{ expected_ssh_port }}',
'python_path': ['python3000'],
'remote_name': null,
'ssh_args': [

@ -55,6 +55,7 @@
- hosts: cd-normal
tasks:
- include_tasks: ../_mitogen_only.yml
- include_tasks: ../_expected_ssh_port.yml
- mitogen_get_stack:
delegate_to: cd-alias
register: out
@ -72,7 +73,7 @@
'keepalive_interval': 30,
'keepalive_count': 10,
'password': null,
'port': null,
'port': '{{ expected_ssh_port }}',
"python_path": ["python3000"],
'remote_name': null,
'ssh_args': [
@ -98,6 +99,7 @@
- hosts: cd-alias
tasks:
- include_tasks: ../_mitogen_only.yml
- include_tasks: ../_expected_ssh_port.yml
- mitogen_get_stack:
register: out
- assert_equal:
@ -114,7 +116,7 @@
'keepalive_interval': 30,
'keepalive_count': 10,
'password': null,
'port': null,
'port': '{{ expected_ssh_port }}',
"python_path": ["python3000"],
'remote_name': null,
'ssh_args': [
@ -140,6 +142,7 @@
- hosts: cd-normal-normal
tasks:
- include_tasks: ../_mitogen_only.yml
- include_tasks: ../_expected_ssh_port.yml
- mitogen_get_stack:
register: out
- assert_equal:
@ -167,7 +170,7 @@
'keepalive_interval': 30,
'keepalive_count': 10,
'password': null,
'port': null,
'port': '{{ expected_ssh_port }}',
"python_path": ["python3000"],
'remote_name': null,
'ssh_args': [
@ -193,6 +196,7 @@
- hosts: cd-normal-alias
tasks:
- include_tasks: ../_mitogen_only.yml
- include_tasks: ../_expected_ssh_port.yml
- mitogen_get_stack:
register: out
- assert_equal:
@ -237,7 +241,7 @@
'keepalive_interval': 30,
'keepalive_count': 10,
'password': null,
'port': null,
'port': '{{ expected_ssh_port }}',
"python_path": ["python3000"],
'remote_name': null,
'ssh_args': [
@ -262,6 +266,7 @@
- hosts: cd-newuser-normal-normal
tasks:
- include_tasks: ../_mitogen_only.yml
- include_tasks: ../_expected_ssh_port.yml
- mitogen_get_stack:
register: out
- assert_equal:
@ -289,7 +294,7 @@
'keepalive_interval': 30,
'keepalive_count': 10,
'password': null,
'port': null,
'port': '{{ expected_ssh_port }}',
"python_path": ["python3000"],
'remote_name': null,
'ssh_args': [
@ -315,6 +320,7 @@
- hosts: cd-newuser-normal-normal
tasks:
- include_tasks: ../_mitogen_only.yml
- include_tasks: ../_expected_ssh_port.yml
- mitogen_get_stack:
delegate_to: cd-alias
register: out
@ -332,7 +338,7 @@
'keepalive_interval': 30,
'keepalive_count': 10,
'password': null,
'port': null,
'port': '{{ expected_ssh_port }}',
"python_path": ["python3000"],
'remote_name': null,
'ssh_args': [

@ -11,7 +11,8 @@
- assert:
that: (not not out.mitogen_loaded) == (not not is_mitogen)
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
tags:
- local
- local_blemished

@ -14,7 +14,8 @@
- assert:
that: not out.mitogen_loaded
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
when: False
tags:
- paramiko

@ -11,7 +11,8 @@
- assert:
that: (not not out.mitogen_loaded) == (not not is_mitogen)
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
tags:
- ssh
- ssh_blemished

@ -31,6 +31,8 @@
- assert:
that:
- old_become_env.pid != new_become_env.pid
fail_msg: old_become_env={{old_become_env}} new_become_env={{new_become_env}}
fail_msg: |
old_become_env={{ old_become_env }}
new_become_env={{ new_become_env }}
tags:
- reconnection

@ -17,7 +17,8 @@
- assert:
that:
- out.stdout is match('.*python([0-9.]+)?\(mitogen:[a-z]+@[^:]+:[0-9]+\)')
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
- name: Get cmdline, with mitogen_mask_remote_name
shell: 'cat /proc/$PPID/cmdline | tr \\0 \\n'
@ -29,7 +30,8 @@
- assert:
that:
- out.stdout is match('.*python([0-9.]+)?\(mitogen:ansible\)')
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
tags:
- mitogen_only
- remote_name

@ -46,7 +46,8 @@
- out.failed
- '"Name or service not known" in out.msg or
"Temporary failure in name resolution" in out.msg'
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
when:
- ansible_facts.virtualization_type == "docker"
- ansible_facts.python.version_info[:2] >= [2, 5]

@ -135,7 +135,8 @@
assert:
that:
- legacy.deprecations | default([]) | length > 0
fail_msg: legacy={{legacy}}
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'
@ -146,7 +147,8 @@
assert:
that:
- legacy.warnings | default([]) | length > 0
fail_msg: legacy={{legacy}}
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'
@ -168,7 +170,8 @@
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}}
fail_msg: |
auto_silent_out={{ auto_silent_out }}
- name: test that auto_legacy_silent never warns and got the same answer as auto_legacy
@ -186,7 +189,8 @@
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}}
fail_msg: |
legacy_silent={{ legacy_silent }}
- name: ensure modules can't set discovered_interpreter_X or ansible_X_interpreter
block:
@ -212,7 +216,7 @@
assert:
that:
- auto_out.ansible_facts.discovered_interpreter_python == discovered_interpreter_expected
fail_msg: >-
fail_msg: |
distro={{ distro }}
distro_major= {{ distro_major }}
system={{ system }}

@ -19,6 +19,7 @@
- assert:
that: stat.stat.exists
fail_msg: stat={{stat}}
fail_msg: |
stat={{ stat }}
tags:
- cwd_prseserved

@ -12,6 +12,7 @@
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}}
fail_msg: |
out={{ out }}
tags:
- adjacent_to_playbook

@ -11,6 +11,7 @@
that:
- out.external1_path == "ansible/lib/module_utils/external1.py"
- out.external2_path == "ansible/lib/module_utils/external2.py"
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
tags:
- from_config_path

@ -10,6 +10,7 @@
- assert:
that:
- out.extmod_path == "ansible/lib/module_utils/externalpkg/extmod.py"
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
tags:
- from_config_path

@ -7,4 +7,5 @@
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}}
fail_msg: |
out={{ out }}

@ -6,4 +6,5 @@
- assert:
that:
- out.path == "ansible/integration/module_utils/roles/override_modrole/module_utils/known_hosts.py"
fail_msg: out={{out}}
fail_msg: |
out={{ out }}

@ -13,7 +13,8 @@
- assert:
that: "out.stdout == ''"
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
tags:
- become_flags
@ -29,6 +30,7 @@
- assert:
that: "out2.stdout == '2'"
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
tags:
- become_flags

@ -11,6 +11,7 @@
- assert:
that: "result.stdout == '123'"
fail_msg: result={{result}}
fail_msg: |
result={{ result }}
tags:
- environment

@ -3,30 +3,39 @@
- name: integration/playbook_semantics/with_items.yml
hosts: test-targets
gather_facts: true
tasks:
- block:
- name: Spin up a few interpreters
become: true
vars:
ansible_become_user: "mitogen__user{{ item }}"
command:
cmd: whoami
with_sequence: start=1 end=3
register: first_run
changed_when: false
# TODO: https://github.com/dw/mitogen/issues/692
# - name: Spin up a few interpreters
# shell: whoami
# become: true
# vars:
# ansible_become_user: "mitogen__user{{item}}"
# with_sequence: start=1 end=3
# register: first_run
- name: Reuse them
become: true
vars:
ansible_become_user: "mitogen__user{{ item }}"
command:
cmd: whoami
with_sequence: start=1 end=3
register: second_run
changed_when: false
# - name: Reuse them
# shell: whoami
# become: true
# vars:
# ansible_become_user: "mitogen__user{{item}}"
# with_sequence: start=1 end=3
# register: second_run
# - name: Verify first and second run matches expected username.
# assert:
# that:
# - 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
- name: Verify first and second run matches expected username.
vars:
user_expected: "mitogen__user{{ item | int + 1 }}"
assert:
that:
- first_run.results[item | int].stdout == user_expected
- second_run.results[item | int].stdout == user_expected
with_sequence: start=0 end=2
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

@ -12,7 +12,8 @@
- assert:
that: echo.stdout == ""
fail_msg: echo={{echo}}
fail_msg: |
echo={{ echo }}
- name: Create /etc/environment
copy:
@ -32,7 +33,8 @@
- assert:
that: echo.stdout == "555"
fail_msg: echo={{echo}}
fail_msg: |
echo={{ echo }}
- name: Cleanup /etc/environment
file:
@ -51,4 +53,5 @@
- assert:
that: echo.stdout == ""
fail_msg: echo={{echo}}
fail_msg: |
echo={{ echo }}

@ -10,7 +10,8 @@
- assert:
that: echo.stdout == ""
fail_msg: echo={{echo}}
fail_msg: |
echo={{ echo }}
- name: Copy pam_environment
copy:
@ -23,7 +24,8 @@
- assert:
that: echo.stdout == "321"
fail_msg: echo={{echo}}
fail_msg: |
echo={{ echo }}
- name: Cleanup pam_environment
file:
@ -35,4 +37,5 @@
- assert:
that: echo.stdout == ""
fail_msg: echo={{echo}}
fail_msg: |
echo={{ echo }}

@ -25,6 +25,7 @@
- assert:
that:
- not out.stat.exists
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
tags:
- atexit

@ -16,6 +16,7 @@
out.results[0].item == '1' and
out.results[0].rc == 0 and
(out.results[0].stdout == ansible_nodename)
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
tags:
- builtin_command_module

@ -22,6 +22,7 @@
- (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}}
fail_msg: |
out={{ out }}
tags:
- crashy_new_style_module

@ -22,6 +22,7 @@
(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}}
fail_msg: |
out={{ out }}
tags:
- custom_bash_hashbang_argument

@ -12,6 +12,7 @@
(not out.changed) and
(not out.results[0].changed) and
out.results[0].msg == 'Here is my input'
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
tags:
- custom_bash_old_style_module

@ -11,6 +11,7 @@
(not out.changed) and
(not out.results[0].changed) and
out.results[0].msg == 'Here is my input'
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
tags:
- custom_bash_want_json_module

@ -23,6 +23,7 @@
out.changed and
out.results[0].changed and
out.results[0].msg == 'Hello, world.'
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
tags:
- custom_binary_producing_json

@ -31,6 +31,7 @@
- out.results[0].failed
- out.results[0].msg.startswith('MODULE FAILURE')
- out.results[0].rc == 0
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
tags:
- custom_binary_producing_junk

@ -28,7 +28,8 @@
'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}}
fail_msg: |
out={{ out }}
tags:
- custom_binary_single_null

@ -10,13 +10,15 @@
that:
- out.results[0].input.foo
- out.results[0].message == 'I am a perl script! Here is my input.'
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
- assert:
that:
- (not out.changed)
- (not out.results[0].changed)
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
when:
- ansible_version.full is version('2.4', '>=', strict=True)
tags:

@ -10,13 +10,15 @@
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}}
fail_msg: |
out={{ out }}
- assert:
that:
- (not out.changed)
- (not out.results[0].changed)
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
when:
- ansible_version.full is version('2.4', '>=', strict=True)
tags:

@ -12,6 +12,7 @@
(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}}
fail_msg: |
out={{ out }}
tags:
- custom_python_json_args_module

@ -18,7 +18,8 @@
# 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}}
fail_msg: |
out={{ out }}
tags:
- custom_python_new_style_module
- mitogen_only

@ -17,7 +17,8 @@
# 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}}
fail_msg: |
out={{ out }}
# Verify sys.argv is not Unicode.
- custom_python_detect_environment:
@ -26,7 +27,8 @@
- assert:
that:
- out.argv_types_correct
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
tags:
- custom_python_new_style_module
- mitogen_only

@ -14,7 +14,8 @@
- assert:
that: out.ok
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
tags:
- custom_python_prehistoric_module
- mitogen_only

@ -12,6 +12,7 @@
(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}}
fail_msg: |
out={{ out }}
tags:
- custom_python_want_json_module

@ -14,6 +14,7 @@
(not out.changed) and
(not out.results[0].changed) and
out.results[0].msg == 'Here is my input'
fail_msg: out={{out}}
fail_msg: |
out={{ out }}
tags:
- custom_script_interpreter

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

Loading…
Cancel
Save