Merge branch 'dmw'
- Build matrix simplification - Ansible 2.6 support - New 0-RTT temp dir scheme - don't generate illegal default remote_name - fix connection delegation config issues - /etc/environment emulation - fix LXD calling wrong commandspull/350/head
commit
9fe0a2fbd3
@ -0,0 +1,65 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# Run tests/ansible/all.yml under Ansible and Ansible-Mitogen
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import ci_lib
|
||||||
|
from ci_lib import run
|
||||||
|
|
||||||
|
|
||||||
|
BASE_PORT = 2201
|
||||||
|
TESTS_DIR = os.path.join(ci_lib.GIT_ROOT, 'tests/ansible')
|
||||||
|
HOSTS_DIR = os.path.join(ci_lib.TMP, 'hosts')
|
||||||
|
|
||||||
|
|
||||||
|
with ci_lib.Fold('docker_setup'):
|
||||||
|
for i, distro in enumerate(ci_lib.DISTROS):
|
||||||
|
try:
|
||||||
|
run("docker rm -f target-%s", distro)
|
||||||
|
except: pass
|
||||||
|
|
||||||
|
run("""
|
||||||
|
docker run
|
||||||
|
--rm
|
||||||
|
--detach
|
||||||
|
--publish 0.0.0.0:%s:22/tcp
|
||||||
|
--name=target-%s
|
||||||
|
mitogen/%s-test
|
||||||
|
""", BASE_PORT + i, distro, distro,)
|
||||||
|
|
||||||
|
|
||||||
|
with ci_lib.Fold('job_setup'):
|
||||||
|
os.chdir(TESTS_DIR)
|
||||||
|
os.chmod('../data/docker/mitogen__has_sudo_pubkey.key', int('0600', 7))
|
||||||
|
|
||||||
|
# Don't set -U as that will upgrade Paramiko to a non-2.6 compatible version.
|
||||||
|
run("pip install -q ansible==%s", ci_lib.ANSIBLE_VERSION)
|
||||||
|
|
||||||
|
run("mkdir %s", HOSTS_DIR)
|
||||||
|
run("ln -s %s/common-hosts %s", TESTS_DIR, HOSTS_DIR)
|
||||||
|
|
||||||
|
with open(os.path.join(HOSTS_DIR, 'target'), 'w') as fp:
|
||||||
|
fp.write('[test-targets]\n')
|
||||||
|
for i, distro in enumerate(ci_lib.DISTROS):
|
||||||
|
fp.write("target-%s "
|
||||||
|
"ansible_host=%s "
|
||||||
|
"ansible_port=%s "
|
||||||
|
"ansible_user=mitogen__has_sudo_nopw "
|
||||||
|
"ansible_password=has_sudo_nopw_password"
|
||||||
|
"\n" % (
|
||||||
|
distro,
|
||||||
|
ci_lib.DOCKER_HOSTNAME,
|
||||||
|
BASE_PORT + i,
|
||||||
|
))
|
||||||
|
|
||||||
|
# Build the binaries.
|
||||||
|
run("make -C %s", TESTS_DIR)
|
||||||
|
if not ci_lib.exists_in_path('sshpass'):
|
||||||
|
run("sudo apt-get update")
|
||||||
|
run("sudo apt-get install -y sshpass")
|
||||||
|
|
||||||
|
|
||||||
|
with ci_lib.Fold('ansible'):
|
||||||
|
run('/usr/bin/time ./run_ansible_playbook.sh all.yml -i "%s" %s',
|
||||||
|
HOSTS_DIR, ' '.join(sys.argv[1:]))
|
@ -1,64 +0,0 @@
|
|||||||
#!/bin/bash -ex
|
|
||||||
# Run tests/ansible/all.yml under Ansible and Ansible-Mitogen
|
|
||||||
|
|
||||||
TRAVIS_BUILD_DIR="${TRAVIS_BUILD_DIR:-`pwd`}"
|
|
||||||
TMPDIR="/tmp/ansible-tests-$$"
|
|
||||||
ANSIBLE_VERSION="${VER:-2.6.1}"
|
|
||||||
export ANSIBLE_STRATEGY="${STRATEGY:-mitogen_linear}"
|
|
||||||
DISTRO="${DISTRO:-debian}"
|
|
||||||
|
|
||||||
export PYTHONPATH="${PYTHONPATH}:${TRAVIS_BUILD_DIR}"
|
|
||||||
|
|
||||||
# SSH passes these through to the container when run interactively, causing
|
|
||||||
# stdout to get messed up with libc warnings.
|
|
||||||
unset LANG LC_ALL
|
|
||||||
|
|
||||||
function on_exit()
|
|
||||||
{
|
|
||||||
rm -rf "$TMPDIR"
|
|
||||||
docker kill target || true
|
|
||||||
}
|
|
||||||
|
|
||||||
trap on_exit EXIT
|
|
||||||
mkdir "$TMPDIR"
|
|
||||||
|
|
||||||
|
|
||||||
echo travis_fold:start:docker_setup
|
|
||||||
DOCKER_HOSTNAME="$(python ${TRAVIS_BUILD_DIR}/tests/show_docker_hostname.py)"
|
|
||||||
|
|
||||||
docker run \
|
|
||||||
--rm \
|
|
||||||
--detach \
|
|
||||||
--publish 0.0.0.0:2201:22/tcp \
|
|
||||||
--name=target \
|
|
||||||
mitogen/${DISTRO}-test
|
|
||||||
echo travis_fold:end:docker_setup
|
|
||||||
|
|
||||||
|
|
||||||
echo travis_fold:start:job_setup
|
|
||||||
pip install ansible=="${ANSIBLE_VERSION}"
|
|
||||||
cd ${TRAVIS_BUILD_DIR}/tests/ansible
|
|
||||||
|
|
||||||
chmod go= ${TRAVIS_BUILD_DIR}/tests/data/docker/mitogen__has_sudo_pubkey.key
|
|
||||||
echo '[test-targets]' > ${TMPDIR}/hosts
|
|
||||||
echo \
|
|
||||||
target \
|
|
||||||
ansible_host=$DOCKER_HOSTNAME \
|
|
||||||
ansible_port=2201 \
|
|
||||||
ansible_user=mitogen__has_sudo_nopw \
|
|
||||||
ansible_password=has_sudo_nopw_password \
|
|
||||||
>> ${TMPDIR}/hosts
|
|
||||||
|
|
||||||
# Build the binaries.
|
|
||||||
make -C ${TRAVIS_BUILD_DIR}/tests/ansible
|
|
||||||
|
|
||||||
[ ! "$(type -p sshpass)" ] && sudo apt install -y sshpass
|
|
||||||
|
|
||||||
echo travis_fold:end:job_setup
|
|
||||||
|
|
||||||
|
|
||||||
echo travis_fold:start:ansible
|
|
||||||
/usr/bin/time ./run_ansible_playbook.sh \
|
|
||||||
all.yml \
|
|
||||||
-i "${TMPDIR}/hosts" "$@"
|
|
||||||
echo travis_fold:end:ansible
|
|
@ -0,0 +1,100 @@
|
|||||||
|
|
||||||
|
from __future__ import absolute_import
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import atexit
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import shlex
|
||||||
|
import shutil
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# check_output() monkeypatch cutpasted from testlib.py
|
||||||
|
#
|
||||||
|
|
||||||
|
def subprocess__check_output(*popenargs, **kwargs):
|
||||||
|
# Missing from 2.6.
|
||||||
|
process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
|
||||||
|
output, _ = process.communicate()
|
||||||
|
retcode = process.poll()
|
||||||
|
if retcode:
|
||||||
|
cmd = kwargs.get("args")
|
||||||
|
if cmd is None:
|
||||||
|
cmd = popenargs[0]
|
||||||
|
raise subprocess.CalledProcessError(retcode, cmd)
|
||||||
|
return output
|
||||||
|
|
||||||
|
if not hasattr(subprocess, 'check_output'):
|
||||||
|
subprocess.check_output = subprocess__check_output
|
||||||
|
|
||||||
|
# -----------------
|
||||||
|
|
||||||
|
def _argv(s, *args):
|
||||||
|
if args:
|
||||||
|
s %= args
|
||||||
|
return shlex.split(s)
|
||||||
|
|
||||||
|
|
||||||
|
def run(s, *args, **kwargs):
|
||||||
|
argv = _argv(s, *args)
|
||||||
|
print('Running: %s' % (argv,))
|
||||||
|
return subprocess.check_call(argv, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def get_output(s, *args, **kwargs):
|
||||||
|
argv = _argv(s, *args)
|
||||||
|
print('Running: %s' % (argv,))
|
||||||
|
return subprocess.check_output(argv, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def exists_in_path(progname):
|
||||||
|
return any(os.path.exists(os.path.join(dirname, progname))
|
||||||
|
for dirname in os.environ['PATH'].split(os.pathsep))
|
||||||
|
|
||||||
|
|
||||||
|
class TempDir(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.path = tempfile.mkdtemp(prefix='mitogen_ci_lib')
|
||||||
|
atexit.register(self.destroy)
|
||||||
|
|
||||||
|
def destroy(self, rmtree=shutil.rmtree):
|
||||||
|
rmtree(self.path)
|
||||||
|
|
||||||
|
|
||||||
|
class Fold(object):
|
||||||
|
def __init__(self, name):
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
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__), '..'))
|
||||||
|
DISTROS = os.environ.get('DISTROS', 'debian centos6 centos7').split()
|
||||||
|
TMP = TempDir().path
|
||||||
|
|
||||||
|
os.environ['PYTHONDONTWRITEBYTECODE'] = 'x'
|
||||||
|
os.environ['PYTHONPATH'] = '%s:%s' % (
|
||||||
|
os.environ.get('PYTHONPATH', ''),
|
||||||
|
GIT_ROOT
|
||||||
|
)
|
||||||
|
|
||||||
|
DOCKER_HOSTNAME = subprocess.check_output([
|
||||||
|
sys.executable,
|
||||||
|
os.path.join(GIT_ROOT, 'tests/show_docker_hostname.py'),
|
||||||
|
]).decode().strip()
|
||||||
|
|
||||||
|
# SSH passes these through to the container when run interactively, causing
|
||||||
|
# stdout to get messed up with libc warnings.
|
||||||
|
os.environ.pop('LANG', None)
|
||||||
|
os.environ.pop('LC_ALL', None)
|
@ -0,0 +1,70 @@
|
|||||||
|
# Copyright 2017, David Wilson
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import mitogen.core
|
||||||
|
import mitogen.parent
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Stream(mitogen.parent.Stream):
|
||||||
|
child_is_immediate_subprocess = False
|
||||||
|
create_child_args = {
|
||||||
|
# If lxc finds any of stdin, stdout, stderr connected to a TTY, to
|
||||||
|
# prevent input injection it creates a proxy pty, forcing all IO to be
|
||||||
|
# buffered in <4KiB chunks. So ensure stderr is also routed to the
|
||||||
|
# socketpair.
|
||||||
|
'merge_stdio': True
|
||||||
|
}
|
||||||
|
|
||||||
|
container = None
|
||||||
|
lxc_path = 'lxc'
|
||||||
|
python_path = 'python'
|
||||||
|
|
||||||
|
def construct(self, container, lxc_path=None, **kwargs):
|
||||||
|
super(Stream, self).construct(**kwargs)
|
||||||
|
self.container = container
|
||||||
|
if lxc_path:
|
||||||
|
self.lxc_path = lxc_path
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
super(Stream, self).connect()
|
||||||
|
self.name = u'lxd.' + self.container
|
||||||
|
|
||||||
|
def get_boot_command(self):
|
||||||
|
bits = [
|
||||||
|
self.lxc_path,
|
||||||
|
'exec',
|
||||||
|
'--force-noninteractive',
|
||||||
|
self.container,
|
||||||
|
'--',
|
||||||
|
]
|
||||||
|
return bits + super(Stream, self).get_boot_command()
|
@ -1,10 +1,14 @@
|
|||||||
|
|
||||||
all: \
|
TARGETS+=lib/modules/custom_binary_producing_junk
|
||||||
lib/modules/custom_binary_producing_junk \
|
TARGETS+=lib/modules/custom_binary_producing_json
|
||||||
lib/modules/custom_binary_producing_json
|
|
||||||
|
all: clean $(TARGETS)
|
||||||
|
|
||||||
lib/modules/custom_binary_producing_junk: lib/modules.src/custom_binary_producing_junk.c
|
lib/modules/custom_binary_producing_junk: lib/modules.src/custom_binary_producing_junk.c
|
||||||
$(CC) -o $@ $<
|
$(CC) -o $@ $<
|
||||||
|
|
||||||
lib/modules/custom_binary_producing_json: lib/modules.src/custom_binary_producing_json.c
|
lib/modules/custom_binary_producing_json: lib/modules.src/custom_binary_producing_json.c
|
||||||
$(CC) -o $@ $<
|
$(CC) -o $@ $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(TARGETS)
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
# Execute 'hostname' 100 times in a loop. Loops execute within TaskExecutor
|
||||||
|
# within a single WorkerProcess, each iteration is a fair approximation of the
|
||||||
|
# non-controller overhead involved in executing a task.
|
||||||
|
#
|
||||||
|
# See also: loop-100-tasks.yml
|
||||||
|
#
|
||||||
|
- hosts: all
|
||||||
|
tasks:
|
||||||
|
- command: hostname
|
||||||
|
with_sequence: start=1 end=100
|
@ -0,0 +1,112 @@
|
|||||||
|
# Execute 'hostname' 100 times, using 100 individual tasks. Each task causes a
|
||||||
|
# new WorkerProcess to be forked, along with get_vars() calculation, and in the
|
||||||
|
# Mitogen extension, reestablishment of the UNIX socket connectionto the
|
||||||
|
# multiplexer process.
|
||||||
|
#
|
||||||
|
# It does not measure at least module dependency scanning (cached after first
|
||||||
|
# iteration).
|
||||||
|
#
|
||||||
|
# See also: loop-100-items.yml
|
||||||
|
#
|
||||||
|
- hosts: all
|
||||||
|
tasks:
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
||||||
|
- command: hostname
|
@ -0,0 +1,51 @@
|
|||||||
|
# vim: syntax=dosini
|
||||||
|
|
||||||
|
[connection-delegation-test]
|
||||||
|
cd-bastion
|
||||||
|
cd-rack11 mitogen_via=ssh-user@cd-bastion
|
||||||
|
cd-rack11a mitogen_via=root@cd-rack11
|
||||||
|
cd-rack11a-docker mitogen_via=docker-admin@cd-rack11a ansible_connection=docker
|
||||||
|
|
||||||
|
[connection-delegation-cycle]
|
||||||
|
# Create cycle with Docker container.
|
||||||
|
cdc-bastion mitogen_via=cdc-rack11a-docker
|
||||||
|
cdc-rack11 mitogen_via=ssh-user@cdc-bastion
|
||||||
|
cdc-rack11a mitogen_via=root@cdc-rack11
|
||||||
|
cdc-rack11a-docker mitogen_via=docker-admin@cdc-rack11a ansible_connection=docker
|
||||||
|
|
||||||
|
[conn-delegation]
|
||||||
|
cd-user1 ansible_user=mitogen__user1 ansible_connection=mitogen_sudo mitogen_via=target
|
||||||
|
|
||||||
|
|
||||||
|
# Connection delegation scenarios. It's impossible to connection to them, but
|
||||||
|
# you can inspect the would-be config via "mitogen_get_stack" action.
|
||||||
|
[cd-no-connect]
|
||||||
|
# Normal inventory host, no aliasing.
|
||||||
|
cd-normal ansible_connection=mitogen_doas ansible_user=normal-user
|
||||||
|
# Inventory host that is really a different host.
|
||||||
|
cd-alias ansible_connection=ssh ansible_user=alias-user ansible_host=alias-host
|
||||||
|
|
||||||
|
# Via one normal host.
|
||||||
|
cd-normal-normal mitogen_via=cd-normal
|
||||||
|
# Via one aliased host.
|
||||||
|
cd-normal-alias mitogen_via=cd-alias
|
||||||
|
|
||||||
|
# newuser@host via host with explicit username.
|
||||||
|
cd-newuser-normal-normal mitogen_via=cd-normal ansible_user=newuser-normal-normal-user
|
||||||
|
|
||||||
|
# doas:newuser via host.
|
||||||
|
cd-newuser-doas-normal mitogen_via=cd-normal ansible_connection=mitogen_doas ansible_user=newuser-doas-normal-user
|
||||||
|
|
||||||
|
|
||||||
|
# Connection Delegation issue #340 reproduction.
|
||||||
|
# Path to jails is SSH to H -> mitogen_sudo to root -> jail to J
|
||||||
|
|
||||||
|
[issue340]
|
||||||
|
# 'target' plays the role of the normal host machine H.
|
||||||
|
# 'mitogen__sudo1' plays the role of root@H via mitogen_sudo.
|
||||||
|
# 'mitogen__user1' plays the role of root@J via mitogen__user1.
|
||||||
|
# 'mitogen__user2' plays the role of E, the delgate_to target for certs.
|
||||||
|
|
||||||
|
i340-root ansible_user=mitogen__sudo1 ansible_connection=mitogen_sudo mitogen_via=target
|
||||||
|
i340-jail ansible_user=mitogen__user1 ansible_connection=mitogen_sudo mitogen_via=i340-root
|
||||||
|
i340-certs ansible_user=mitogen__user2 ansible_connection=mitogen_sudo mitogen_via=target
|
@ -1,16 +0,0 @@
|
|||||||
|
|
||||||
[test-targets]
|
|
||||||
localhost
|
|
||||||
|
|
||||||
[connection-delegation-test]
|
|
||||||
cd-bastion
|
|
||||||
cd-rack11 mitogen_via=ssh-user@cd-bastion
|
|
||||||
cd-rack11a mitogen_via=root@cd-rack11
|
|
||||||
cd-rack11a-docker mitogen_via=docker-admin@cd-rack11a ansible_connection=docker
|
|
||||||
|
|
||||||
[connection-delegation-cycle]
|
|
||||||
# Create cycle with Docker container.
|
|
||||||
cdc-bastion mitogen_via=cdc-rack11a-docker
|
|
||||||
cdc-rack11 mitogen_via=ssh-user@cdc-bastion
|
|
||||||
cdc-rack11a mitogen_via=root@cdc-rack11
|
|
||||||
cdc-rack11a-docker mitogen_via=docker-admin@cdc-rack11a ansible_connection=docker
|
|
@ -0,0 +1 @@
|
|||||||
|
../common-hosts
|
@ -0,0 +1,2 @@
|
|||||||
|
[test-targets]
|
||||||
|
target ansible_host=localhost
|
@ -1,63 +1,127 @@
|
|||||||
|
#
|
||||||
|
# Ensure _make_tmp_path returns the same result across invocations for a single
|
||||||
|
# user account, and that the path returned cleans itself up on connection
|
||||||
|
# termination.
|
||||||
|
#
|
||||||
|
# Related bugs prior to the new-style handling:
|
||||||
|
# https://github.com/dw/mitogen/issues/239
|
||||||
|
# https://github.com/dw/mitogen/issues/301
|
||||||
|
|
||||||
- name: integration/action/make_tmp_path.yml
|
- name: integration/action/make_tmp_path.yml
|
||||||
hosts: test-targets
|
hosts: test-targets
|
||||||
any_errors_fatal: true
|
any_errors_fatal: true
|
||||||
tasks:
|
tasks:
|
||||||
- name: "Find out root's homedir."
|
- meta: end_play
|
||||||
# Runs first because it blats regular Ansible facts with junk, so
|
when: not is_mitogen
|
||||||
# non-become run fixes that up.
|
|
||||||
setup: gather_subset=min
|
|
||||||
become: true
|
|
||||||
register: root_facts
|
|
||||||
|
|
||||||
- name: "Find regular homedir"
|
|
||||||
setup: gather_subset=min
|
|
||||||
register: user_facts
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# non-become
|
# non-root
|
||||||
#
|
#
|
||||||
|
|
||||||
- action_passthrough:
|
- name: "Find regular temp path"
|
||||||
|
action_passthrough:
|
||||||
method: _make_tmp_path
|
method: _make_tmp_path
|
||||||
register: out
|
register: tmp_path
|
||||||
|
|
||||||
- assert:
|
- name: "Write some junk in regular temp path"
|
||||||
# This string must match ansible.cfg::remote_tmp
|
shell: hostname > {{tmp_path.result}}/hostname
|
||||||
that: out.result.startswith("{{user_facts.ansible_facts.ansible_user_dir}}/.ansible/mitogen-tests/")
|
|
||||||
|
|
||||||
- stat:
|
- name: "Verify junk did not persist across tasks"
|
||||||
path: "{{out.result}}"
|
stat: path={{tmp_path.result}}/hostname
|
||||||
register: st
|
register: junk_stat
|
||||||
|
|
||||||
- assert:
|
- name: "Verify junk did not persist across tasks"
|
||||||
that: st.stat.exists and st.stat.isdir and st.stat.mode == "0700"
|
assert:
|
||||||
|
that:
|
||||||
|
- not junk_stat.stat.exists
|
||||||
|
|
||||||
- file:
|
- name: "Verify temp path hasn't changed since start"
|
||||||
path: "{{out.result}}"
|
action_passthrough:
|
||||||
state: absent
|
method: _make_tmp_path
|
||||||
|
register: tmp_path2
|
||||||
|
|
||||||
|
- name: "Verify temp path hasn't changed since start"
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- tmp_path2.result == tmp_path.result
|
||||||
|
|
||||||
|
- name: "Verify temp path changes across connection reset"
|
||||||
|
mitogen_shutdown_all:
|
||||||
|
|
||||||
|
- name: "Verify temp path changes across connection reset"
|
||||||
|
action_passthrough:
|
||||||
|
method: _make_tmp_path
|
||||||
|
register: tmp_path2
|
||||||
|
|
||||||
|
- name: "Verify temp path changes across connection reset"
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- tmp_path2.result != tmp_path.result
|
||||||
|
|
||||||
|
- name: "Verify old path disappears across connection reset"
|
||||||
|
stat: path={{tmp_path.result}}
|
||||||
|
register: junk_stat
|
||||||
|
|
||||||
|
- name: "Verify old path disappears across connection reset"
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not junk_stat.stat.exists
|
||||||
|
|
||||||
#
|
#
|
||||||
# become. make_tmp_path() must evaluate HOME in the context of the SSH
|
# root
|
||||||
# user, not the become user.
|
|
||||||
#
|
#
|
||||||
|
|
||||||
- action_passthrough:
|
- name: "Find root temp path"
|
||||||
|
become: true
|
||||||
|
action_passthrough:
|
||||||
method: _make_tmp_path
|
method: _make_tmp_path
|
||||||
register: out
|
register: tmp_path_root
|
||||||
|
|
||||||
|
- name: "Verify root temp path differs from regular path"
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- tmp_path2.result != tmp_path_root.result
|
||||||
|
|
||||||
|
#
|
||||||
|
# readonly homedir
|
||||||
|
#
|
||||||
|
|
||||||
|
- name: "Try writing to temp directory for the readonly_homedir user"
|
||||||
become: true
|
become: true
|
||||||
|
become_user: mitogen__readonly_homedir
|
||||||
|
action_passthrough:
|
||||||
|
method: _make_tmp_path
|
||||||
|
register: tmp_path
|
||||||
|
|
||||||
- assert:
|
- name: "Try writing to temp directory for the readonly_homedir user"
|
||||||
# This string must match ansible.cfg::remote_tmp
|
become: true
|
||||||
that: out.result.startswith("{{user_facts.ansible_facts.ansible_user_dir}}/.ansible/mitogen-tests/")
|
become_user: mitogen__readonly_homedir
|
||||||
|
shell: hostname > {{tmp_path.result}}/hostname
|
||||||
|
|
||||||
- stat:
|
#
|
||||||
path: "{{out.result}}"
|
# modules get the same temp dir
|
||||||
register: st
|
#
|
||||||
|
|
||||||
|
- name: "Verify modules get the same tmpdir as the action plugin"
|
||||||
|
action_passthrough:
|
||||||
|
method: _make_tmp_path
|
||||||
|
register: tmp_path
|
||||||
|
|
||||||
|
- name: "Verify modules get the same tmpdir as the action plugin"
|
||||||
|
custom_python_detect_environment:
|
||||||
|
register: out
|
||||||
|
|
||||||
- assert:
|
# v2.6 related: https://github.com/ansible/ansible/pull/39833
|
||||||
that: st.stat.exists and st.stat.isdir and st.stat.mode == "0700"
|
- name: "Verify modules get the same tmpdir as the action plugin (<2.5)"
|
||||||
|
when: ansible_version.full < '2.5'
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- out.module_path == tmp_path.result
|
||||||
|
- out.module_tmpdir == None
|
||||||
|
|
||||||
- file:
|
- name: "Verify modules get the same tmpdir as the action plugin (>2.5)"
|
||||||
path: "{{out.result}}"
|
when: ansible_version.full > '2.5'
|
||||||
state: absent
|
assert:
|
||||||
|
that:
|
||||||
|
- out.module_path == tmp_path.result
|
||||||
|
- out.module_tmpdir == tmp_path.result
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
- import_playbook: delegate_to_template.yml
|
||||||
|
- import_playbook: stack_construction.yml
|
@ -0,0 +1,41 @@
|
|||||||
|
# Ensure templated delegate_to field works.
|
||||||
|
|
||||||
|
- name: integration/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
|
||||||
|
any_errors_fatal: true
|
||||||
|
tasks:
|
||||||
|
- mitogen_get_stack:
|
||||||
|
delegate_to: "{{ physical_host }}"
|
||||||
|
register: out
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that: |
|
||||||
|
out.result == [
|
||||||
|
{
|
||||||
|
'kwargs': {
|
||||||
|
'check_host_keys': 'ignore',
|
||||||
|
'connect_timeout': 10,
|
||||||
|
'hostname': 'cd-normal-alias',
|
||||||
|
'identity_file': None,
|
||||||
|
'password': None,
|
||||||
|
'port': None,
|
||||||
|
'python_path': None,
|
||||||
|
'ssh_args': [
|
||||||
|
'-o',
|
||||||
|
'ForwardAgent=yes',
|
||||||
|
'-o',
|
||||||
|
'ControlMaster=auto',
|
||||||
|
'-o',
|
||||||
|
'ControlPersist=60s',
|
||||||
|
],
|
||||||
|
'ssh_debug_level': None,
|
||||||
|
'ssh_path': 'ssh',
|
||||||
|
'username': None,
|
||||||
|
},
|
||||||
|
'method': 'ssh',
|
||||||
|
},
|
||||||
|
]
|
@ -0,0 +1,336 @@
|
|||||||
|
# https://github.com/dw/mitogen/issues/251
|
||||||
|
|
||||||
|
# ansible_mitogen.connection internally reinterprets Ansible state into a
|
||||||
|
# 'connection stack' -- this is just a list of dictionaries specifying a
|
||||||
|
# sequence of proxied Router connection methods and their kwargs used to
|
||||||
|
# establish the connection. That list is passed to ContextService, which loops
|
||||||
|
# over the stack specifying via=(None or previous entry) for each connection
|
||||||
|
# method.
|
||||||
|
|
||||||
|
# mitogen_get_stack is a magic action that returns the stack, so we can test
|
||||||
|
# all kinds of scenarios without actually needing a real environmnt.
|
||||||
|
|
||||||
|
# Updating this file? Install 'pprintpp' and hack lib/callbacks/nice_stdout.py
|
||||||
|
# to use it instead of the built-in function, then simply s/'/'/ to get the
|
||||||
|
# cutpasteable formatted dicts below. WARNING: remove the trailing comma from
|
||||||
|
# the result list element, it seems to cause assert to silently succeed!
|
||||||
|
|
||||||
|
|
||||||
|
- name: integration/delegation/stack_construction.yml
|
||||||
|
hosts: cd-normal
|
||||||
|
tasks:
|
||||||
|
# used later for local_action test.
|
||||||
|
- local_action: custom_python_detect_environment
|
||||||
|
register: local_env
|
||||||
|
|
||||||
|
|
||||||
|
- hosts: cd-normal
|
||||||
|
any_errors_fatal: true
|
||||||
|
tasks:
|
||||||
|
- mitogen_get_stack:
|
||||||
|
register: out
|
||||||
|
- assert:
|
||||||
|
that: |
|
||||||
|
out.result == [
|
||||||
|
{
|
||||||
|
"kwargs": {
|
||||||
|
"connect_timeout": 10,
|
||||||
|
"doas_path": None,
|
||||||
|
"password": None,
|
||||||
|
"python_path": ["/usr/bin/python"],
|
||||||
|
"username": "normal-user",
|
||||||
|
},
|
||||||
|
"method": "doas",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
- hosts: cd-normal
|
||||||
|
tasks:
|
||||||
|
- mitogen_get_stack:
|
||||||
|
delegate_to: cd-alias
|
||||||
|
register: out
|
||||||
|
- assert:
|
||||||
|
that: |
|
||||||
|
out.result == [
|
||||||
|
{
|
||||||
|
'kwargs': {
|
||||||
|
'check_host_keys': 'ignore',
|
||||||
|
'connect_timeout': 10,
|
||||||
|
'hostname': 'alias-host',
|
||||||
|
'identity_file': None,
|
||||||
|
'password': None,
|
||||||
|
'port': None,
|
||||||
|
'python_path': None,
|
||||||
|
'ssh_args': [
|
||||||
|
'-o',
|
||||||
|
'ForwardAgent=yes',
|
||||||
|
'-o',
|
||||||
|
'ControlMaster=auto',
|
||||||
|
'-o',
|
||||||
|
'ControlPersist=60s',
|
||||||
|
],
|
||||||
|
'ssh_debug_level': None,
|
||||||
|
'ssh_path': 'ssh',
|
||||||
|
'username': 'alias-user',
|
||||||
|
},
|
||||||
|
'method': 'ssh',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
- hosts: cd-alias
|
||||||
|
tasks:
|
||||||
|
- mitogen_get_stack:
|
||||||
|
register: out
|
||||||
|
- assert:
|
||||||
|
that: |
|
||||||
|
out.result == [
|
||||||
|
{
|
||||||
|
'kwargs': {
|
||||||
|
'check_host_keys': 'ignore',
|
||||||
|
'connect_timeout': 10,
|
||||||
|
'hostname': 'alias-host',
|
||||||
|
'identity_file': None,
|
||||||
|
'password': None,
|
||||||
|
'port': None,
|
||||||
|
'python_path': ['/usr/bin/python'],
|
||||||
|
'ssh_args': [
|
||||||
|
'-o',
|
||||||
|
'ForwardAgent=yes',
|
||||||
|
'-o',
|
||||||
|
'ControlMaster=auto',
|
||||||
|
'-o',
|
||||||
|
'ControlPersist=60s',
|
||||||
|
],
|
||||||
|
'ssh_debug_level': None,
|
||||||
|
'ssh_path': 'ssh',
|
||||||
|
'username': 'alias-user',
|
||||||
|
},
|
||||||
|
'method': 'ssh',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
- hosts: cd-normal-normal
|
||||||
|
tasks:
|
||||||
|
- mitogen_get_stack:
|
||||||
|
register: out
|
||||||
|
- assert:
|
||||||
|
that: |
|
||||||
|
out.result == [
|
||||||
|
{
|
||||||
|
'kwargs': {
|
||||||
|
'connect_timeout': 10,
|
||||||
|
'doas_path': None,
|
||||||
|
'password': None,
|
||||||
|
'python_path': None,
|
||||||
|
'username': 'normal-user',
|
||||||
|
},
|
||||||
|
'method': 'doas',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'kwargs': {
|
||||||
|
'check_host_keys': 'ignore',
|
||||||
|
'connect_timeout': 10,
|
||||||
|
'hostname': 'cd-normal-normal',
|
||||||
|
'identity_file': None,
|
||||||
|
'password': None,
|
||||||
|
'port': None,
|
||||||
|
'python_path': ['/usr/bin/python'],
|
||||||
|
'ssh_args': [
|
||||||
|
'-o',
|
||||||
|
'ForwardAgent=yes',
|
||||||
|
'-o',
|
||||||
|
'ControlMaster=auto',
|
||||||
|
'-o',
|
||||||
|
'ControlPersist=60s',
|
||||||
|
],
|
||||||
|
'ssh_debug_level': None,
|
||||||
|
'ssh_path': 'ssh',
|
||||||
|
'username': None,
|
||||||
|
},
|
||||||
|
'method': 'ssh',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
- hosts: cd-normal-alias
|
||||||
|
tasks:
|
||||||
|
- mitogen_get_stack:
|
||||||
|
register: out
|
||||||
|
- assert:
|
||||||
|
that: |
|
||||||
|
out.result == [
|
||||||
|
{
|
||||||
|
'kwargs': {
|
||||||
|
'check_host_keys': 'ignore',
|
||||||
|
'connect_timeout': 10,
|
||||||
|
'hostname': 'alias-host',
|
||||||
|
'identity_file': None,
|
||||||
|
'password': None,
|
||||||
|
'port': None,
|
||||||
|
'python_path': None,
|
||||||
|
'ssh_args': [
|
||||||
|
'-o',
|
||||||
|
'ForwardAgent=yes',
|
||||||
|
'-o',
|
||||||
|
'ControlMaster=auto',
|
||||||
|
'-o',
|
||||||
|
'ControlPersist=60s',
|
||||||
|
],
|
||||||
|
'ssh_debug_level': None,
|
||||||
|
'ssh_path': 'ssh',
|
||||||
|
'username': 'alias-user',
|
||||||
|
},
|
||||||
|
'method': 'ssh',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'kwargs': {
|
||||||
|
'check_host_keys': 'ignore',
|
||||||
|
'connect_timeout': 10,
|
||||||
|
'hostname': 'cd-normal-alias',
|
||||||
|
'identity_file': None,
|
||||||
|
'password': None,
|
||||||
|
'port': None,
|
||||||
|
'python_path': ['/usr/bin/python'],
|
||||||
|
'ssh_args': [
|
||||||
|
'-o',
|
||||||
|
'ForwardAgent=yes',
|
||||||
|
'-o',
|
||||||
|
'ControlMaster=auto',
|
||||||
|
'-o',
|
||||||
|
'ControlPersist=60s',
|
||||||
|
],
|
||||||
|
'ssh_debug_level': None,
|
||||||
|
'ssh_path': 'ssh',
|
||||||
|
'username': None,
|
||||||
|
},
|
||||||
|
'method': 'ssh',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
- hosts: cd-newuser-normal-normal
|
||||||
|
tasks:
|
||||||
|
- mitogen_get_stack:
|
||||||
|
register: out
|
||||||
|
- assert:
|
||||||
|
that: |
|
||||||
|
out.result == [
|
||||||
|
{
|
||||||
|
'kwargs': {
|
||||||
|
'connect_timeout': 10,
|
||||||
|
'doas_path': None,
|
||||||
|
'password': None,
|
||||||
|
'python_path': None,
|
||||||
|
'username': 'normal-user',
|
||||||
|
},
|
||||||
|
'method': 'doas',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'kwargs': {
|
||||||
|
'check_host_keys': 'ignore',
|
||||||
|
'connect_timeout': 10,
|
||||||
|
'hostname': 'cd-newuser-normal-normal',
|
||||||
|
'identity_file': None,
|
||||||
|
'password': None,
|
||||||
|
'port': None,
|
||||||
|
'python_path': ['/usr/bin/python'],
|
||||||
|
'ssh_args': [
|
||||||
|
'-o',
|
||||||
|
'ForwardAgent=yes',
|
||||||
|
'-o',
|
||||||
|
'ControlMaster=auto',
|
||||||
|
'-o',
|
||||||
|
'ControlPersist=60s',
|
||||||
|
],
|
||||||
|
'ssh_debug_level': None,
|
||||||
|
'ssh_path': 'ssh',
|
||||||
|
'username': 'newuser-normal-normal-user',
|
||||||
|
},
|
||||||
|
'method': 'ssh',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
- hosts: cd-newuser-normal-normal
|
||||||
|
tasks:
|
||||||
|
- mitogen_get_stack:
|
||||||
|
delegate_to: cd-alias
|
||||||
|
register: out
|
||||||
|
- assert:
|
||||||
|
that: |
|
||||||
|
out.result == [
|
||||||
|
{
|
||||||
|
'kwargs': {
|
||||||
|
'check_host_keys': 'ignore',
|
||||||
|
'connect_timeout': 10,
|
||||||
|
'hostname': 'alias-host',
|
||||||
|
'identity_file': None,
|
||||||
|
'password': None,
|
||||||
|
'port': None,
|
||||||
|
'python_path': None,
|
||||||
|
'ssh_args': [
|
||||||
|
'-o',
|
||||||
|
'ForwardAgent=yes',
|
||||||
|
'-o',
|
||||||
|
'ControlMaster=auto',
|
||||||
|
'-o',
|
||||||
|
'ControlPersist=60s',
|
||||||
|
],
|
||||||
|
'ssh_debug_level': None,
|
||||||
|
'ssh_path': 'ssh',
|
||||||
|
'username': 'alias-user',
|
||||||
|
},
|
||||||
|
'method': 'ssh',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
- hosts: cd-newuser-normal-normal
|
||||||
|
tasks:
|
||||||
|
- local_action: mitogen_get_stack
|
||||||
|
register: out
|
||||||
|
- assert:
|
||||||
|
that: |
|
||||||
|
out.result == [
|
||||||
|
{
|
||||||
|
'kwargs': {
|
||||||
|
'python_path': [
|
||||||
|
hostvars['cd-normal'].local_env.sys_executable
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'method': 'local',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
- hosts: cd-newuser-doas-normal
|
||||||
|
tasks:
|
||||||
|
- mitogen_get_stack:
|
||||||
|
register: out
|
||||||
|
- assert:
|
||||||
|
that: |
|
||||||
|
out.result == [
|
||||||
|
{
|
||||||
|
'kwargs': {
|
||||||
|
'connect_timeout': 10,
|
||||||
|
'doas_path': None,
|
||||||
|
'password': None,
|
||||||
|
'python_path': None,
|
||||||
|
'username': 'normal-user',
|
||||||
|
},
|
||||||
|
'method': 'doas',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'kwargs': {
|
||||||
|
'connect_timeout': 10,
|
||||||
|
'doas_path': None,
|
||||||
|
'password': None,
|
||||||
|
'python_path': ['/usr/bin/python'],
|
||||||
|
'username': 'newuser-doas-normal-user',
|
||||||
|
},
|
||||||
|
'method': 'doas',
|
||||||
|
},
|
||||||
|
]
|
@ -1,2 +0,0 @@
|
|||||||
|
|
||||||
- import_playbook: readonly_homedir.yml
|
|
@ -1,20 +0,0 @@
|
|||||||
# https://github.com/dw/mitogen/issues/239
|
|
||||||
# While remote_tmp is used in the context of the SSH user by action code
|
|
||||||
# running on the controller, Ansiballz ignores it and uses the system default
|
|
||||||
# instead.
|
|
||||||
|
|
||||||
- name: integration/remote_tmp/readonly_homedir.yml
|
|
||||||
hosts: test-targets
|
|
||||||
any_errors_fatal: true
|
|
||||||
tasks:
|
|
||||||
- custom_python_detect_environment:
|
|
||||||
become: true
|
|
||||||
become_user: mitogen__readonly_homedir
|
|
||||||
register: out
|
|
||||||
vars:
|
|
||||||
ansible_become_pass: readonly_homedir_password
|
|
||||||
|
|
||||||
- name: Verify system temp directory was used.
|
|
||||||
assert:
|
|
||||||
that:
|
|
||||||
- out.__file__.startswith("/tmp/ansible_")
|
|
@ -0,0 +1,80 @@
|
|||||||
|
# issue #338: ensure /etc/environment is reloaded if it changes.
|
||||||
|
# Actually this test uses ~/.pam_environment, which is using the same logic,
|
||||||
|
# but less likely to brick a development workstation
|
||||||
|
|
||||||
|
- name: integration/runner/etc_environment.yml
|
||||||
|
hosts: test-targets
|
||||||
|
any_errors_fatal: true
|
||||||
|
gather_facts: true
|
||||||
|
tasks:
|
||||||
|
# ~/.pam_environment
|
||||||
|
|
||||||
|
- file:
|
||||||
|
path: ~/.pam_environment
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- shell: echo $MAGIC_PAM_ENV
|
||||||
|
register: echo
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that: echo.stdout == ""
|
||||||
|
|
||||||
|
- copy:
|
||||||
|
dest: ~/.pam_environment
|
||||||
|
content: |
|
||||||
|
MAGIC_PAM_ENV=321
|
||||||
|
|
||||||
|
- shell: echo $MAGIC_PAM_ENV
|
||||||
|
register: echo
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that: echo.stdout == "321"
|
||||||
|
|
||||||
|
- file:
|
||||||
|
path: ~/.pam_environment
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- shell: echo $MAGIC_PAM_ENV
|
||||||
|
register: echo
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that: echo.stdout == ""
|
||||||
|
|
||||||
|
|
||||||
|
# /etc/environment
|
||||||
|
- meta: end_play
|
||||||
|
when: ansible_virtualization_type != "docker"
|
||||||
|
|
||||||
|
- file:
|
||||||
|
path: /etc/environment
|
||||||
|
state: absent
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- shell: echo $MAGIC_ETC_ENV
|
||||||
|
register: echo
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that: echo.stdout == ""
|
||||||
|
|
||||||
|
- copy:
|
||||||
|
dest: /etc/environment
|
||||||
|
content: |
|
||||||
|
MAGIC_ETC_ENV=555
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- shell: echo $MAGIC_ETC_ENV
|
||||||
|
register: echo
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that: echo.stdout == "555"
|
||||||
|
|
||||||
|
- file:
|
||||||
|
path: /etc/environment
|
||||||
|
state: absent
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- shell: echo $MAGIC_ETC_ENV
|
||||||
|
register: echo
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that: echo.stdout == ""
|
@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
- name: integration/runner/missing_module.yml
|
||||||
|
hosts: test-targets
|
||||||
|
connection: local
|
||||||
|
tasks:
|
||||||
|
- connection: local
|
||||||
|
command: |
|
||||||
|
ansible -vvv
|
||||||
|
-i "{{inventory_file}}"
|
||||||
|
test-targets
|
||||||
|
-m missing_module
|
||||||
|
args:
|
||||||
|
chdir: ../..
|
||||||
|
register: out
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that: |
|
||||||
|
'The module missing_module was not found in configured module paths.' in out.stdout
|
@ -0,0 +1,22 @@
|
|||||||
|
"""
|
||||||
|
Fetch the connection configuration stack that would be used to connect to a
|
||||||
|
target, without actually connecting to it.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import ansible_mitogen.connection
|
||||||
|
|
||||||
|
from ansible.plugins.action import ActionBase
|
||||||
|
|
||||||
|
|
||||||
|
class ActionModule(ActionBase):
|
||||||
|
def run(self, tmp=None, task_vars=None):
|
||||||
|
if not isinstance(self._connection,
|
||||||
|
ansible_mitogen.connection.Connection):
|
||||||
|
return {
|
||||||
|
'skipped': True,
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
'changed': True,
|
||||||
|
'result': self._connection._build_stack(),
|
||||||
|
}
|
@ -1,155 +0,0 @@
|
|||||||
|
|
||||||
#
|
|
||||||
# Add users expected by tests to an OS X machine. Assumes passwordless sudo to
|
|
||||||
# root.
|
|
||||||
#
|
|
||||||
# WARNING: this creates non-privilged accounts with pre-set passwords!
|
|
||||||
#
|
|
||||||
|
|
||||||
- hosts: test-targets
|
|
||||||
gather_facts: true
|
|
||||||
become: true
|
|
||||||
tasks:
|
|
||||||
- name: Disable non-localhost SSH for Mitogen users
|
|
||||||
blockinfile:
|
|
||||||
path: /etc/ssh/sshd_config
|
|
||||||
block: |
|
|
||||||
Match User mitogen__* Address !127.0.0.1
|
|
||||||
DenyUsers *
|
|
||||||
|
|
||||||
#
|
|
||||||
# Hashed passwords.
|
|
||||||
#
|
|
||||||
- name: Create Mitogen test group
|
|
||||||
group:
|
|
||||||
name: "mitogen__group"
|
|
||||||
|
|
||||||
- name: Create Mitogen test users
|
|
||||||
user:
|
|
||||||
name: "mitogen__{{item}}"
|
|
||||||
shell: /bin/bash
|
|
||||||
groups: mitogen__group
|
|
||||||
password: "{{ (item + '_password') | password_hash('sha256') }}"
|
|
||||||
with_items:
|
|
||||||
- has_sudo
|
|
||||||
- has_sudo_pubkey
|
|
||||||
- require_tty
|
|
||||||
- pw_required
|
|
||||||
- readonly_homedir
|
|
||||||
- require_tty_pw_required
|
|
||||||
- slow_user
|
|
||||||
when: ansible_system != 'Darwin'
|
|
||||||
|
|
||||||
- name: Create Mitogen test users
|
|
||||||
user:
|
|
||||||
name: "mitogen__user{{item}}"
|
|
||||||
shell: /bin/bash
|
|
||||||
password: "{{ ('user' + item + '_password') | password_hash('sha256') }}"
|
|
||||||
with_sequence: start=1 end=21
|
|
||||||
when: ansible_system != 'Darwin'
|
|
||||||
|
|
||||||
#
|
|
||||||
# Plaintext passwords
|
|
||||||
#
|
|
||||||
- name: Create Mitogen test users
|
|
||||||
user:
|
|
||||||
name: "mitogen__{{item}}"
|
|
||||||
shell: /bin/bash
|
|
||||||
groups: mitogen__group
|
|
||||||
password: "{{item}}_password"
|
|
||||||
with_items:
|
|
||||||
- has_sudo
|
|
||||||
- has_sudo_pubkey
|
|
||||||
- require_tty
|
|
||||||
- pw_required
|
|
||||||
- require_tty_pw_required
|
|
||||||
- readonly_homedir
|
|
||||||
- slow_user
|
|
||||||
when: ansible_system == 'Darwin'
|
|
||||||
|
|
||||||
- name: Create Mitogen test users
|
|
||||||
user:
|
|
||||||
name: "mitogen__user{{item}}"
|
|
||||||
shell: /bin/bash
|
|
||||||
password: "user{{item}}_password"
|
|
||||||
with_sequence: start=1 end=21
|
|
||||||
when: ansible_system == 'Darwin'
|
|
||||||
|
|
||||||
- name: Hide test users from login window.
|
|
||||||
shell: >
|
|
||||||
defaults
|
|
||||||
write
|
|
||||||
/Library/Preferences/com.apple.loginwindow
|
|
||||||
HiddenUsersList
|
|
||||||
-array-add '{{item}}'
|
|
||||||
with_items:
|
|
||||||
- mitogen__require_tty
|
|
||||||
- mitogen__pw_required
|
|
||||||
- mitogen__require_tty_pw_required
|
|
||||||
when: ansible_system == 'Darwin'
|
|
||||||
|
|
||||||
- name: Hide test users from login window.
|
|
||||||
shell: >
|
|
||||||
defaults
|
|
||||||
write
|
|
||||||
/Library/Preferences/com.apple.loginwindow
|
|
||||||
HiddenUsersList
|
|
||||||
-array-add 'mitogen__user{{item}}'
|
|
||||||
with_sequence: start=1 end=21
|
|
||||||
when: ansible_distribution == 'MacOSX'
|
|
||||||
|
|
||||||
- name: Readonly homedir for one account
|
|
||||||
shell: "chown -R root: ~mitogen__readonly_homedir"
|
|
||||||
|
|
||||||
- name: Slow bash profile for one account
|
|
||||||
copy:
|
|
||||||
dest: ~mitogen__slow_user/.{{item}}
|
|
||||||
src: ../data/docker/mitogen__slow_user.profile
|
|
||||||
with_items:
|
|
||||||
- bashrc
|
|
||||||
- profile
|
|
||||||
|
|
||||||
- name: Install pubkey for one account
|
|
||||||
file:
|
|
||||||
path: ~mitogen__has_sudo_pubkey/.ssh
|
|
||||||
state: directory
|
|
||||||
mode: go=
|
|
||||||
owner: mitogen__has_sudo_pubkey
|
|
||||||
|
|
||||||
- name: Install pubkey for one account
|
|
||||||
copy:
|
|
||||||
dest: ~mitogen__has_sudo_pubkey/.ssh/authorized_keys
|
|
||||||
src: ../data/docker/mitogen__has_sudo_pubkey.key.pub
|
|
||||||
mode: go=
|
|
||||||
owner: mitogen__has_sudo_pubkey
|
|
||||||
|
|
||||||
- name: Require a TTY for two accounts
|
|
||||||
lineinfile:
|
|
||||||
path: /etc/sudoers
|
|
||||||
line: "{{item}}"
|
|
||||||
with_items:
|
|
||||||
- Defaults>mitogen__pw_required targetpw
|
|
||||||
- Defaults>mitogen__require_tty requiretty
|
|
||||||
- Defaults>mitogen__require_tty_pw_required requiretty,targetpw
|
|
||||||
|
|
||||||
- name: Require password for two accounts
|
|
||||||
lineinfile:
|
|
||||||
path: /etc/sudoers
|
|
||||||
line: "{{lookup('pipe', 'whoami')}} ALL = ({{item}}) ALL"
|
|
||||||
with_items:
|
|
||||||
- mitogen__pw_required
|
|
||||||
- mitogen__require_tty_pw_required
|
|
||||||
|
|
||||||
- name: Allow passwordless for two accounts
|
|
||||||
lineinfile:
|
|
||||||
path: /etc/sudoers
|
|
||||||
line: "{{lookup('pipe', 'whoami')}} ALL = ({{item}}) NOPASSWD:ALL"
|
|
||||||
with_items:
|
|
||||||
- mitogen__require_tty
|
|
||||||
- mitogen__readonly_homedir
|
|
||||||
|
|
||||||
- name: Allow passwordless for many accounts
|
|
||||||
lineinfile:
|
|
||||||
path: /etc/sudoers
|
|
||||||
line: "{{lookup('pipe', 'whoami')}} ALL = (mitogen__user{{item}}) NOPASSWD:ALL"
|
|
||||||
with_sequence: start=1 end=21
|
|
@ -0,0 +1,14 @@
|
|||||||
|
# issue #332: Ansible 2.6 file.py started defining an excepthook and private
|
||||||
|
# AnsibleModuleError. Ensure file fails correctly.
|
||||||
|
|
||||||
|
- name: regression/issue_332_ansiblemoduleerror_first_occurrence.yml
|
||||||
|
hosts: test-targets
|
||||||
|
tasks:
|
||||||
|
- file: path=/usr/bin/does-not-exist mode='a-s' state=file follow=yes
|
||||||
|
ignore_errors: true
|
||||||
|
register: out
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- out.state == 'absent'
|
||||||
|
- out.msg == 'file (/usr/bin/does-not-exist) is absent, cannot continue'
|
@ -1,120 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
"""
|
|
||||||
Build the Docker images used for testing.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import commands
|
|
||||||
import os
|
|
||||||
import shlex
|
|
||||||
import subprocess
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
|
|
||||||
DEBIAN_DOCKERFILE = r"""
|
|
||||||
FROM debian:stretch
|
|
||||||
RUN apt-get update
|
|
||||||
RUN \
|
|
||||||
apt-get install -y python2.7 openssh-server sudo rsync git strace \
|
|
||||||
libjson-perl python-virtualenv && \
|
|
||||||
apt-get clean && \
|
|
||||||
rm -rf /var/cache/apt
|
|
||||||
"""
|
|
||||||
|
|
||||||
CENTOS6_DOCKERFILE = r"""
|
|
||||||
FROM centos:6
|
|
||||||
RUN yum clean all && \
|
|
||||||
yum -y install -y python2.6 openssh-server sudo rsync git strace sudo \
|
|
||||||
perl-JSON python-virtualenv && \
|
|
||||||
yum clean all && \
|
|
||||||
groupadd sudo && \
|
|
||||||
ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
CENTOS7_DOCKERFILE = r"""
|
|
||||||
FROM centos:7
|
|
||||||
RUN yum clean all && \
|
|
||||||
yum -y install -y python2.7 openssh-server sudo rsync git strace sudo \
|
|
||||||
perl-JSON python-virtualenv && \
|
|
||||||
yum clean all && \
|
|
||||||
groupadd sudo && \
|
|
||||||
ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
DOCKERFILE = r"""
|
|
||||||
COPY data/001-mitogen.sudo /etc/sudoers.d/001-mitogen
|
|
||||||
COPY data/docker/ssh_login_banner.txt /etc/ssh/banner.txt
|
|
||||||
RUN \
|
|
||||||
chsh -s /bin/bash && \
|
|
||||||
mkdir -p /var/run/sshd && \
|
|
||||||
echo i-am-mitogen-test-docker-image > /etc/sentinel && \
|
|
||||||
echo "Banner /etc/ssh/banner.txt" >> /etc/ssh/sshd_config && \
|
|
||||||
groupadd mitogen__sudo_nopw && \
|
|
||||||
useradd -s /bin/bash -m mitogen__has_sudo -G SUDO_GROUP && \
|
|
||||||
useradd -s /bin/bash -m mitogen__has_sudo_pubkey -G SUDO_GROUP && \
|
|
||||||
useradd -s /bin/bash -m mitogen__has_sudo_nopw -G mitogen__sudo_nopw && \
|
|
||||||
useradd -s /bin/bash -m mitogen__webapp && \
|
|
||||||
useradd -s /bin/bash -m mitogen__pw_required && \
|
|
||||||
useradd -s /bin/bash -m mitogen__require_tty && \
|
|
||||||
useradd -s /bin/bash -m mitogen__require_tty_pw_required && \
|
|
||||||
useradd -s /bin/bash -m mitogen__readonly_homedir && \
|
|
||||||
useradd -s /bin/bash -m mitogen__slow_user && \
|
|
||||||
chown -R root: ~mitogen__readonly_homedir && \
|
|
||||||
( for i in `seq 1 21`; do useradd -s /bin/bash -m mitogen__user${i}; done; ) && \
|
|
||||||
( for i in `seq 1 21`; do echo mitogen__user${i}:user${i}_password | chpasswd; done; ) && \
|
|
||||||
( echo 'root:rootpassword' | chpasswd; ) && \
|
|
||||||
( echo 'mitogen__has_sudo:has_sudo_password' | chpasswd; ) && \
|
|
||||||
( echo 'mitogen__has_sudo_pubkey:has_sudo_pubkey_password' | chpasswd; ) && \
|
|
||||||
( echo 'mitogen__has_sudo_nopw:has_sudo_nopw_password' | chpasswd; ) && \
|
|
||||||
( echo 'mitogen__webapp:webapp_password' | chpasswd; ) && \
|
|
||||||
( echo 'mitogen__pw_required:pw_required_password' | chpasswd; ) && \
|
|
||||||
( echo 'mitogen__require_tty:require_tty_password' | chpasswd; ) && \
|
|
||||||
( echo 'mitogen__require_tty_pw_required:require_tty_pw_required_password' | chpasswd; ) && \
|
|
||||||
( echo 'mitogen__readonly_homedir:readonly_homedir_password' | chpasswd; ) && \
|
|
||||||
( echo 'mitogen__slow_user:slow_user_password' | chpasswd; ) && \
|
|
||||||
mkdir ~mitogen__has_sudo_pubkey/.ssh && \
|
|
||||||
( echo '#!/bin/bash\nexec strace -ff -o /tmp/pywrap$$.trace python2.7 "$@"' > /usr/local/bin/pywrap; chmod +x /usr/local/bin/pywrap; )
|
|
||||||
|
|
||||||
COPY data/docker/mitogen__has_sudo_pubkey.key.pub /home/mitogen__has_sudo_pubkey/.ssh/authorized_keys
|
|
||||||
COPY data/docker/mitogen__slow_user.profile /home/mitogen__slow_user/.profile
|
|
||||||
COPY data/docker/mitogen__slow_user.profile /home/mitogen__slow_user/.bashrc
|
|
||||||
|
|
||||||
RUN \
|
|
||||||
chown -R mitogen__has_sudo_pubkey ~mitogen__has_sudo_pubkey && \
|
|
||||||
chmod -R go= ~mitogen__has_sudo_pubkey
|
|
||||||
|
|
||||||
RUN sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
|
|
||||||
RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd
|
|
||||||
|
|
||||||
ENV NOTVISIBLE "in users profile"
|
|
||||||
RUN echo "export VISIBLE=now" >> /etc/profile
|
|
||||||
|
|
||||||
EXPOSE 22
|
|
||||||
CMD ["/usr/sbin/sshd", "-D"]
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def sh(s, *args):
|
|
||||||
if args:
|
|
||||||
s %= tuple(map(commands.mkarg, args))
|
|
||||||
return shlex.split(s)
|
|
||||||
|
|
||||||
|
|
||||||
for (distro, wheel, prefix) in (
|
|
||||||
('debian', 'sudo', DEBIAN_DOCKERFILE),
|
|
||||||
('centos6', 'wheel', CENTOS6_DOCKERFILE),
|
|
||||||
('centos7', 'wheel', CENTOS7_DOCKERFILE),
|
|
||||||
):
|
|
||||||
mydir = os.path.abspath(os.path.dirname(__file__))
|
|
||||||
with tempfile.NamedTemporaryFile(dir=mydir) as dockerfile_fp:
|
|
||||||
dockerfile_fp.write(prefix)
|
|
||||||
dockerfile_fp.write(DOCKERFILE.replace('SUDO_GROUP', wheel))
|
|
||||||
dockerfile_fp.flush()
|
|
||||||
|
|
||||||
subprocess.check_call(sh('docker build %s -t %s -f %s',
|
|
||||||
mydir,
|
|
||||||
'mitogen/%s-test' % (distro,),
|
|
||||||
dockerfile_fp.name
|
|
||||||
))
|
|
@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
os.environ['ORIGINAL_ARGV'] = repr(sys.argv)
|
||||||
|
os.execv(sys.executable, sys.argv[sys.argv.index('--') + 1:])
|
@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
os.environ['ORIGINAL_ARGV'] = repr(sys.argv)
|
||||||
|
os.execv(sys.executable, sys.argv[sys.argv.index('--') + 1:])
|
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
# `image_prep`
|
||||||
|
|
||||||
|
This directory contains Ansible playbooks for building the Docker containers
|
||||||
|
used for testing, or for setting up an OS X laptop so the tests can (mostly)
|
||||||
|
run locally.
|
||||||
|
|
||||||
|
The Docker config is more heavily jinxed to trigger adverse conditions in the
|
||||||
|
code, the OS X config just has the user accounts.
|
||||||
|
|
||||||
|
See ../README.md for a (mostly) description of the accounts created.
|
||||||
|
|
||||||
|
|
||||||
|
## Building the containers
|
||||||
|
|
||||||
|
``./build_docker_images.sh``
|
||||||
|
|
||||||
|
|
||||||
|
## Preparing an OS X box
|
||||||
|
|
||||||
|
WARNING: this creates a ton of accounts with preconfigured passwords. It is
|
||||||
|
generally impossible to restrict remote access to these, so your only option is
|
||||||
|
to disable remote login and sharing.
|
||||||
|
|
||||||
|
``ansible-playbook -b -c local -i localhost, -l localhost setup.yml``
|
@ -0,0 +1,117 @@
|
|||||||
|
|
||||||
|
- hosts: all
|
||||||
|
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;
|
||||||
|
fi
|
||||||
|
|
||||||
|
- hosts: all
|
||||||
|
strategy: mitogen_free
|
||||||
|
# Can't gather facts before here.
|
||||||
|
gather_facts: true
|
||||||
|
vars:
|
||||||
|
distro: "{{ansible_distribution}}"
|
||||||
|
ver: "{{ansible_distribution_major_version}}"
|
||||||
|
|
||||||
|
packages:
|
||||||
|
common:
|
||||||
|
- git
|
||||||
|
- openssh-server
|
||||||
|
- rsync
|
||||||
|
- strace
|
||||||
|
- sudo
|
||||||
|
Debian:
|
||||||
|
"9":
|
||||||
|
- libjson-perl
|
||||||
|
- python-virtualenv
|
||||||
|
CentOS:
|
||||||
|
"6":
|
||||||
|
- perl-JSON
|
||||||
|
"7":
|
||||||
|
- perl-JSON
|
||||||
|
- python-virtualenv
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- when: ansible_virtualization_type != "docker"
|
||||||
|
meta: end_play
|
||||||
|
|
||||||
|
- apt:
|
||||||
|
name: "{{packages.common + packages[distro][ver]}}"
|
||||||
|
state: installed
|
||||||
|
update_cache: true
|
||||||
|
when: distro == "Debian"
|
||||||
|
|
||||||
|
- yum:
|
||||||
|
name: "{{packages.common + packages[distro][ver]}}"
|
||||||
|
state: installed
|
||||||
|
update_cache: true
|
||||||
|
when: distro == "CentOS"
|
||||||
|
|
||||||
|
- command: apt-get clean
|
||||||
|
when: distro == "Debian"
|
||||||
|
|
||||||
|
- command: yum clean all
|
||||||
|
when: distro == "CentOS"
|
||||||
|
|
||||||
|
- shell: rm -rf {{item}}/*
|
||||||
|
with_items:
|
||||||
|
- /var/cache/apt
|
||||||
|
- /var/lib/apt/lists
|
||||||
|
when: distro == "Debian"
|
||||||
|
|
||||||
|
- user:
|
||||||
|
name: root
|
||||||
|
password: "{{ 'rootpassword' | password_hash('sha256') }}"
|
||||||
|
shell: /bin/bash
|
||||||
|
|
||||||
|
- file:
|
||||||
|
path: /var/run/sshd
|
||||||
|
state: directory
|
||||||
|
|
||||||
|
- command: ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
|
||||||
|
args:
|
||||||
|
creates: /etc/ssh/ssh_host_rsa_key
|
||||||
|
|
||||||
|
- group:
|
||||||
|
name: "{{sudo_group[distro]}}"
|
||||||
|
|
||||||
|
- copy:
|
||||||
|
dest: /etc/sentinel
|
||||||
|
content: |
|
||||||
|
i-am-mitogen-test-docker-image
|
||||||
|
|
||||||
|
- copy:
|
||||||
|
dest: /etc/ssh/banner.txt
|
||||||
|
src: ../data/docker/ssh_login_banner.txt
|
||||||
|
|
||||||
|
- copy:
|
||||||
|
dest: /etc/sudoers.d/001-mitogen
|
||||||
|
src: ../data/docker/001-mitogen.sudo
|
||||||
|
|
||||||
|
- lineinfile:
|
||||||
|
path: /etc/ssh/sshd_config
|
||||||
|
line: Banner /etc/ssh/banner.txt
|
||||||
|
|
||||||
|
- lineinfile:
|
||||||
|
path: /etc/ssh/sshd_config
|
||||||
|
line: PermitRootLogin yes
|
||||||
|
regexp: '.*PermitRootLogin.*'
|
||||||
|
|
||||||
|
- lineinfile:
|
||||||
|
path: /etc/pam.d/sshd
|
||||||
|
regexp: '.*session.*required.*pam_loginuid.so'
|
||||||
|
line: session optional pam_loginuid.so
|
||||||
|
|
||||||
|
- copy:
|
||||||
|
mode: 'u+rwx,go=rx'
|
||||||
|
dest: /usr/local/bin/pywrap
|
||||||
|
content: |
|
||||||
|
#!/bin/bash
|
||||||
|
exec strace -ff -o /tmp/pywrap$$.trace python2.7 "$@"'
|
@ -0,0 +1,152 @@
|
|||||||
|
#
|
||||||
|
# Add users expected by tests. Assumes passwordless sudo to root.
|
||||||
|
#
|
||||||
|
# WARNING: this creates non-privilged accounts with pre-set passwords!
|
||||||
|
#
|
||||||
|
|
||||||
|
- hosts: all
|
||||||
|
gather_facts: true
|
||||||
|
strategy: mitogen_free
|
||||||
|
become: true
|
||||||
|
vars:
|
||||||
|
distro: "{{ansible_distribution}}"
|
||||||
|
ver: "{{ansible_distribution_major_version}}"
|
||||||
|
|
||||||
|
special_users:
|
||||||
|
- has_sudo
|
||||||
|
- has_sudo_nopw
|
||||||
|
- has_sudo_pubkey
|
||||||
|
- pw_required
|
||||||
|
- readonly_homedir
|
||||||
|
- require_tty
|
||||||
|
- require_tty_pw_required
|
||||||
|
- slow_user
|
||||||
|
- webapp
|
||||||
|
- sudo1
|
||||||
|
- sudo2
|
||||||
|
- sudo3
|
||||||
|
- sudo4
|
||||||
|
|
||||||
|
user_groups:
|
||||||
|
has_sudo: ['mitogen__group', '{{sudo_group[distro]}}']
|
||||||
|
has_sudo_pubkey: ['mitogen__group', '{{sudo_group[distro]}}']
|
||||||
|
has_sudo_nopw: ['mitogen__group', 'mitogen__sudo_nopw']
|
||||||
|
sudo1: ['mitogen__group', 'mitogen__sudo_nopw']
|
||||||
|
sudo2: ['mitogen__group', '{{sudo_group[distro]}}']
|
||||||
|
sudo3: ['mitogen__group', '{{sudo_group[distro]}}']
|
||||||
|
sudo4: ['mitogen__group', '{{sudo_group[distro]}}']
|
||||||
|
|
||||||
|
normal_users: "{{
|
||||||
|
lookup('sequence', 'start=1 end=5 format=user%d', wantlist=True)
|
||||||
|
}}"
|
||||||
|
|
||||||
|
all_users: "{{
|
||||||
|
special_users +
|
||||||
|
normal_users
|
||||||
|
}}"
|
||||||
|
tasks:
|
||||||
|
- name: Disable non-localhost SSH for Mitogen users
|
||||||
|
when: false
|
||||||
|
blockinfile:
|
||||||
|
path: /etc/ssh/sshd_config
|
||||||
|
block: |
|
||||||
|
Match User mitogen__* Address !127.0.0.1
|
||||||
|
DenyUsers *
|
||||||
|
|
||||||
|
- name: Create Mitogen test groups
|
||||||
|
group:
|
||||||
|
name: "mitogen__{{item}}"
|
||||||
|
with_items:
|
||||||
|
- group
|
||||||
|
- sudo_nopw
|
||||||
|
|
||||||
|
- name: Create user accounts
|
||||||
|
block:
|
||||||
|
- user:
|
||||||
|
name: "mitogen__{{item}}"
|
||||||
|
shell: /bin/bash
|
||||||
|
groups: "{{user_groups[item]|default(['mitogen__group'])}}"
|
||||||
|
password: "{{ (item + '_password') | password_hash('sha256') }}"
|
||||||
|
loop: "{{all_users}}"
|
||||||
|
when: ansible_system != 'Darwin'
|
||||||
|
- user:
|
||||||
|
name: "mitogen__{{item}}"
|
||||||
|
shell: /bin/bash
|
||||||
|
groups: "{{user_groups[item]|default(['mitogen__group'])}}"
|
||||||
|
password: "{{item}}_password"
|
||||||
|
loop: "{{all_users}}"
|
||||||
|
when: ansible_system == 'Darwin'
|
||||||
|
|
||||||
|
- name: Hide users from login window.
|
||||||
|
loop: "{{all_users}}"
|
||||||
|
when: ansible_system == 'Darwin'
|
||||||
|
osx_defaults:
|
||||||
|
array_add: true
|
||||||
|
domain: /Library/Preferences/com.apple.loginwindow
|
||||||
|
type: array
|
||||||
|
key: HiddenUsersList
|
||||||
|
value: ['mitogen_{{item}}']
|
||||||
|
|
||||||
|
- name: Readonly homedir for one account
|
||||||
|
shell: "chown -R root: ~mitogen__readonly_homedir"
|
||||||
|
|
||||||
|
- name: Slow bash profile for one account
|
||||||
|
copy:
|
||||||
|
dest: ~mitogen__slow_user/.{{item}}
|
||||||
|
src: ../data/docker/mitogen__slow_user.profile
|
||||||
|
with_items:
|
||||||
|
- bashrc
|
||||||
|
- profile
|
||||||
|
|
||||||
|
- name: Install pubkey for mitogen__has_sudo_pubkey
|
||||||
|
block:
|
||||||
|
- file:
|
||||||
|
path: ~mitogen__has_sudo_pubkey/.ssh
|
||||||
|
state: directory
|
||||||
|
mode: go=
|
||||||
|
owner: mitogen__has_sudo_pubkey
|
||||||
|
- copy:
|
||||||
|
dest: ~mitogen__has_sudo_pubkey/.ssh/authorized_keys
|
||||||
|
src: ../data/docker/mitogen__has_sudo_pubkey.key.pub
|
||||||
|
mode: go=
|
||||||
|
owner: mitogen__has_sudo_pubkey
|
||||||
|
|
||||||
|
- name: Install slow profile for one account
|
||||||
|
block:
|
||||||
|
- copy:
|
||||||
|
dest: ~mitogen__slow_user/.profile
|
||||||
|
src: ../data/docker/mitogen__slow_user.profile
|
||||||
|
- copy:
|
||||||
|
dest: ~mitogen__slow_user/.bashrc
|
||||||
|
src: ../data/docker/mitogen__slow_user.profile
|
||||||
|
|
||||||
|
- name: Require a TTY for two accounts
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "{{item}}"
|
||||||
|
with_items:
|
||||||
|
- Defaults>mitogen__pw_required targetpw
|
||||||
|
- Defaults>mitogen__require_tty requiretty
|
||||||
|
- Defaults>mitogen__require_tty_pw_required requiretty,targetpw
|
||||||
|
|
||||||
|
- name: Require password for two accounts
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "{{lookup('pipe', 'whoami')}} ALL = ({{item}}) ALL"
|
||||||
|
with_items:
|
||||||
|
- mitogen__pw_required
|
||||||
|
- mitogen__require_tty_pw_required
|
||||||
|
|
||||||
|
- name: Allow passwordless sudo for require_tty/readonly_homedir
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "{{lookup('pipe', 'whoami')}} ALL = ({{item}}) NOPASSWD:ALL"
|
||||||
|
with_items:
|
||||||
|
- mitogen__require_tty
|
||||||
|
- mitogen__readonly_homedir
|
||||||
|
|
||||||
|
- name: Allow passwordless for many accounts
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "{{lookup('pipe', 'whoami')}} ALL = (mitogen__{{item}}) NOPASSWD:ALL"
|
||||||
|
loop: "{{normal_users}}"
|
@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
[defaults]
|
||||||
|
strategy_plugins = ../../ansible_mitogen/plugins/strategy
|
||||||
|
retry_files_enabled = false
|
@ -0,0 +1,57 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
Build the Docker images used for testing.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import commands
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
import shlex
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
|
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 [
|
||||||
|
('debian:stretch', 'debian'),
|
||||||
|
('centos:6', 'centos6'),
|
||||||
|
('centos:7', 'centos7')
|
||||||
|
]:
|
||||||
|
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),
|
||||||
|
)
|
||||||
|
|
||||||
|
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)))
|
@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
- hosts: all
|
||||||
|
gather_facts: false
|
||||||
|
tasks:
|
||||||
|
- set_fact:
|
||||||
|
# Hacktacular.. but easiest place for it with current structure.
|
||||||
|
sudo_group:
|
||||||
|
MacOSX: admin
|
||||||
|
Debian: sudo
|
||||||
|
CentOS: wheel
|
||||||
|
|
||||||
|
- import_playbook: _container_setup.yml
|
||||||
|
- import_playbook: _user_accounts.yml
|
@ -0,0 +1,29 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
import mitogen
|
||||||
|
|
||||||
|
import unittest2
|
||||||
|
|
||||||
|
import testlib
|
||||||
|
|
||||||
|
|
||||||
|
def has_subseq(seq, subseq):
|
||||||
|
return any(seq[x:x+len(subseq)] == subseq for x in range(0, len(seq)))
|
||||||
|
|
||||||
|
|
||||||
|
class FakeLxcAttachTest(testlib.RouterMixin, unittest2.TestCase):
|
||||||
|
def test_okay(self):
|
||||||
|
lxc_attach_path = testlib.data_path('fake_lxc_attach.py')
|
||||||
|
context = self.router.lxc(
|
||||||
|
container='container_name',
|
||||||
|
lxc_attach_path=lxc_attach_path,
|
||||||
|
)
|
||||||
|
|
||||||
|
argv = eval(context.call(os.getenv, 'ORIGINAL_ARGV'))
|
||||||
|
self.assertEquals(argv[0], lxc_attach_path)
|
||||||
|
self.assertTrue('--clear-env' in argv)
|
||||||
|
self.assertTrue(has_subseq(argv, ['--name', 'container_name']))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest2.main()
|
@ -0,0 +1,26 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
import mitogen
|
||||||
|
|
||||||
|
import unittest2
|
||||||
|
|
||||||
|
import testlib
|
||||||
|
|
||||||
|
|
||||||
|
class FakeLxcTest(testlib.RouterMixin, unittest2.TestCase):
|
||||||
|
def test_okay(self):
|
||||||
|
lxc_path = testlib.data_path('fake_lxc.py')
|
||||||
|
context = self.router.lxd(
|
||||||
|
container='container_name',
|
||||||
|
lxc_path=lxc_path,
|
||||||
|
)
|
||||||
|
|
||||||
|
argv = eval(context.call(os.getenv, 'ORIGINAL_ARGV'))
|
||||||
|
self.assertEquals(argv[0], lxc_path)
|
||||||
|
self.assertEquals(argv[1], 'exec')
|
||||||
|
self.assertEquals(argv[2], '--force-noninteractive')
|
||||||
|
self.assertEquals(argv[3], 'container_name')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest2.main()
|
Loading…
Reference in New Issue