Refactor docker tests to be buildable without docker.

This make this build a bit non-deterministic since the way
it will be built depend on wether docker is installed or not.

We now use the docker_pull from bazelbuild/continuous-integration
to generate the base docker images and generate a fake docker binary
that just error out when docker is not found so test will fails.

Note that the docker_pull is set to timeout after an hour and
can be extremely slow so we also set it to non quiet.

Fixes #3278.

Change-Id: I98d5b436e424f53981e113c1a4fd1346a5564df7
PiperOrigin-RevId: 162476033
diff --git a/WORKSPACE b/WORKSPACE
index 08fd585..a86ba4e 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -35,8 +35,9 @@
 
 # For src/test/docker/...
 load("//src/test/docker:docker_repository.bzl", "docker_repository")
-
 docker_repository()
+load("//src/test/docker:flavours.bzl", "pull_images_for_docker_tests")
+pull_images_for_docker_tests()
 
 # To run the Android integration tests in //src/test/shell/bazel/android:all or
 # build the Android sample app in //examples/android/java/bazel:hello_world
diff --git a/src/test/docker/BUILD b/src/test/docker/BUILD
index 7393126..e489a6b 100644
--- a/src/test/docker/BUILD
+++ b/src/test/docker/BUILD
@@ -1,43 +1,6 @@
 load("//tools/build_defs/docker:docker.bzl", "docker_build")
 load("//tools/build_defs/pkg:pkg.bzl", "pkg_tar")
-
-# This is totally non hermetic, we should really replace that by a
-# docker_pull rule.
-FLAVOURS = [f[f.find(".") + 1:] for f in glob(["Dockerfile.*"])]
-
-[
-    # This is totally non hermetic.
-    genrule(
-        name = "docker-" + flavour,
-        srcs = ["Dockerfile." + flavour],
-        outs = ["docker-%s.tar" % flavour],
-        cmd = "\n".join([
-            "DIR=\"$$(dirname $<)\"",
-            "IMG=\"$$(basename $<)\"",
-            "DOCKER=\"$${PWD}/$(location @docker//:docker)\"",
-            "(",
-            "   cd $$DIR",
-            "   $$DOCKER build -f $$IMG -t bazel_tools_cpp_test:%s ." % flavour,
-            ")",
-            "$$DOCKER save bazel_tools_cpp_test:%s > $@" % flavour,
-        ]),
-        tags = ["local"],
-        # Docker needs to knows how to contact the daemon from the environment.
-        # @docker//:docker point to a docker wrapper that copy the environment
-        # of the user.
-        tools = ["@docker//:docker"],
-    )
-    for flavour in FLAVOURS
-]
-
-# Just to avoid re-reading docker images all the time
-[
-    docker_build(
-        name = "base-" + flavour,
-        base = "docker-" + flavour,
-    )
-    for flavour in FLAVOURS
-]
+load("//src/test/docker:flavours.bzl", "FLAVOURS")
 
 pkg_tar(
     name = "bazel_cc_configure",
@@ -49,7 +12,7 @@
 [
     [docker_build(
         name = "bazel_cc_configure-%s-%s" % (flavour, mode),
-        base = ":base-" + flavour,
+        base = "@docker-%s//:image" % flavour,
         entrypoint = "/opt/workspace/compile.sh && ./output/bazel test ${EXTRA_BAZEL_ARGS} //examples/cpp:hello-success_test",
         env = {
             "EXTRA_BAZEL_ARGS": "--spawn_strategy=standalone --genrule_strategy=standalone -c %s" % mode,
diff --git a/src/test/docker/docker_pull.bzl b/src/test/docker/docker_pull.bzl
new file mode 100644
index 0000000..8fab57f
--- /dev/null
+++ b/src/test/docker/docker_pull.bzl
@@ -0,0 +1,80 @@
+# 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.
+
+"""Quick and not really nice docker_pull rules based on the docker daemon."""
+
+def _impl(repository_ctx):
+  docker = repository_ctx.which("docker")
+  if docker == None and repository_ctx.attr.optional:
+    repository_ctx.file("BUILD", """
+load("@bazel_tools//tools/build_defs/docker:docker.bzl", "docker_build")
+
+# an empty image to still allow building despite not having the base
+# image.
+docker_build(
+    name = "image",
+    visibility = ['//visibility:public'],
+)
+""")
+    repository_ctx.file("image.tar")
+    return
+
+  repository_ctx.file("BUILD", """
+load("@bazel_tools//tools/build_defs/docker:docker.bzl", "docker_build")
+
+docker_build(
+    name = "image",
+    base = ":base.tar",
+    visibility = ["//visibility:public"],
+)
+""")
+  tag = repository_ctx.attr.tag
+  cmd = "pull"
+  if repository_ctx.attr.dockerfile:
+    dockerfile = repository_ctx.path(repository_ctx.attr.dockerfile)
+    cmd = "build"
+    print("Running `docker build`")
+    result = repository_ctx.execute([
+        docker,
+        "build",
+        "-q",
+        "-t",
+        tag,
+        "-f",
+        dockerfile,
+        dockerfile.dirname,
+    ], quiet=False, timeout=3600)
+  else:
+    print("Running `docker pull`")
+    result = repository_ctx.execute([docker, "pull", tag], quiet=False, timeout=3600)
+  if result.return_code:
+    fail("docker %s failed with error code %s:\n%s" % (
+        cmd,
+        result.return_code,
+        result.stderr))
+  result = repository_ctx.execute([
+      docker, "save", "-o", repository_ctx.path("base.tar"), tag])
+  if result.return_code:
+    fail("docker save failed with error code %s:\n%s" % (
+        result.return_code,
+        result.stderr))
+
+docker_pull = repository_rule(
+    implementation = _impl,
+    attrs = {
+        "tag": attr.string(mandatory=True),
+        "dockerfile": attr.label(default=None),
+        "optional": attr.bool(default=False),
+    },
+)
diff --git a/src/test/docker/docker_repository.bzl b/src/test/docker/docker_repository.bzl
index 4f96170..1b49d1a 100644
--- a/src/test/docker/docker_repository.bzl
+++ b/src/test/docker/docker_repository.bzl
@@ -20,11 +20,17 @@
     # on it, silently ignoring.
     ctx.file("BUILD",
              "\n".join([
-                 "filegroup(",
+                 "sh_binary(",
                  "    name = 'docker',",
+                 "    srcs = ['docker.sh'],",
                  "    visibility = ['//visibility:public'],",
                  ")"
                  ]))
+    ctx.file("docker.sh", "\n".join([
+        "#!/bin/bash",
+        "echo 'ERROR: docker is not installed' >&2",
+        "exit 1"
+        ]))
   else:
     exports = []
     for k in ctx.os.environ:
@@ -55,3 +61,4 @@
 def docker_repository():
   """Declare a @docker repository that provide a docker binary."""
   docker_repository_(name = "docker")
+
diff --git a/src/test/docker/flavours.bzl b/src/test/docker/flavours.bzl
new file mode 100644
index 0000000..713c7c5
--- /dev/null
+++ b/src/test/docker/flavours.bzl
@@ -0,0 +1,37 @@
+# Copyright 2017 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.
+"""Create base images for docker tests."""
+# TODO(dmarting): Right now we use a custom docker_pull that can build
+# docker images, which is not reproducible and as a high cost, ideally
+# we would switch to the docker_pull from bazelbuild/rules_docker but
+# we do not have an easy mean to create and maintain the images we need
+# for those tests.
+load("//src/test/docker:docker_pull.bzl", "docker_pull")
+
+FLAVOURS = [
+    "centos6.7",
+    "debian-stretch",
+    "fedora23",
+    "ubuntu-15.04",
+    "ubuntu-16.04",
+]
+
+def pull_images_for_docker_tests():
+  for flavour in FLAVOURS:
+    docker_pull(
+        name = "docker-" + flavour,
+        tag = "bazel_tools_cpp_test:" + flavour,
+        dockerfile = "//src/test/docker:Dockerfile." + flavour,
+        optional = True,
+    )