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: \
|
||||
lib/modules/custom_binary_producing_junk \
|
||||
lib/modules/custom_binary_producing_json
|
||||
TARGETS+=lib/modules/custom_binary_producing_junk
|
||||
TARGETS+=lib/modules/custom_binary_producing_json
|
||||
|
||||
all: clean $(TARGETS)
|
||||
|
||||
lib/modules/custom_binary_producing_junk: lib/modules.src/custom_binary_producing_junk.c
|
||||
$(CC) -o $@ $<
|
||||
|
||||
lib/modules/custom_binary_producing_json: lib/modules.src/custom_binary_producing_json.c
|
||||
$(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
|
||||
hosts: test-targets
|
||||
any_errors_fatal: true
|
||||
tasks:
|
||||
- name: "Find out root's homedir."
|
||||
# Runs first because it blats regular Ansible facts with junk, so
|
||||
# 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
|
||||
- meta: end_play
|
||||
when: not is_mitogen
|
||||
|
||||
#
|
||||
# non-become
|
||||
# non-root
|
||||
#
|
||||
|
||||
- action_passthrough:
|
||||
- name: "Find regular temp path"
|
||||
action_passthrough:
|
||||
method: _make_tmp_path
|
||||
register: out
|
||||
register: tmp_path
|
||||
|
||||
- name: "Write some junk in regular temp path"
|
||||
shell: hostname > {{tmp_path.result}}/hostname
|
||||
|
||||
- name: "Verify junk did not persist across tasks"
|
||||
stat: path={{tmp_path.result}}/hostname
|
||||
register: junk_stat
|
||||
|
||||
- name: "Verify junk did not persist across tasks"
|
||||
assert:
|
||||
that:
|
||||
- not junk_stat.stat.exists
|
||||
|
||||
- name: "Verify temp path hasn't changed since start"
|
||||
action_passthrough:
|
||||
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
|
||||
|
||||
- assert:
|
||||
# This string must match ansible.cfg::remote_tmp
|
||||
that: out.result.startswith("{{user_facts.ansible_facts.ansible_user_dir}}/.ansible/mitogen-tests/")
|
||||
- name: "Verify temp path changes across connection reset"
|
||||
assert:
|
||||
that:
|
||||
- tmp_path2.result != tmp_path.result
|
||||
|
||||
- stat:
|
||||
path: "{{out.result}}"
|
||||
register: st
|
||||
- name: "Verify old path disappears across connection reset"
|
||||
stat: path={{tmp_path.result}}
|
||||
register: junk_stat
|
||||
|
||||
- assert:
|
||||
that: st.stat.exists and st.stat.isdir and st.stat.mode == "0700"
|
||||
- name: "Verify old path disappears across connection reset"
|
||||
assert:
|
||||
that:
|
||||
- not junk_stat.stat.exists
|
||||
|
||||
- file:
|
||||
path: "{{out.result}}"
|
||||
state: absent
|
||||
#
|
||||
# root
|
||||
#
|
||||
|
||||
- name: "Find root temp path"
|
||||
become: true
|
||||
action_passthrough:
|
||||
method: _make_tmp_path
|
||||
register: tmp_path_root
|
||||
|
||||
- name: "Verify root temp path differs from regular path"
|
||||
assert:
|
||||
that:
|
||||
- tmp_path2.result != tmp_path_root.result
|
||||
|
||||
#
|
||||
# become. make_tmp_path() must evaluate HOME in the context of the SSH
|
||||
# user, not the become user.
|
||||
# readonly homedir
|
||||
#
|
||||
|
||||
- action_passthrough:
|
||||
- name: "Try writing to temp directory for the readonly_homedir user"
|
||||
become: true
|
||||
become_user: mitogen__readonly_homedir
|
||||
action_passthrough:
|
||||
method: _make_tmp_path
|
||||
register: out
|
||||
register: tmp_path
|
||||
|
||||
- name: "Try writing to temp directory for the readonly_homedir user"
|
||||
become: true
|
||||
become_user: mitogen__readonly_homedir
|
||||
shell: hostname > {{tmp_path.result}}/hostname
|
||||
|
||||
- assert:
|
||||
# This string must match ansible.cfg::remote_tmp
|
||||
that: out.result.startswith("{{user_facts.ansible_facts.ansible_user_dir}}/.ansible/mitogen-tests/")
|
||||
#
|
||||
# modules get the same temp dir
|
||||
#
|
||||
|
||||
- stat:
|
||||
path: "{{out.result}}"
|
||||
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:
|
||||
that: st.stat.exists and st.stat.isdir and st.stat.mode == "0700"
|
||||
# v2.6 related: https://github.com/ansible/ansible/pull/39833
|
||||
- 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:
|
||||
path: "{{out.result}}"
|
||||
state: absent
|
||||
- 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 == 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