aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/test/docker/docker_test.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/docker/docker_test.py')
-rw-r--r--src/test/docker/docker_test.py158
1 files changed, 158 insertions, 0 deletions
diff --git a/src/test/docker/docker_test.py b/src/test/docker/docker_test.py
new file mode 100644
index 0000000000..a185e27775
--- /dev/null
+++ b/src/test/docker/docker_test.py
@@ -0,0 +1,158 @@
+# Copyright 2016 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""A simple test runner for docker (experimental)."""
+
+import copy
+import os
+import os.path
+import shlex
+import StringIO
+import subprocess
+import sys
+import threading
+
+from third_party.py import gflags
+
+gflags.DEFINE_multistring(
+ "image", [],
+ "The list of additional docker image to load (path to a docker_build "
+ "target), optional.")
+
+gflags.DEFINE_string(
+ "main", None,
+ "The main image to run (path to a docker_build target), mandatory.")
+gflags.MarkFlagAsRequired("main")
+
+gflags.DEFINE_string(
+ "cmd", None,
+ "A command to provide to the docker image, optional (default: use the "
+ "entrypoint).")
+
+gflags.DEFINE_string("docker", "docker", "Path to the docker client binary.")
+
+gflags.DEFINE_boolean("verbose", True, "Be verbose.")
+
+FLAGS = gflags.FLAGS
+
+LOCAL_IMAGE_PREFIX = "bazel/"
+
+
+def _copy_stream(in_stream, out_stream):
+ for c in iter(lambda: in_stream.read(1), ""):
+ out_stream.write(c)
+ out_stream.flush()
+
+
+def execute(command, stdout=sys.stdout, stderr=sys.stderr, env=None):
+ """Execute a command while redirecting its output streams."""
+ if FLAGS.verbose:
+ print "Executing '%s'" % " ".join(command)
+ p = subprocess.Popen(command,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ env=env)
+ t1 = threading.Thread(target=_copy_stream, args=[p.stdout, stdout])
+ t2 = threading.Thread(target=_copy_stream, args=[p.stderr, stderr])
+ t1.daemon = True
+ t2.daemon = True
+ t1.start()
+ t2.start()
+ p.wait()
+ t1.join()
+ t2.join()
+ return p.returncode
+
+
+def load_image(image):
+ """Load a docker image using the runner provided by docker_build."""
+ tag = LOCAL_IMAGE_PREFIX + ":".join(image.rsplit("/", 1))
+ err = StringIO.StringIO()
+ env = copy.deepcopy(os.environ)
+ env["DOCKER"] = FLAGS.docker
+ ret = execute([image], stderr=err, env=env)
+ if ret != 0:
+ sys.stderr.write("Error loading image %s (return code: %s):\n" %
+ (image, ret))
+ sys.stderr.write(err.getvalue())
+ return None
+ return tag
+
+
+def load_images(images):
+ """Load a series of docker images using the docker_build's runner."""
+ print "### Image loading ###"
+ return [load_image(image) for image in images]
+
+
+def cleanup_images(tags):
+ """Remove docker tags and images previously loaded."""
+ print "### Image cleanup ###"
+ for tag in tags:
+ if isinstance(tag, basestring):
+ execute([FLAGS.docker, "rmi", tag])
+
+
+def run_image(tag):
+ """Run a docker image, in background."""
+ print "Running " + tag
+ out = StringIO.StringIO()
+ err = StringIO.StringIO()
+ process = execute([FLAGS.docker, "run", "--rm", tag], out, err)
+ if process.wait() != 0:
+ sys.stderr.write("Error running docker run on %s:\n" % tag)
+ sys.stderr.write(err.getvalue())
+ return None
+ else:
+ return out.getvalue().strip()
+
+
+def run_images(tags):
+ """Run a list of docker images, in background."""
+ print "### Running images ###"
+ return [run_image(tag) for tag in tags]
+
+
+def cleanup_containers(containers):
+ """Kill containers."""
+ print "### Containers cleanup ###"
+ for c in containers:
+ if isinstance(c, basestring):
+ execute([FLAGS.docker, "kill", c])
+
+
+def main(unused_argv):
+ tags = load_images([FLAGS.main] + FLAGS.image)
+ if None in tags:
+ cleanup_images(tags)
+ return -1
+ try:
+ containers = run_images(tags[1:])
+ ret = -1
+ if None not in containers:
+ print "### Running main container ###"
+ ret = execute([
+ FLAGS.docker,
+ "run",
+ "--rm",
+ "--attach=STDOUT",
+ "--attach=STDERR", tags[0]
+ ] + ([] if FLAGS.cmd is None else shlex.split(FLAGS.cmd)))
+ finally:
+ cleanup_containers(containers)
+ cleanup_images(tags)
+ return ret
+
+
+if __name__ == "__main__":
+ sys.exit(main(FLAGS(sys.argv)))