| |
@@ -25,81 +25,82 @@
|
| |
|
| |
import argparse
|
| |
import errno
|
| |
- import json
|
| |
import os
|
| |
+ import re
|
| |
import shutil
|
| |
import shlex
|
| |
- import signal
|
| |
import subprocess
|
| |
import sys
|
| |
import tempfile
|
| |
import time
|
| |
- import distutils.util
|
| |
+ import yaml
|
| |
+
|
| |
+ CLEANUP_CIDS = []
|
| |
|
| |
|
| |
def main(argv):
|
| |
parser = argparse.ArgumentParser(description="Inventory for a container image in a registry")
|
| |
- parser.add_argument("--list", action="store_true", help="Verbose output")
|
| |
- parser.add_argument('--host', help="Get host variables")
|
| |
+ parser.add_argument("--output", "-o", default="inventory.yaml", help="Inventory output file")
|
| |
parser.add_argument('--docker-extra-args', help="Extra docker arguments for launching container",
|
| |
default=os.environ.get("TEST_DOCKER_EXTRA_ARGS", ""))
|
| |
parser.add_argument("subjects", nargs="*", default=shlex.split(os.environ.get("TEST_SUBJECTS", "")))
|
| |
opts = parser.parse_args()
|
| |
|
| |
+ if os.path.isfile("inventory"):
|
| |
+ with open("inventory") as f:
|
| |
+ for line in f:
|
| |
+ m = re.match("export TEST_DOCKER_EXTRA_ARGS=(.*)", line)
|
| |
+ if m:
|
| |
+ opts.docker_extra_args = m.group(1).strip("\"")
|
| |
+
|
| |
try:
|
| |
- if opts.host:
|
| |
- _, data = inv_host(opts.host, opts.docker_extra_args)
|
| |
- else:
|
| |
- data = inv_list(opts.subjects, opts.docker_extra_args)
|
| |
- sys.stdout.write(json.dumps(data, indent=4, separators=(',', ': ')))
|
| |
+ data = inv_list(opts.subjects, opts.docker_extra_args)
|
| |
+ if not data:
|
| |
+ return 0
|
| |
+ inv_file = opts.output
|
| |
+ with open(inv_file, "w") as f:
|
| |
+ yaml.dump(data, f, default_flow_style=False)
|
| |
+ print("INFO: Successfully created {0}".format(inv_file))
|
| |
except RuntimeError as ex:
|
| |
- sys.stderr.write("{0}: {1}\n".format(os.path.basename(sys.argv[0]), str(ex)))
|
| |
+ for cid in CLEANUP_CIDS:
|
| |
+ subprocess.call(["/usr/bin/docker", "rm", "-f", cid])
|
| |
+ sys.stderr.write("FAIL {0}: {1}\n".format(os.path.basename(sys.argv[0]), str(ex)))
|
| |
return 1
|
| |
|
| |
return 0
|
| |
|
| |
|
| |
def inv_list(subjects, docker_extra_args):
|
| |
- hosts = []
|
| |
- variables = {}
|
| |
+ if not subjects:
|
| |
+ return None
|
| |
+ hosts = {}
|
| |
for subject in subjects:
|
| |
- if subject.startswith("docker:"):
|
| |
- image = subject[7:]
|
| |
- name, host_vars = inv_host(image, docker_extra_args)
|
| |
- if host_vars:
|
| |
- hosts.append(name)
|
| |
- variables[name] = host_vars
|
| |
- return {"localhost": {"hosts": hosts, "vars": {}},
|
| |
- "subjects": {"hosts": hosts, "vars": {}},
|
| |
- "_meta": {"hostvars": variables}}
|
| |
-
|
| |
-
|
| |
- def inv_host(image, docker_extra_args):
|
| |
- null = open(os.devnull, 'w')
|
| |
+ if not subject.startswith("docker:"):
|
| |
+ continue
|
| |
+ image = subject[7:]
|
| |
+ name, host_vars = create_host(image, docker_extra_args)
|
| |
+ if host_vars:
|
| |
+ hosts[name] = host_vars
|
| |
+ if not hosts:
|
| |
+ return None
|
| |
+ inventory = {"localhost": {"hosts": hosts}}
|
| |
+ return inventory
|
| |
|
| |
- try:
|
| |
- tty = os.open("/dev/tty", os.O_WRONLY)
|
| |
- os.dup2(tty, 2)
|
| |
- except OSError:
|
| |
- tty = None
|
| |
- pass
|
| |
+
|
| |
+ def create_host(image, docker_extra_args):
|
| |
+ global CLEANUP_CIDS
|
| |
+ null = open(os.devnull, 'w')
|
| |
|
| |
directory = tempfile.mkdtemp(prefix="inventory-docker")
|
| |
cidfile = os.path.join(directory, "cid")
|
| |
|
| |
- # Determine if container should be kept available for diagnosis after completion
|
| |
- try:
|
| |
- diagnose = distutils.util.strtobool(os.getenv("TEST_DEBUG", "0"))
|
| |
- except ValueError:
|
| |
- diagnose = 0
|
| |
-
|
| |
# Check for any additional arguments to include when starting docker container
|
| |
try:
|
| |
extra_arg_list = shlex.split(docker_extra_args)
|
| |
except ValueError:
|
| |
raise RuntimeError("Could not parse DOCKER_EXTRA_ARGS")
|
| |
|
| |
- sys.stderr.write("Launching Docker container for {0}\n".format(image))
|
| |
+ print("Launching Docker container for {0}\n".format(image))
|
| |
|
| |
# Make sure the docker service is running
|
| |
cmd = [
|
| |
@@ -130,7 +131,9 @@
|
| |
raise RuntimeError("Could not find container file for launched container")
|
| |
|
| |
with open(cidfile, "r") as f:
|
| |
- name = f.read().strip()
|
| |
+ name = f.read().strip()[:12]
|
| |
+ shutil.rmtree(directory)
|
| |
+ CLEANUP_CIDS.append(name)
|
| |
|
| |
# Need to figure out what python interpreter to use
|
| |
interpreters = ["/usr/bin/python2", "/usr/bin/python3"]
|
| |
@@ -146,89 +149,23 @@
|
| |
sys.stderr.write("ERROR: Could not set ansible_python_interpreter")
|
| |
return None
|
| |
|
| |
- python2_deps = ["python2-dnf", "libselinux-python"]
|
| |
- python3_deps = ["python3-dnf", "python3-libselinux"]
|
| |
-
|
| |
- # Now install the necessary stuff in the container :S
|
| |
- install = [
|
| |
- "/usr/bin/docker", "exec", "--user=root", name, "/usr/bin/yum", "-y", "install"
|
| |
- ]
|
| |
- if ansible_python_interpreter == "/usr/bin/python2":
|
| |
- install.extend(python2_deps)
|
| |
- if ansible_python_interpreter == "/usr/bin/python3":
|
| |
- install.extend(python3_deps)
|
| |
- try:
|
| |
- subprocess.check_call(install, stdout=sys.stderr.fileno())
|
| |
- except subprocess.CalledProcessError:
|
| |
- # Could not install necessary packages to run the tests.
|
| |
- # Need to stop and remove the container.
|
| |
- subprocess.call(["/usr/bin/docker", "rm", "-f", name], stdout=null)
|
| |
- raise RuntimeError("Could not install Ansible dependencies in launched container")
|
| |
-
|
| |
- # Directory to place artifacts
|
| |
- artifacts = os.environ.get("TEST_ARTIFACTS", os.path.join(os.getcwd(), "artifacts"))
|
| |
-
|
| |
# The variables
|
| |
variables = {
|
| |
"ansible_connection": "docker",
|
| |
- "ansible_python_interpreter": ansible_python_interpreter
|
| |
+ "ansible_python_interpreter": ansible_python_interpreter,
|
| |
}
|
| |
|
| |
- # Process of our parent
|
| |
- ppid = os.getppid()
|
| |
-
|
| |
- child = os.fork()
|
| |
- if child:
|
| |
- return name, variables
|
| |
-
|
| |
- # Daemonize and watch the processes
|
| |
- os.chdir("/")
|
| |
- os.setsid()
|
| |
- os.umask(0)
|
| |
-
|
| |
- if tty is None:
|
| |
- tty = null.fileno()
|
| |
-
|
| |
- # Duplicate standard input to standard output and standard error.
|
| |
- os.dup2(null.fileno(), 0)
|
| |
- os.dup2(tty, 1)
|
| |
- os.dup2(tty, 2)
|
| |
-
|
| |
- # Now wait for the parent process to go away, then kill the VM
|
| |
- while True:
|
| |
- time.sleep(3)
|
| |
-
|
| |
- try:
|
| |
- os.kill(ppid, 0)
|
| |
- except OSError:
|
| |
- break # Either of the processes no longer exist
|
| |
-
|
| |
- if diagnose:
|
| |
- sys.stderr.write("\n")
|
| |
- sys.stderr.write("DIAGNOSE: docker exec -it {0} /bin/bash\n".format(name))
|
| |
- sys.stderr.write("DIAGNOSE: kill {0} # when finished\n".format(os.getpid()))
|
| |
-
|
| |
- def _signal_handler(*args):
|
| |
- sys.stderr.write("\nDIAGNOSE ending...\n")
|
| |
-
|
| |
- signal.signal(signal.SIGTERM, _signal_handler)
|
| |
- signal.pause()
|
| |
-
|
| |
- # Dump the container logs
|
| |
+ artifacts = os.environ.get("TEST_ARTIFACTS", os.path.join(os.getcwd(), "artifacts"))
|
| |
try:
|
| |
os.makedirs(artifacts)
|
| |
except OSError as exc:
|
| |
if exc.errno != errno.EEXIST or not os.path.isdir(artifacts):
|
| |
raise
|
| |
- log = os.path.join(artifacts, "{0}.log".format(os.path.basename(image)))
|
| |
-
|
| |
- # Kill the container
|
| |
+ log = os.path.join(artifacts, "docker-{0}.log".format(os.path.basename(name)))
|
| |
with open(log, "w") as f:
|
| |
subprocess.call(["/usr/bin/docker", "logs", name], stdout=f.fileno())
|
| |
- subprocess.call(["/usr/bin/docker", "rm", "-f", name], stdout=null)
|
| |
|
| |
- shutil.rmtree(directory)
|
| |
- sys.exit(0)
|
| |
+ return name, variables
|
| |
|
| |
|
| |
if __name__ == '__main__':
|
| |