commit
5667116f58
@ -0,0 +1,66 @@
|
||||
#!/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
|
||||
--hostname=target-%s
|
||||
--name=target-%s
|
||||
mitogen/%s-test
|
||||
""", BASE_PORT + i, distro, 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/hosts/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,102 @@
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import print_function
|
||||
|
||||
import atexit
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import shlex
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
import os
|
||||
os.system('curl -H Metadata-Flavor:Google http://metadata.google.internal/computeMetadata/v1/instance/machine-type')
|
||||
|
||||
#
|
||||
# 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,56 @@
|
||||
# coding: utf-8
|
||||
# Copyright 2018, Yannig Perré
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import absolute_import
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
import ansible.plugins.connection.kubectl
|
||||
from ansible.module_utils.six import iteritems
|
||||
|
||||
try:
|
||||
import ansible_mitogen
|
||||
except ImportError:
|
||||
base_dir = os.path.dirname(__file__)
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(base_dir, '../../..')))
|
||||
del base_dir
|
||||
|
||||
import ansible_mitogen.connection
|
||||
|
||||
|
||||
class Connection(ansible_mitogen.connection.Connection):
|
||||
transport = 'kubectl'
|
||||
|
||||
def get_extra_args(self):
|
||||
parameters = []
|
||||
for key, option in iteritems(ansible.plugins.connection.kubectl.CONNECTION_OPTIONS):
|
||||
if self.get_task_var('ansible_' + key) is not None:
|
||||
parameters += [ option, self.get_task_var('ansible_' + key) ]
|
||||
|
||||
return parameters
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,226 +0,0 @@
|
||||
|
||||
Mitogen Compared To
|
||||
-------------------
|
||||
|
||||
This provides a little free-text summary of conceptual differences between
|
||||
Mitogen and other tools, along with some basic perceptual metrics (project
|
||||
maturity/age, quality of tests, function matrix)
|
||||
|
||||
|
||||
Ansible
|
||||
#######
|
||||
|
||||
Ansible_ is a complete provisioning system, Mitogen is a small component of such a system.
|
||||
|
||||
You should use Ansible if ...
|
||||
|
||||
You should not use Ansible if ...
|
||||
|
||||
|
||||
.. _Ansible: https://docs.ansible.com/ansible/latest/index.html
|
||||
.. _ansible.src: https://github.com/ansible/ansible/
|
||||
|
||||
Baker
|
||||
#####
|
||||
|
||||
Baker_ lets you easily add a command line interface to your Python
|
||||
functions using a simple decorator, to create scripts with "sub-commands",
|
||||
similar to Django's ``manage.py``, ``svn``, ``hg``, etc.
|
||||
|
||||
- Unmaintained since 2015
|
||||
- No obvious remote execution functionality
|
||||
|
||||
.. _Baker: https://bitbucket.org/mchaput/baker
|
||||
|
||||
Chopsticks
|
||||
##########
|
||||
|
||||
Chopsticks_ also supports recursion! but the recursively executed instance has no special knowledge of its identity in a tree structure, and little support for functions running in the master to directly invoke functions in a recursive context.. effectively each recursion produces a new master, from which function calls must be made.
|
||||
|
||||
executing functions from __main__ entails picking just that function and deps
|
||||
out of the main module, not transferring the module intact. that approach works
|
||||
but it's much messier than just arranging for __main__ to be imported and
|
||||
executed through the import mechanism.
|
||||
|
||||
supports sudo but no support for require_tty or typing a sudo password. also supports SSH and Docker.
|
||||
|
||||
good set of tests
|
||||
|
||||
real PEP-302 module loader, but doesn't try to cope with master also relying on
|
||||
a PEP-302 module loader (e.g. py2exe).
|
||||
|
||||
Based on the tox configuration Python 2.7, and 3.3 to 3.6 are supported.
|
||||
|
||||
I/O multiplexer in the master, but not in children.
|
||||
|
||||
As with Execnet it includes its own serialization - pencode_ supports
|
||||
|
||||
- most Python primitive types (``bytes``/``str``/``unicode``, ``list``, ``tuple`` ...)
|
||||
- identity references
|
||||
- self referencing (recursive) data srtuctures
|
||||
|
||||
pencode lacks support for arbitrary classes. Byte strings require special
|
||||
treatment if they contain non-ascii characters. Some primitive types
|
||||
(e.g. ``complex``) are not handled. This would be straightforwar to address.
|
||||
Values are length-prefixed with a 32 bit unsigned integer, meaning values
|
||||
are limited to 4 billion bytes or items in length.
|
||||
|
||||
design is reminiscent of Mitogen in places (Tunnel is practically identical to
|
||||
Mitogen's Stream), and closer to Execnet elsewhere (lack of uniformity,
|
||||
tendency to prefer logic expressed in if/else special case soup rather than the
|
||||
type system, though some of that is due to supporting Python 3, so not judging
|
||||
too harshly!)
|
||||
|
||||
Chopsticks has its own `Chopsticks vs`_ comparisons.
|
||||
|
||||
You should use Chopsticks if you need Python 3 support.
|
||||
|
||||
.. _Chopsticks: https://chopsticks.readthedocs.io/en/stable/
|
||||
.. _Chopsticks.src: https://github.com/lordmauve/chopsticks/
|
||||
.. _Chopsticks vs: https://chopsticks.readthedocs.io/en/stable/intro.html#chopsticks-vs
|
||||
.. _pencode: https://github.com/lordmauve/chopsticks/blob/master/doc/pencode.rst
|
||||
.. _pencode.src: https://github.com/lordmauve/chopsticks/blob/master/chopsticks/pencode.py
|
||||
|
||||
Disco
|
||||
#####
|
||||
|
||||
Disco_ is a lightweight, open-source framework for distributed computing
|
||||
based on the MapReduce paradigm.
|
||||
|
||||
- An Erlang core, with Python bindings
|
||||
- Wire format is pickle, according to `Execnet vs NLTK for distributed NLTK`_
|
||||
|
||||
.. _Disco: http://discoproject.org/
|
||||
.. _Execnet vs NLTK for distributed NLTK: https://streamhacker.com/2009/12/14/execnet-disco-distributed-nltk/
|
||||
|
||||
Execnet
|
||||
#######
|
||||
|
||||
Execnet_
|
||||
|
||||
- Parent and children may use threads, gevent, or eventlet, Mitogen only supports threads.
|
||||
- No recursion
|
||||
- Similar Channel abstraction but better developed.. includes waiting for remote to close its end
|
||||
- Heavier emphasis on passing chunks of Python source code around, modules are loaded one-at-a-time with no dependency resolution mechanism
|
||||
- Built-in unidirectional rsync-alike, compared to Mitogen's SSH emulation which allows use of real rsync in any supported mode
|
||||
- no support for sudo, but supports connecting to vagrant
|
||||
- works with read-only filesystem
|
||||
- includes its own serialization_ independent of the standard library
|
||||
|
||||
The obj and all contained objects must be of a builtin python type
|
||||
(so nested dicts, sets, etc. are all ok but not user-level instances).
|
||||
|
||||
- Known uses include `pytest-xdist`_, and `Distributed NLTK`_
|
||||
|
||||
You should use Execnet if you value code maturity more than featureset.
|
||||
|
||||
.. _Execnet: https://codespeak.net/execnet/
|
||||
.. _serialization: https://codespeak.net/execnet/basics.html#dumps-loads
|
||||
.. _pytest-xdist: https://pypi.python.org/pypi/pytest-xdist
|
||||
.. _Distributed NLTK: https://streamhacker.com/2009/12/14/execnet-disco-distributed-nltk/
|
||||
|
||||
Fabric
|
||||
######
|
||||
|
||||
Fabric_ allows execution of shell snippets on remote machines, Python functions run
|
||||
locally, any remote interaction is fundamentally done via shell, with all the
|
||||
limitations that entails. prefers to depend on SSH features (e.g. tunnelling)
|
||||
than reinvent them
|
||||
|
||||
You should use Fabric if you enjoy being woken at 4am to pages about broken
|
||||
shell snippets.
|
||||
|
||||
.. _fabric: http://www.fabfile.org/
|
||||
|
||||
Invoke
|
||||
######
|
||||
|
||||
Invoke_
|
||||
|
||||
Python 2.6+, 3.3+
|
||||
|
||||
Basically a Fabric-alike
|
||||
|
||||
.. _invoke: http://www.pyinvoke.org/
|
||||
|
||||
Multiprocessing
|
||||
###############
|
||||
|
||||
multiprocessing_ was added to the stdlib in Python 2.6.
|
||||
|
||||
multiprocessing is a package that supports spawning processes using an
|
||||
API similar to the threading module. The multiprocessing package offers
|
||||
both local and remote concurrency
|
||||
|
||||
There is a backport_ for Python 2.4 & 2.5, but it is not pure Python.
|
||||
pymultiprocessing_ appears to be a pure Python implementation.
|
||||
An ecosystem_ of packages has built up around multiprocessing.
|
||||
|
||||
The `programming guidelines`_ section notes
|
||||
|
||||
- Arguments to proxies must be picklable. On Windows this also applies to
|
||||
``multiprocessing.Process.__init__()`` arguments.
|
||||
- Callers should beware replacing ``sys.stdin``, because
|
||||
``multiprocessing.Process._bootstrap()``
|
||||
will close it and open /dev/null instead
|
||||
|
||||
.. _programming guidelines: https://docs.python.org/2/library/multiprocessing.html#programming-guidelines
|
||||
.. _backport: https://pypi.python.org/pypi/multiprocessing
|
||||
.. _pymultiprocessing: https://pypi.python.org/pypi/pymultiprocessing
|
||||
.. _ecosystem: https://pypi.python.org/pypi?%3Aaction=search&term=multiprocessing&submit=search
|
||||
|
||||
Paver
|
||||
#####
|
||||
|
||||
Paver_
|
||||
|
||||
More or less another task execution framework / make-alike, doesn't really deal
|
||||
with remote execution at all.
|
||||
|
||||
.. _Paver: https://github.com/paver/paver/
|
||||
|
||||
Plumbum
|
||||
#######
|
||||
|
||||
Plumbum_
|
||||
|
||||
Shell-only
|
||||
|
||||
Basically syntax sugar for running shell commands. Nicer than raw shell
|
||||
(depending on your opinions of operating overloading), but it's still shell.
|
||||
|
||||
.. _Plumbum: https://pypi.python.org/pypi/plumbum
|
||||
|
||||
Pyro4
|
||||
#####
|
||||
|
||||
Pyro4_
|
||||
...
|
||||
|
||||
.. _Pyro4: https://pythonhosted.org/Pyro4/
|
||||
|
||||
RPyC
|
||||
####
|
||||
|
||||
RPyC_
|
||||
|
||||
- supports transparent object proxies similar to Pyro (with all the pain and suffering hidden network IO entails)
|
||||
- significantly more 'frameworkey' feel
|
||||
- runs multiplexer in a thread too?
|
||||
- bootstrap over SSH only, no recursion and no sudo
|
||||
- requires a writable filesystem
|
||||
|
||||
.. _RPyC: https://rpyc.readthedocs.io/en/latest/
|
||||
|
||||
Salt
|
||||
####
|
||||
|
||||
Salt_
|
||||
|
||||
- no crappy deps
|
||||
|
||||
You should use Salt if you enjoy firefighting endless implementation bugs,
|
||||
otherwise you should prefer Ansible.
|
||||
|
||||
.. _Salt: https://docs.saltstack.com/en/latest/topics/
|
||||
.. _Salt.src: https://github.com/saltstack/salt
|
||||
@ -1,3 +1,4 @@
|
||||
Sphinx==1.7.1
|
||||
sphinx-autobuild==0.6.0 # Last version to support Python 2.6
|
||||
sphinxcontrib-programoutput==0.11
|
||||
alabaster==0.7.10
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 160 60" style="enable-background:new 0 0 160 60;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFED58;}
|
||||
</style>
|
||||
<rect class="st0" width="160" height="60"/>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M43.8,23.9c-0.4-1.6-1.3-2.7-2.7-3.4c-0.8-0.3-1.7-0.5-2.7-0.5c-1.9,0-3.5,0.7-4.7,2.2c-1.2,1.4-1.8,3.6-1.8,6.4
|
||||
s0.7,4.9,2,6.1c1.3,1.2,2.8,1.8,4.5,1.8c1.6,0,3-0.5,4-1.4c1.1-1,1.7-2.2,2-3.7h-5.5v-4h9.8v12.6h-3.3L45,37.1
|
||||
c-1,1.1-1.8,1.9-2.6,2.4c-1.3,0.8-2.9,1.2-4.8,1.2c-3.1,0-5.7-1.1-7.7-3.2c-2.1-2.2-3.1-5.1-3.1-8.9c0-3.8,1.1-6.9,3.2-9.2
|
||||
s4.9-3.5,8.3-3.5c3,0,5.4,0.8,7.2,2.3c1.8,1.5,2.8,3.4,3.1,5.7C48.6,23.9,43.8,23.9,43.8,23.9L43.8,23.9z"/>
|
||||
<path d="M66.8,37.9c-1.5,1.8-3.7,2.7-6.7,2.7c-3,0-5.2-0.9-6.7-2.7c-1.5-1.8-2.2-4-2.2-6.6c0-2.5,0.7-4.7,2.2-6.5
|
||||
c1.5-1.9,3.7-2.7,6.7-2.7c3,0,5.2,0.9,6.7,2.7c1.5,1.9,2.2,4,2.2,6.5C69,33.9,68.2,36.1,66.8,37.9z M63.1,35.4
|
||||
c0.7-1,1.1-2.3,1.1-4s-0.3-3.1-1.1-4C62.4,26.4,61.3,26,60,26c-1.3,0-2.4,0.5-3.1,1.4c-0.7,0.9-1.1,2.3-1.1,4c0,1.7,0.3,3.1,1.1,4
|
||||
c0.7,1,1.7,1.4,3.1,1.4S62.4,36.3,63.1,35.4z"/>
|
||||
<path d="M86.2,37.9c-1.5,1.8-3.7,2.7-6.7,2.7c-3,0-5.2-0.9-6.7-2.7c-1.5-1.8-2.2-4-2.2-6.6c0-2.5,0.7-4.7,2.2-6.5
|
||||
c1.5-1.9,3.7-2.7,6.7-2.7c3,0,5.2,0.9,6.7,2.7c1.5,1.9,2.2,4,2.2,6.5C88.4,33.9,87.6,36.1,86.2,37.9z M82.5,35.4
|
||||
c0.7-1,1.1-2.3,1.1-4s-0.3-3.1-1.1-4c-0.7-0.9-1.7-1.4-3.1-1.4c-1.3,0-2.4,0.5-3.1,1.4c-0.7,0.9-1.1,2.3-1.1,4
|
||||
c0,1.7,0.3,3.1,1.1,4c0.7,1,1.7,1.4,3.1,1.4C80.8,36.8,81.8,36.3,82.5,35.4z"/>
|
||||
<path d="M100.3,22.9c0.8,0.5,1.5,1.1,2,1.9v-8.3h4.6V40h-4.4v-2.4c-0.7,1-1.4,1.8-2.2,2.3s-1.9,0.7-3.1,0.7c-2,0-3.7-0.8-5.1-2.5
|
||||
S90,34.4,90,31.8c0-3,0.7-5.3,2.1-7c1.4-1.7,3.2-2.6,5.5-2.6C98.6,22.2,99.5,22.5,100.3,22.9L100.3,22.9z M101.4,35.3
|
||||
c0.7-1,1-2.2,1-3.7c0-2.1-0.5-3.6-1.6-4.6c-0.7-0.5-1.4-0.8-2.3-0.8c-1.3,0-2.3,0.5-2.9,1.5c-0.6,1-0.9,2.3-0.9,3.7
|
||||
c0,1.6,0.3,2.9,1,3.8c0.6,1,1.6,1.4,2.9,1.4C99.8,36.8,100.8,36.3,101.4,35.3L101.4,35.3z"/>
|
||||
<path d="M132.6,43.5c-0.2-0.2-0.5-0.7-0.7-1c-0.1-0.2-1.7-3.6-3.2-6.6c1.6-2.5,3.2-4.9,3.3-5c0.3-0.4,0.7-0.7,1.1-0.9l0-0.3
|
||||
c-0.7,0-2.5-0.1-2.9,0.4c-0.4,0.5-0.4,0.5-0.8,1.1c-0.1,0.2-0.8,1.2-1.6,2.6c-0.7-1.4-1.1-2.4-1.2-2.5c-0.2-0.6-0.5-1.1-0.9-1.6
|
||||
c-0.4-0.5-1-0.8-1.6-1.1c1.3-0.6,2.3-1.4,2.9-2.2c0.6-0.9,0.9-2.1,0.9-3.5c0-2.2-0.7-3.8-2.1-4.7c-1.4-1-3.4-1.4-6-1.4h-9.2v23.5
|
||||
l2.2,0V29.6l0,0h7c0.5,0,1.1,0,1.6,0.1c0.5,0,1,0.2,1.4,0.4c0.4,0.2,0.8,0.5,1.1,0.9c0.1,0.2,1.3,2.5,2.6,5c-1.9,3-4,6.4-4.1,6.6
|
||||
c-0.2,0.3-0.5,0.8-0.7,1c-0.2,0.2-0.4,0.2-0.5,0.3V44l2.5,0c0.4-0.4,0.7-1,1.1-1.6c0.2-0.3,1.4-2.3,2.8-4.5
|
||||
c1.1,2.2,2.1,4.2,2.2,4.5c0.2,0.7,0.7,1.3,1.1,1.6l2.5,0v-0.2C133,43.8,132.8,43.7,132.6,43.5z M123.9,26.5
|
||||
c-0.5,0.4-1.1,0.7-1.9,0.8c-0.7,0.2-1.5,0.3-2.3,0.3l0,0h-6.9v-9.1h7.1c0.7,0,1.4,0.1,2.1,0.2c0.7,0.1,1.3,0.3,1.8,0.7
|
||||
c0.5,0.3,1,0.8,1.3,1.3c0.3,0.6,0.5,1.3,0.5,2.2c0,0.8-0.1,1.5-0.4,2.1C124.8,25.7,124.4,26.2,123.9,26.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
@ -0,0 +1,65 @@
|
||||
# coding: utf-8
|
||||
# Copyright 2018, Yannig Perré
|
||||
#
|
||||
# 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 = True
|
||||
|
||||
pod = None
|
||||
kubectl_path = 'kubectl'
|
||||
kubectl_args = None
|
||||
|
||||
# TODO: better way of capturing errors such as "No such container."
|
||||
create_child_args = {
|
||||
'merge_stdio': True
|
||||
}
|
||||
|
||||
def construct(self, pod, kubectl_path=None, kubectl_args=None, **kwargs):
|
||||
super(Stream, self).construct(**kwargs)
|
||||
assert pod
|
||||
self.pod = pod
|
||||
if kubectl_path:
|
||||
self.kubectl_path = kubectl_path
|
||||
self.kubectl_args = kubectl_args or []
|
||||
|
||||
def connect(self):
|
||||
super(Stream, self).connect()
|
||||
self.name = u'kubectl.%s%s' % (self.pod, self.kubectl_args)
|
||||
|
||||
def get_boot_command(self):
|
||||
bits = [self.kubectl_path] + self.kubectl_args + ['exec', '-it', self.pod]
|
||||
return bits + ["--"] + super(Stream, self).get_boot_command()
|
||||
@ -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',
|
||||
'--mode=noninteractive',
|
||||
self.container,
|
||||
'--',
|
||||
]
|
||||
return bits + super(Stream, self).get_boot_command()
|
||||
@ -1,2 +1,3 @@
|
||||
lib/modules/custom_binary_producing_junk
|
||||
lib/modules/custom_binary_producing_json
|
||||
hosts/*.local
|
||||
|
||||
@ -1,10 +1,16 @@
|
||||
|
||||
all: \
|
||||
lib/modules/custom_binary_producing_junk \
|
||||
lib/modules/custom_binary_producing_json
|
||||
SYSTEM=$(shell uname -s)
|
||||
|
||||
lib/modules/custom_binary_producing_junk: lib/modules.src/custom_binary_producing_junk.c
|
||||
TARGETS+=lib/modules/custom_binary_producing_junk_$(SYSTEM)
|
||||
TARGETS+=lib/modules/custom_binary_producing_json_$(SYSTEM)
|
||||
|
||||
all: clean $(TARGETS)
|
||||
|
||||
lib/modules/custom_binary_producing_junk_$(SYSTEM): lib/modules.src/custom_binary_producing_junk.c
|
||||
$(CC) -o $@ $<
|
||||
|
||||
lib/modules/custom_binary_producing_json: lib/modules.src/custom_binary_producing_json.c
|
||||
lib/modules/custom_binary_producing_json_$(SYSTEM): 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,14 @@
|
||||
|
||||
- hosts: all
|
||||
tasks:
|
||||
- file:
|
||||
dest: /tmp/templates
|
||||
state: "{{item}}"
|
||||
with_items: ["absent", "directory"]
|
||||
|
||||
- copy:
|
||||
dest: /tmp/templates/{{item}}
|
||||
mode: 0755
|
||||
content:
|
||||
Hello from {{item}}
|
||||
with_sequence: start=1 end=20
|
||||
@ -1,2 +1,2 @@
|
||||
[controller]
|
||||
35.206.145.240
|
||||
c
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
[defaults]
|
||||
inventory = hosts,~/mitogen/tests/ansible/lib/inventory
|
||||
gathering = explicit
|
||||
strategy_plugins = ~/mitogen/ansible_mitogen/plugins/strategy
|
||||
action_plugins = ~/mitogen/tests/ansible/lib/action
|
||||
callback_plugins = ~/mitogen/tests/ansible/lib/callback
|
||||
stdout_callback = nice_stdout
|
||||
vars_plugins = ~/mitogen/tests/ansible/lib/vars
|
||||
library = ~/mitogen/tests/ansible/lib/modules
|
||||
retry_files_enabled = False
|
||||
forks = 50
|
||||
|
||||
strategy = mitogen_linear
|
||||
|
||||
host_key_checking = False
|
||||
|
||||
[ssh_connection]
|
||||
ssh_args = -o ForwardAgent=yes -o ControlMaster=auto -o ControlPersist=60s
|
||||
pipelining = True
|
||||
@ -0,0 +1,6 @@
|
||||
|
||||
Host localhost-*
|
||||
Hostname localhost
|
||||
|
||||
Host gitlab.com
|
||||
IdentityFile ~/.ssh/id_gitlab
|
||||
@ -1,100 +0,0 @@
|
||||
mydeb9-1 ansible_connection=docker
|
||||
mydeb9-2 ansible_connection=docker
|
||||
mydeb9-3 ansible_connection=docker
|
||||
mydeb9-4 ansible_connection=docker
|
||||
mydeb9-5 ansible_connection=docker
|
||||
mydeb9-6 ansible_connection=docker
|
||||
mydeb9-7 ansible_connection=docker
|
||||
mydeb9-8 ansible_connection=docker
|
||||
mydeb9-9 ansible_connection=docker
|
||||
mydeb9-10 ansible_connection=docker
|
||||
mydeb9-11 ansible_connection=docker
|
||||
mydeb9-12 ansible_connection=docker
|
||||
mydeb9-13 ansible_connection=docker
|
||||
mydeb9-14 ansible_connection=docker
|
||||
mydeb9-15 ansible_connection=docker
|
||||
mydeb9-16 ansible_connection=docker
|
||||
mydeb9-17 ansible_connection=docker
|
||||
mydeb9-18 ansible_connection=docker
|
||||
mydeb9-19 ansible_connection=docker
|
||||
mydeb9-20 ansible_connection=docker
|
||||
mydeb9-21 ansible_connection=docker
|
||||
mydeb9-22 ansible_connection=docker
|
||||
mydeb9-23 ansible_connection=docker
|
||||
mydeb9-24 ansible_connection=docker
|
||||
mydeb9-25 ansible_connection=docker
|
||||
mydeb9-26 ansible_connection=docker
|
||||
mydeb9-27 ansible_connection=docker
|
||||
mydeb9-28 ansible_connection=docker
|
||||
mydeb9-29 ansible_connection=docker
|
||||
mydeb9-30 ansible_connection=docker
|
||||
mydeb9-31 ansible_connection=docker
|
||||
mydeb9-32 ansible_connection=docker
|
||||
mydeb9-33 ansible_connection=docker
|
||||
mydeb9-34 ansible_connection=docker
|
||||
mydeb9-35 ansible_connection=docker
|
||||
mydeb9-36 ansible_connection=docker
|
||||
mydeb9-37 ansible_connection=docker
|
||||
mydeb9-38 ansible_connection=docker
|
||||
mydeb9-39 ansible_connection=docker
|
||||
mydeb9-40 ansible_connection=docker
|
||||
mydeb9-41 ansible_connection=docker
|
||||
mydeb9-42 ansible_connection=docker
|
||||
mydeb9-43 ansible_connection=docker
|
||||
mydeb9-44 ansible_connection=docker
|
||||
mydeb9-45 ansible_connection=docker
|
||||
mydeb9-46 ansible_connection=docker
|
||||
mydeb9-47 ansible_connection=docker
|
||||
mydeb9-48 ansible_connection=docker
|
||||
mydeb9-49 ansible_connection=docker
|
||||
mydeb9-50 ansible_connection=docker
|
||||
mydeb9-51 ansible_connection=docker
|
||||
mydeb9-52 ansible_connection=docker
|
||||
mydeb9-53 ansible_connection=docker
|
||||
mydeb9-54 ansible_connection=docker
|
||||
mydeb9-55 ansible_connection=docker
|
||||
mydeb9-56 ansible_connection=docker
|
||||
mydeb9-57 ansible_connection=docker
|
||||
mydeb9-58 ansible_connection=docker
|
||||
mydeb9-59 ansible_connection=docker
|
||||
mydeb9-60 ansible_connection=docker
|
||||
mydeb9-61 ansible_connection=docker
|
||||
mydeb9-62 ansible_connection=docker
|
||||
mydeb9-63 ansible_connection=docker
|
||||
mydeb9-64 ansible_connection=docker
|
||||
mydeb9-65 ansible_connection=docker
|
||||
mydeb9-66 ansible_connection=docker
|
||||
mydeb9-67 ansible_connection=docker
|
||||
mydeb9-68 ansible_connection=docker
|
||||
mydeb9-69 ansible_connection=docker
|
||||
mydeb9-70 ansible_connection=docker
|
||||
mydeb9-71 ansible_connection=docker
|
||||
mydeb9-72 ansible_connection=docker
|
||||
mydeb9-73 ansible_connection=docker
|
||||
mydeb9-74 ansible_connection=docker
|
||||
mydeb9-75 ansible_connection=docker
|
||||
mydeb9-76 ansible_connection=docker
|
||||
mydeb9-77 ansible_connection=docker
|
||||
mydeb9-78 ansible_connection=docker
|
||||
mydeb9-79 ansible_connection=docker
|
||||
mydeb9-80 ansible_connection=docker
|
||||
mydeb9-81 ansible_connection=docker
|
||||
mydeb9-82 ansible_connection=docker
|
||||
mydeb9-83 ansible_connection=docker
|
||||
mydeb9-84 ansible_connection=docker
|
||||
mydeb9-85 ansible_connection=docker
|
||||
mydeb9-86 ansible_connection=docker
|
||||
mydeb9-87 ansible_connection=docker
|
||||
mydeb9-88 ansible_connection=docker
|
||||
mydeb9-89 ansible_connection=docker
|
||||
mydeb9-90 ansible_connection=docker
|
||||
mydeb9-91 ansible_connection=docker
|
||||
mydeb9-92 ansible_connection=docker
|
||||
mydeb9-93 ansible_connection=docker
|
||||
mydeb9-94 ansible_connection=docker
|
||||
mydeb9-95 ansible_connection=docker
|
||||
mydeb9-96 ansible_connection=docker
|
||||
mydeb9-97 ansible_connection=docker
|
||||
mydeb9-98 ansible_connection=docker
|
||||
mydeb9-99 ansible_connection=docker
|
||||
mydeb9-100 ansible_connection=docker
|
||||
@ -0,0 +1,43 @@
|
||||
# vim: syntax=dosini
|
||||
|
||||
|
||||
# This must be defined explicitly, otherwise _create_implicit_localhost()
|
||||
# generates its own copy, which includes an ansible_python_interpreter that
|
||||
# varies according to host machine.
|
||||
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
|
||||
|
||||
[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
|
||||
@ -1,7 +1,3 @@
|
||||
|
||||
[test-targets]
|
||||
localhost
|
||||
|
||||
[connection-delegation-test]
|
||||
cd-bastion
|
||||
cd-rack11 mitogen_via=ssh-user@cd-bastion
|
||||
@ -0,0 +1,4 @@
|
||||
---
|
||||
|
||||
ansible_connection: setns
|
||||
mitogen_kind: lxc
|
||||
@ -0,0 +1,12 @@
|
||||
# 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
|
||||
@ -0,0 +1,25 @@
|
||||
k3
|
||||
|
||||
[k3-x10]
|
||||
k3-[01:10]
|
||||
|
||||
[k3-x20]
|
||||
k3-[01:20]
|
||||
|
||||
[k3-x50]
|
||||
k3-[01:50]
|
||||
|
||||
[k3-x100]
|
||||
k3-[001:100]
|
||||
|
||||
[k3-x200]
|
||||
k3-[001:200]
|
||||
|
||||
[k3-x300]
|
||||
k3-[001:300]
|
||||
|
||||
[k3-x400]
|
||||
k3-[001:400]
|
||||
|
||||
[k3-x500]
|
||||
k3-[001:500]
|
||||
@ -0,0 +1,8 @@
|
||||
localhost
|
||||
target ansible_host=localhost
|
||||
|
||||
[test-targets]
|
||||
target
|
||||
|
||||
[localhost-x10]
|
||||
localhost-[01:10]
|
||||
@ -0,0 +1,10 @@
|
||||
nessy
|
||||
|
||||
[nessy-x10]
|
||||
nessy-[00:10]
|
||||
|
||||
[nessy-x20]
|
||||
nessy-[00:20]
|
||||
|
||||
[nessy-x50]
|
||||
nessy-[00:50]
|
||||
@ -0,0 +1,9 @@
|
||||
# integration/delegation/delegate_to_container.yml
|
||||
|
||||
# Patterned after openstack-ansible/all_containers.yml
|
||||
osa-host-machine ansible_host=172.29.236.100
|
||||
|
||||
[osa-all-containers]
|
||||
osa-container-1 container_tech=lxc
|
||||
osa-container-2 container_tech=lxc
|
||||
osa-container-3 container_tech=lxc
|
||||
@ -1,5 +1,9 @@
|
||||
- import_playbook: remote_file_exists.yml
|
||||
- import_playbook: remote_expand_user.yml
|
||||
- import_playbook: copy.yml
|
||||
- import_playbook: fixup_perms2__copy.yml
|
||||
- import_playbook: low_level_execute_command.yml
|
||||
- import_playbook: make_tmp_path.yml
|
||||
- import_playbook: remote_expand_user.yml
|
||||
- import_playbook: remote_file_exists.yml
|
||||
- import_playbook: remove_tmp_path.yml
|
||||
- import_playbook: synchronize.yml
|
||||
- import_playbook: transfer_data.yml
|
||||
|
||||
@ -0,0 +1,83 @@
|
||||
# Verify copy module for small and large files, and inline content.
|
||||
|
||||
- name: integration/action/synchronize.yml
|
||||
hosts: test-targets
|
||||
any_errors_fatal: true
|
||||
tasks:
|
||||
- copy:
|
||||
dest: /tmp/copy-tiny-file
|
||||
content:
|
||||
this is a tiny file.
|
||||
connection: local
|
||||
|
||||
- copy:
|
||||
dest: /tmp/copy-large-file
|
||||
# Must be larger than Connection.SMALL_SIZE_LIMIT.
|
||||
content: "{% for x in range(200000) %}x{% endfor %}"
|
||||
connection: local
|
||||
|
||||
# end of making files
|
||||
|
||||
- file:
|
||||
state: absent
|
||||
path: "{{item}}"
|
||||
with_items:
|
||||
- /tmp/copy-tiny-file.out
|
||||
- /tmp/copy-large-file.out
|
||||
- /tmp/copy-tiny-inline-file.out
|
||||
- /tmp/copy-large-inline-file.out
|
||||
|
||||
# end of cleaning out files
|
||||
|
||||
- copy:
|
||||
dest: /tmp/copy-large-file.out
|
||||
src: /tmp/copy-large-file
|
||||
|
||||
- copy:
|
||||
dest: /tmp/copy-tiny-file.out
|
||||
src: /tmp/copy-tiny-file
|
||||
|
||||
- copy:
|
||||
dest: /tmp/copy-tiny-inline-file.out
|
||||
content: "tiny inline content"
|
||||
|
||||
- copy:
|
||||
dest: /tmp/copy-large-inline-file.out
|
||||
content: |
|
||||
{% for x in range(200000) %}y{% endfor %}
|
||||
|
||||
# stat results
|
||||
|
||||
- stat:
|
||||
path: "{{item}}"
|
||||
with_items:
|
||||
- /tmp/copy-tiny-file.out
|
||||
- /tmp/copy-large-file.out
|
||||
- /tmp/copy-tiny-inline-file.out
|
||||
- /tmp/copy-large-inline-file.out
|
||||
register: stat
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- stat.results[0].stat.checksum == "f29faa9a6f19a700a941bf2aa5b281643c4ec8a0"
|
||||
- stat.results[1].stat.checksum == "62951f943c41cdd326e5ce2b53a779e7916a820d"
|
||||
- stat.results[2].stat.checksum == "b26dd6444595e2bdb342aa0a91721b57478b5029"
|
||||
- stat.results[3].stat.checksum == "d675f47e467eae19e49032a2cc39118e12a6ee72"
|
||||
|
||||
- file:
|
||||
state: absent
|
||||
path: "{{item}}"
|
||||
with_items:
|
||||
- /tmp/copy-tiny-file
|
||||
- /tmp/copy-tiny-file.out
|
||||
- /tmp/copy-no-mode
|
||||
- /tmp/copy-no-mode.out
|
||||
- /tmp/copy-with-mode
|
||||
- /tmp/copy-with-mode.out
|
||||
- /tmp/copy-large-file
|
||||
- /tmp/copy-large-file.out
|
||||
- /tmp/copy-tiny-inline-file.out
|
||||
- /tmp/copy-large-inline-file
|
||||
- /tmp/copy-large-inline-file.out
|
||||
|
||||
# end of cleaning out files (again)
|
||||
@ -0,0 +1,117 @@
|
||||
# Verify action plugins still set file modes correctly even though
|
||||
# fixup_perms2() avoids setting execute bit despite being asked to.
|
||||
|
||||
- name: integration/action/fixup_perms2__copy.yml
|
||||
hosts: test-targets
|
||||
any_errors_fatal: true
|
||||
tasks:
|
||||
- name: Get default remote file mode
|
||||
shell: python -c 'import os; print("%04o" % (int("0666", 8) & ~os.umask(0)))'
|
||||
register: py_umask
|
||||
|
||||
- name: Set default file mode
|
||||
set_fact:
|
||||
mode: "{{py_umask.stdout}}"
|
||||
|
||||
#
|
||||
# copy module (no mode).
|
||||
#
|
||||
|
||||
- name: "Copy files (no mode)"
|
||||
copy:
|
||||
content: ""
|
||||
dest: /tmp/copy-no-mode
|
||||
|
||||
- stat: path=/tmp/copy-no-mode
|
||||
register: out
|
||||
- assert:
|
||||
that:
|
||||
- out.stat.mode == mode
|
||||
|
||||
#
|
||||
# copy module (explicit mode).
|
||||
#
|
||||
|
||||
- name: "Copy files from content: arg"
|
||||
copy:
|
||||
content: ""
|
||||
mode: 0400
|
||||
dest: /tmp/copy-with-mode
|
||||
|
||||
- stat: path=/tmp/copy-with-mode
|
||||
register: out
|
||||
- assert:
|
||||
that:
|
||||
- out.stat.mode == "0400"
|
||||
|
||||
#
|
||||
# copy module (existing disk files, no mode).
|
||||
#
|
||||
|
||||
- file:
|
||||
path: /tmp/weird-mode.out
|
||||
state: absent
|
||||
|
||||
- name: Create local test file.
|
||||
connection: local
|
||||
copy:
|
||||
content: "weird mode"
|
||||
dest: "/tmp/weird-mode"
|
||||
mode: "1462"
|
||||
|
||||
- copy:
|
||||
src: "/tmp/weird-mode"
|
||||
dest: "/tmp/weird-mode.out"
|
||||
|
||||
- stat:
|
||||
path: "/tmp/weird-mode.out"
|
||||
register: out
|
||||
- assert:
|
||||
that:
|
||||
- out.stat.mode == mode
|
||||
|
||||
#
|
||||
# copy module (existing disk files, preserve mode).
|
||||
#
|
||||
|
||||
- copy:
|
||||
src: "/tmp/weird-mode"
|
||||
dest: "/tmp/weird-mode"
|
||||
mode: preserve
|
||||
|
||||
- stat:
|
||||
path: "/tmp/weird-mode"
|
||||
register: out
|
||||
- assert:
|
||||
that:
|
||||
- out.stat.mode == "1462"
|
||||
|
||||
#
|
||||
# copy module (existing disk files, explicit mode).
|
||||
#
|
||||
|
||||
- copy:
|
||||
src: "/tmp/weird-mode"
|
||||
dest: "/tmp/weird-mode"
|
||||
mode: "1461"
|
||||
|
||||
- stat:
|
||||
path: "/tmp/weird-mode"
|
||||
register: out
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- out.stat.mode == "1461"
|
||||
|
||||
- file:
|
||||
state: absent
|
||||
path: "{{item}}"
|
||||
with_items:
|
||||
- /tmp/weird-mode
|
||||
- /tmp/weird-mode.out
|
||||
- /tmp/copy-no-mode
|
||||
- /tmp/copy-no-mode.out
|
||||
- /tmp/copy-with-mode
|
||||
- /tmp/copy-with-mode.out
|
||||
|
||||
# end of cleaning out files
|
||||
@ -1,63 +1,156 @@
|
||||
#
|
||||
# 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: tmp_path
|
||||
|
||||
- name: "Find regular temp path (new task)"
|
||||
action_passthrough:
|
||||
method: _make_tmp_path
|
||||
register: tmp_path2
|
||||
|
||||
- name: "Find good temp path"
|
||||
set_fact:
|
||||
good_temp_path: "{{tmp_path.result|dirname}}"
|
||||
|
||||
- name: "Find good temp path (new task)"
|
||||
set_fact:
|
||||
good_temp_path2: "{{tmp_path2.result|dirname}}"
|
||||
|
||||
- name: "Verify common base path for both tasks"
|
||||
assert:
|
||||
that:
|
||||
- good_temp_path == good_temp_path2
|
||||
|
||||
- name: "Verify different subdir for both tasks"
|
||||
assert:
|
||||
that:
|
||||
- tmp_path.result != tmp_path2.result
|
||||
|
||||
#
|
||||
# Verify subdirectory removal.
|
||||
#
|
||||
|
||||
- name: Stat temp path
|
||||
stat:
|
||||
path: "{{tmp_path.result}}"
|
||||
register: stat1
|
||||
|
||||
- name: Stat temp path (new task)
|
||||
stat:
|
||||
path: "{{tmp_path2.result}}"
|
||||
register: stat2
|
||||
|
||||
- name: "Verify neither subdir exists any more"
|
||||
assert:
|
||||
that:
|
||||
- not stat1.stat.exists
|
||||
- not stat2.stat.exists
|
||||
|
||||
#
|
||||
# Verify good directory persistence.
|
||||
#
|
||||
|
||||
- name: Stat good temp path (new task)
|
||||
stat:
|
||||
path: "{{good_temp_path}}"
|
||||
register: stat
|
||||
|
||||
- name: "Verify good temp path is persistent"
|
||||
assert:
|
||||
that:
|
||||
- stat.stat.exists
|
||||
|
||||
#
|
||||
# Write some junk into the temp path.
|
||||
#
|
||||
|
||||
- name: "Write junk to temp path and verify it disappears"
|
||||
custom_python_run_script:
|
||||
script: |
|
||||
from ansible.module_utils.basic import get_module_path
|
||||
path = get_module_path() + '/foo.txt'
|
||||
result['path'] = path
|
||||
open(path, 'w').write("bar")
|
||||
register: out
|
||||
|
||||
- name: "Verify junk disappeared."
|
||||
stat:
|
||||
path: "{{out.path}}"
|
||||
register: out
|
||||
|
||||
- assert:
|
||||
# This string must match ansible.cfg::remote_tmp
|
||||
that: out.result.startswith("{{user_facts.ansible_facts.ansible_user_dir}}/.ansible/mitogen-tests/")
|
||||
that:
|
||||
- not out.stat.exists
|
||||
|
||||
- stat:
|
||||
path: "{{out.result}}"
|
||||
register: st
|
||||
#
|
||||
# root
|
||||
#
|
||||
|
||||
- assert:
|
||||
that: st.stat.exists and st.stat.isdir and st.stat.mode == "0700"
|
||||
- name: "Find root temp path"
|
||||
become: true
|
||||
action_passthrough:
|
||||
method: _make_tmp_path
|
||||
register: tmp_path_root
|
||||
|
||||
- file:
|
||||
path: "{{out.result}}"
|
||||
state: absent
|
||||
- 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:
|
||||
method: _make_tmp_path
|
||||
register: out
|
||||
- name: "Try writing to temp directory for the readonly_homedir user"
|
||||
become: true
|
||||
become_user: mitogen__readonly_homedir
|
||||
custom_python_run_script:
|
||||
script: |
|
||||
from ansible.module_utils.basic import get_module_path
|
||||
path = get_module_path() + '/foo.txt'
|
||||
result['path'] = path
|
||||
open(path, 'w').write("bar")
|
||||
register: tmp_path
|
||||
|
||||
- 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 base dir
|
||||
#
|
||||
|
||||
- stat:
|
||||
path: "{{out.result}}"
|
||||
register: st
|
||||
- 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.startswith(good_temp_path2)
|
||||
- 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.startswith(good_temp_path2)
|
||||
- out.module_tmpdir.startswith(good_temp_path2)
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
#
|
||||
# Ensure _remove_tmp_path cleans up the temporary path.
|
||||
#
|
||||
#
|
||||
- name: integration/action/remove_tmp_path.yml
|
||||
hosts: test-targets
|
||||
any_errors_fatal: true
|
||||
tasks:
|
||||
- meta: end_play
|
||||
when: not is_mitogen
|
||||
|
||||
#
|
||||
# Use the copy module to cause a temporary directory to be created, and
|
||||
# return a result with a 'src' attribute pointing into that directory.
|
||||
#
|
||||
|
||||
- copy:
|
||||
dest: /tmp/remove_tmp_path_test
|
||||
content: "{{ 123123 | random }}"
|
||||
register: out
|
||||
|
||||
- stat:
|
||||
path: "{{out.src}}"
|
||||
register: out2
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- not out2.stat.exists
|
||||
|
||||
- stat:
|
||||
path: "{{out.src|dirname}}"
|
||||
register: out2
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- not out2.stat.exists
|
||||
|
||||
- file:
|
||||
path: /tmp/remove_tmp_path_test
|
||||
state: absent
|
||||
@ -0,0 +1,58 @@
|
||||
# Verify basic operation of the synchronize module.
|
||||
|
||||
- name: integration/action/synchronize.yml
|
||||
hosts: test-targets
|
||||
any_errors_fatal: true
|
||||
vars:
|
||||
ansible_user: mitogen__has_sudo_pubkey
|
||||
ansible_ssh_private_key_file: /tmp/synchronize-action-key
|
||||
tasks:
|
||||
# must copy git file to set proper file mode.
|
||||
- copy:
|
||||
dest: /tmp/synchronize-action-key
|
||||
src: ../../../data/docker/mitogen__has_sudo_pubkey.key
|
||||
mode: u=rw,go=
|
||||
connection: local
|
||||
|
||||
- file:
|
||||
path: /tmp/sync-test
|
||||
state: absent
|
||||
connection: local
|
||||
|
||||
- file:
|
||||
path: /tmp/sync-test
|
||||
state: directory
|
||||
connection: local
|
||||
|
||||
- copy:
|
||||
dest: /tmp/sync-test/item
|
||||
content: "item!"
|
||||
connection: local
|
||||
|
||||
- file:
|
||||
path: /tmp/sync-test.out
|
||||
state: absent
|
||||
become: true
|
||||
|
||||
- synchronize:
|
||||
private_key: /tmp/synchronize-action-key
|
||||
dest: /tmp/sync-test.out
|
||||
src: /tmp/sync-test/
|
||||
|
||||
- slurp:
|
||||
src: /tmp/sync-test.out/item
|
||||
register: out
|
||||
|
||||
- set_fact: outout="{{out.content|b64decode}}"
|
||||
|
||||
- assert:
|
||||
that: outout == "item!"
|
||||
|
||||
- file:
|
||||
path: "{{item}}"
|
||||
state: absent
|
||||
become: true
|
||||
with_items:
|
||||
- /tmp/synchronize-action-key
|
||||
- /tmp/sync-test
|
||||
- /tmp/sync-test.out
|
||||
@ -0,0 +1,22 @@
|
||||
---
|
||||
|
||||
- shell: dd if=/dev/urandom of=/tmp/{{file_name}} bs=1024 count={{file_size}}
|
||||
args:
|
||||
creates: /tmp/{{file_name}}
|
||||
connection: local
|
||||
|
||||
- copy:
|
||||
dest: /tmp/{{file_name}}.out
|
||||
src: /tmp/{{file_name}}
|
||||
|
||||
- stat: path=/tmp/{{file_name}}
|
||||
register: original
|
||||
connection: local
|
||||
|
||||
- stat: path=/tmp/{{file_name}}.out
|
||||
register: copied
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- original.stat.checksum == copied.stat.checksum
|
||||
- original.stat.mtime|int == copied.stat.mtime|int
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue