Migrate docker_config.bzl to use new package manager.

New package manager is at
https://github.com/GoogleCloudPlatform/base-images-docker/tree/master/package_managers.

Change-Id: Ia2aca9e68b07ae6197a2bf03425c5c31f21d3e60
diff --git a/WORKSPACE b/WORKSPACE
index 467d211..61bc489 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -65,6 +65,25 @@
     tag = "16.04",
 )
 
+http_archive(
+    name = "debian_docker",
+    sha256 = "4c479f8e4889ae950cd3698721d412ed120285bc3bbee23a545f2d41e0409bc0",
+    strip_prefix = "base-images-docker-9197e457d3fbad9311d521500a1daf5bf436ff4e",
+    urls = ["https://github.com/GoogleCloudPlatform/base-images-docker/archive/9197e457d3fbad9311d521500a1daf5bf436ff4e.tar.gz"],
+)
+
+http_file(
+    name = "bazel_gpg",
+    sha256 = "e0e806160454a3e5e308188439525896bf9881f1f2f0b887192428f517da4131",
+    url = "https://bazel.build/bazel-release.pub.gpg",
+)
+
+http_file(
+    name = "launchpad_openjdk_gpg",
+    sha256 = "54b6274820df34a936ccc6f5cb725a9b7bb46075db7faf0ef7e2d86452fa09fd",
+    url = "http://keyserver.ubuntu.com/pks/lookup?op=get&fingerprint=on&search=0xEB9B1D8886F44E2A",
+)
+
 # Use http_archive rule instead of git_repository rule
 # https://docs.bazel.build/versions/master/be/workspace.html#git_repository
 http_archive(
diff --git a/rules/BUILD b/rules/BUILD
index 01e4d9d..89b55cf 100644
--- a/rules/BUILD
+++ b/rules/BUILD
@@ -17,26 +17,8 @@
 package(default_visibility = ["//visibility:public"])
 
 load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar")
-load(
-    "@bazel_toolchains//rules:docker_config.bzl",
-    "all_tools",
-    "default_tools",
-    "docker_toolchain_autoconfig",
-)
+load("@bazel_toolchains//rules:docker_config.bzl", "docker_toolchain_autoconfig")
 load("@bazel_toolchains//rules:environments.bzl", "clang_env", "gcc_env")
-load("@distroless//cacerts:cacerts.bzl", "cacerts")
-load(
-    "@jessie_package_bundle//file:packages.bzl",
-    jessie_packages = "packages",
-)
-load(
-    "@trusty_package_bundle//file:packages.bzl",
-    trusty_packages = "packages",
-)
-load(
-    "@xenial_package_bundle//file:packages.bzl",
-    xenial_packages = "packages",
-)
 
 pkg_tar(
     name = "cc-sample-project-tar",
@@ -46,28 +28,30 @@
     ],
 )
 
-cacerts(
-    name = "jessie_cacerts",
-    deb = jessie_packages["ca-certificates"],
-)
-
-cacerts(
-    name = "trusty_cacerts",
-    deb = trusty_packages["ca-certificates"],
-)
-
-cacerts(
-    name = "xenial_cacerts",
-    deb = xenial_packages["ca-certificates"],
-)
-
 docker_toolchain_autoconfig(
     name = "debian8-clang-0.7.0-autoconfig",
     base = "@debian8-clang//image",
     bazel_version = "0.7.0",
-    distro = "jessie",
     env = clang_env(),
-    install_tools = default_tools,
+    packages = [
+        "bazel",
+        "ca-certificates-java=20161107'*'",
+        "curl",
+        "git",
+        "openjdk-8-jdk-headless",
+        "openjdk-8-jre-headless",
+        "python-dev",
+        "unzip",
+        "wget",
+        "zip",
+    ],
+    additional_repos = [
+        "deb http://deb.debian.org/debian jessie-backports main",
+        "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8",
+    ],
+    keys = [
+        "@bazel_gpg//file",
+    ],
     tags = ["manual"],
 )
 
@@ -75,9 +59,26 @@
     name = "trusty-gcc-0.7.0-autoconfig",
     base = "@official_trusty//image",
     bazel_version = "0.7.0",
-    distro = "trusty",
     env = gcc_env(),
-    install_tools = all_tools,
+    packages = [
+        "bazel",
+        "curl",
+        "gcc",
+        "git",
+        "openjdk-8-jdk",
+        "python-dev",
+        "unzip",
+        "wget",
+        "zip",
+    ],
+    additional_repos = [
+        "deb [arch=amd64] http://ppa.launchpad.net/openjdk-r/ppa/ubuntu/ trusty main",
+        "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8",
+    ],
+    keys = [
+        "@launchpad_openjdk_gpg//file",
+        "@bazel_gpg//file",
+    ],
     tags = ["manual"],
 )
 
@@ -85,8 +86,23 @@
     name = "xenial-gcc-0.7.0-autoconfig",
     base = "@official_xenial//image",
     bazel_version = "0.7.0",
-    distro = "xenial",
     env = gcc_env(),
-    install_tools = all_tools,
+    packages = [
+        "bazel",
+        "curl",
+        "gcc",
+        "git",
+        "openjdk-8-jdk",
+        "python-dev",
+        "unzip",
+        "wget",
+        "zip",
+    ],
+    additional_repos = [
+        "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8",
+    ],
+    keys = [
+        "@bazel_gpg//file",
+    ],
     tags = ["manual"],
 )
diff --git a/rules/docker_config.bzl b/rules/docker_config.bzl
index 6cb0c44..a55d42d 100644
--- a/rules/docker_config.bzl
+++ b/rules/docker_config.bzl
@@ -15,10 +15,10 @@
 """Rules for generating toolchain configs for a Docker container.
 
 Exposes the docker_autoconfigure rule that does the following:
-- Receive a base container as main input. Base container should have a desired
-  set of toolchains (i.e., a C compiler, C libraries, etc) installed.
-- Create a container from the base container which includes debs packages to
-  run Bazel.
+- Receive a base container as main input. Base container could have a desired
+  set of toolchains (i.e., a C compiler, C libraries, java, python, zip, and
+  other tools) installed.
+- Optionally, install more debian packages in the base container.
 - Extend the container to install sources for a project.
 - Run a bazel command to build one or more remote repos inside the container.
 - Copy toolchain configs produced from the execution of Bazel
@@ -32,13 +32,20 @@
       base = "@my_image//image:image.tar",
       config_repos = ["local_config_cc", "<some_other_skylark_repo>"],
       git_repo = "https://github.com/some_git_repo",
-      distro = "trusty", # "trusty", "xenial" and "jessie" are supported
-      install_tools = ["bazel"], # list of tools that are necessary to run
-                                 # autoconfig that will be installed in the container
       env = {
           ... Dictionary of env variables to configure Bazel properly
               for the container
       },
+      packages = [
+          "package_1",
+          "package_2=version",
+      ],
+      additional_repos = [
+          "deb http://deb.debian.org/debian jessie-backports main",
+      ],
+      keys = [
+          "@some_gpg//file",
+      ],
   )
 
 To use the rule run:
@@ -55,58 +62,9 @@
     "@io_bazel_rules_docker//container:container.bzl",
     _container = "container",
 )
-load(
-    "@bazel_package_bundle//file:packages.bzl",
-    bazel_packages = "packages",
-)
-load(
-    "@jessie_package_bundle//file:packages.bzl",
-    jessie_packages = "packages",
-)
-load(
-    "@trusty_package_bundle//file:packages.bzl",
-    trusty_packages = "packages",
-)
-load(
-    "@xenial_package_bundle//file:packages.bzl",
-    xenial_packages = "packages",
-)
-load("@bazel_toolchains//skylib:packages.bzl",
-    "get_jessie_packages",
-    "get_trusty_packages",
-    "get_xenial_packages",
-)
-load(
-    "@bazel_toolchains//skylib:package_names.bzl",
-    "tool_names",
-    "jessie_tools",
-    "trusty_tools",
-    "xenial_tools",
-)
-
-# A couple of lists of tools that are commonly used.
-# Use all_tools if your container has very little other than a base image
-all_tools = [
-    "bazel",
-    "curl",
-    "git",
-    "gcc",
-    "java",
-    "python_dev",
-    "wget",
-    "zip",
-]
-
-# Use default_tools if your container has a C/C++ compiler and little else
-default_tools = [
-    "bazel",
-    "curl",
-    "git",
-    "java",
-    "python_dev",
-    "wget",
-    "zip",
-]
+load("@debian_docker//package_managers:download_pkgs.bzl", "download_pkgs")
+load("@debian_docker//package_managers:install_pkgs.bzl", "install_pkgs")
+load("@debian_docker//package_managers:apt_key.bzl", "add_apt_key")
 
 # External folder is set to be deprecated, lets keep it here for easy
 # refactoring
@@ -115,6 +73,7 @@
 
 # Name of the current workspace
 _WORKSPACE_NAME = "bazel_toolchains"
+
 _WORKSPACE_PREFIX = "@" + _WORKSPACE_NAME + "//"
 
 # Default cc project to use if no git_repo is provided.
@@ -130,6 +89,47 @@
     ".tar.xz",
 ]
 
+def _container_install_pkgs(name, base, packages, additional_repos, keys):
+  """Macro to download and install deb packages in a container.
+
+  The output image with packages installed will have name {name}.tar.
+
+  Args:
+    name: name of this rule. It is also the name of the output image.
+    base: the base layers on top of which to overlay a layer with the
+      desired packages.
+    packages: list of packages to fetch and install in the base image.
+    additional_repos: list of additional debian package repos to use,
+      in sources.list format.
+    keys: label of additional gpg keys to use while downloading packages.
+  """
+
+  # Create an intermediate image which includes additional gpg keys.
+  add_apt_key(
+      name = name + "_with_keys",
+      image = base,
+      keys = keys,
+  )
+
+  # Generate the script to download packages in the container and extract only
+  # the deb packages tarball as the output.
+  download_pkgs(
+      name = name + "_pkgs",
+      additional_repos = additional_repos,
+      image_tar = ":" + name + "_with_keys",
+      packages = packages,
+  )
+
+  # Execute the package installation script in the container and commit the
+  # resulting container locally as a new image named as {name}. The resulting
+  # image is also available as target :{name}.tar.
+  install_pkgs(
+      name = name,
+      image_tar = base,
+      installables_tar = ":" + name + "_pkgs.tar",
+      output_image_name = name,
+  )
+
 def _docker_toolchain_autoconfig_impl(ctx):
   """Implementation for the docker_toolchain_autoconfig rule.
 
@@ -270,17 +270,18 @@
 
 docker_toolchain_autoconfig_ = rule(
     attrs = _container.image.attrs + {
-        "distro": attr.string(),
         "config_repos": attr.string_list(["local_config_cc"]),
         "use_default_project": attr.bool(default = False),
         "git_repo": attr.string(),
-        "install_tools": attr.string_list(),
         "repo_pkg_tar": attr.label(allow_files = tar_filetype),
         "bazel_version": attr.string(),
         "bazel_rc_version": attr.string(),
         "use_bazel_head": attr.bool(default = False),
         "run_tpl": attr.label(allow_files = True),
         "setup_cmd": attr.string(default = "cd ."),
+        "packages": attr.string_list(),
+        "additional_repos": attr.string_list(),
+        "keys": attr.string_list(),
     },
     executable = True,
     outputs = _container.image.outputs + {
@@ -327,8 +328,6 @@
 # Attrs expected in the BUILD rule
 required_attrs = [
     "base",
-    "distro",
-    "install_tools",
 ]
 
 def docker_toolchain_autoconfig(**kwargs):
@@ -363,37 +362,9 @@
     **kwargs:
   Required Args
     name: A unique name for this rule.
-    base: Docker image base - the container with all tools
-        pre-installed for which a configuration will be generated
-    distro: the base distro for the docker container
-        ("jessie", "trusty", "xenial" are supported). If a different distro is used
-        you will need to extend this rule to add the correct packages.
-    install_tools: list of tools to install. The following tools
-        can be installed as part of this rule in the base container:
-          - bazel
-          - curl
-          - gcc
-          - git
-          - java
-          - python_dev
-          - wget
-          - zip
-        All tools are needed for proper execution of this rule, but
-        if the base container already has some of these tools installed you
-        should indicate so. For instance, to install all necessary tools except
-        git, gcc, python_dev and java use:
-          install_tools = ["bazel", "curl", "wget", "zip"]
-        Note: if container has a c/c++ compiler, do not request installation
-        of gcc.
-        Note: if the base container already has a tool installed and
-        installing of the tool is requested, behavior will be undefined
-        (e.g., an overriding package might break some other package that
-        depends on it in unexpected ways).
-        We also defined two macros for lists of tools that are commonly used.
-        Use all_tools if your container has very little other than a base image
-          all_tools = ["bazel", "curl", "git", "gcc", "java", "python_dev", "wget", "zip",]
-        Use default_tools if your container has a C/C++ compiler and little else
-          default_tools = ["bazel", "curl", "git", "java", "python_dev", "wget", "zip",]
+    base: Docker image base - optionally with all tools pre-installed for
+        which a configuration will be generated. Packages can also be installed
+        by listing them in the 'packages' attriute.
   Default Args:
     config_repos: a list of remote repositories. Autoconfig will run targets in
         each of these remote repositories and copy all contents to the mount
@@ -410,6 +381,10 @@
         to run autoconfigure targets.
     setup_cmd: a customized command that will run as the very first command
         inside the docker container.
+    packages: list of packages to fetch and install in the base image.
+    additional_repos: list of additional debian package repos to use,
+        in sources.list format.
+    keys: list of additional gpg keys to use while downloading packages.
   """
   for reserved in reserved_attrs:
     if reserved in kwargs:
@@ -423,8 +398,13 @@
   if "use_bazel_head" in kwargs and ("bazel_version" in kwargs or "bazel_rc_version" in kwargs):
     fail ("Only one of use_bazel_head or a combination of bazel_version and" +
           "bazel_rc_version can be set at a time.")
-  if kwargs["distro"] not in ["jessie", "trusty", "xenial"]:
-    fail("Only jessie, trusty and xenial distros are supported")
+
+  packages_is_empty = "packages" not in kwargs or kwargs["packages"] == []
+
+  if packages_is_empty and "additional_repos" in kwargs:
+    fail("'additional_repos' can only be specified when 'packages' is not empty.")
+  if packages_is_empty and "keys" in kwargs:
+    fail("'keys' can only be specified when 'packages' is not empty.")
 
   # If the git_repo was not provided, use the default autoconfig project
   if "git_repo" not in kwargs:
@@ -435,95 +415,29 @@
       _WORKSPACE_PREFIX + "rules:install_bazel_version.sh"
   ]
 
-  # Set up certificates if git or wget needs to be used
-  if tool_names.git in kwargs["install_tools"] or tool_names.wget in kwargs["install_tools"]:
-    if kwargs["distro"] == "jessie":
-      kwargs["tars"] = [_WORKSPACE_PREFIX + "rules:jessie_cacerts.tar"]
-    elif kwargs["distro"] == "trusty":
-      kwargs["tars"] = [_WORKSPACE_PREFIX + "rules:trusty_cacerts.tar"]
-    elif kwargs["distro"] == "xenial":
-      kwargs["tars"] = [_WORKSPACE_PREFIX + "rules:xenial_cacerts.tar"]
-
-  # Set symlinks and env vars for python / Java if installation was requested.
-  symlinks = {}
-  if tool_names.python_dev in kwargs["install_tools"]:
-    symlinks.update({"/usr/bin/python": "/usr/bin/python2.7"})
-  if tool_names.java in kwargs["install_tools"]:
-    symlinks.update({"/usr/bin/java": "/usr/lib/jvm/java-8-openjdk-amd64/bin/java"})
-  kwargs["symlinks"] = symlinks
-  if tool_names.java in kwargs["install_tools"]:
-    kwargs["env"].update({"JAVA_HOME": "/usr/lib/jvm/java-8-openjdk-amd64"})
-  # Set in "debs" the list with all packages we will install
-  debs = []
-  if kwargs["distro"] == "jessie":
-    debs = autoconf_jessie_packages(kwargs["install_tools"])
-  elif kwargs["distro"] == "trusty":
-    debs = autoconf_trusty_packages(kwargs["install_tools"])
-  elif kwargs["distro"] == "xenial":
-    debs = autoconf_xenial_packages(kwargs["install_tools"])
-  kwargs["debs"] = debs
-
   # The template for the main script to execute for this rule, which produces
   # the toolchain configs
   kwargs["run_tpl"] = _WORKSPACE_PREFIX + "rules:docker_config.sh.tpl"
+
+  # Do not install packags if 'packages' is not specified or is an ampty list.
+  if not packages_is_empty:
+    # "additional_repos" and "keys" are optional for docker_toolchain_autoconfig,
+    # but required for container_install_pkgs". Use empty lists as placeholder.
+    if "additional_repos" not in kwargs:
+      kwargs["additional_repos"] = []
+    if "keys" not in kwargs:
+      kwargs["keys"] = []
+
+    # Install packages in the base image.
+    _container_install_pkgs(
+      name = kwargs["name"] + "_image",
+      base = kwargs["base"],
+      packages = kwargs["packages"],
+      additional_repos = kwargs["additional_repos"],
+      keys = kwargs["keys"],
+    )
+
+    # Use the image with packages installed as the new base for autoconfiguring.
+    kwargs["base"] = ":" + kwargs["name"] + "_image.tar"
+
   docker_toolchain_autoconfig_(**kwargs)
-
-def autoconf_jessie_packages(install_tools):
-  """Return the list of debian packages given the name of tools for Jessie.
-
-  Args:
-    install_tools: a list of tool names.
-
-  Returns:
-    a list of debian packages to install
-  """
-  debs = []
-  for tool in install_tools:
-    if tool in jessie_tools().keys():
-      debs.extend(get_jessie_packages(jessie_tools()[tool]))
-    elif tool == tool_names.bazel:
-      debs.append(bazel_packages["bazel"])
-    else:
-      fail("Installation of %s was requested, but it is not supported for jessie distro" % tool)
-  # Remove duplicates using a depset
-  return depset(debs).to_list()
-
-def autoconf_trusty_packages(install_tools):
-  """Return the list of debian packages given the name of tools for Trusty.
-
-  Args:
-    install_tools: a list of tool names.
-
-  Returns:
-    a list of debian packages to install
-  """
-  debs = []
-  for tool in install_tools:
-    if tool in trusty_tools().keys():
-      debs.extend(get_trusty_packages(trusty_tools()[tool]))
-    elif tool == tool_names.bazel:
-      debs.append(bazel_packages["bazel"])
-    else:
-      fail("Installation of %s was requested, but it is not supported for trusty distro" % tool)
-  # Remove duplicates using a depset
-  return depset(debs).to_list()
-
-def autoconf_xenial_packages(install_tools):
-  """Return the list of debian packages given the name of tools for Xenial.
-
-  Args:
-    install_tools: a list of tool names.
-
-  Returns:
-    a list of debian packages to install
-  """
-  debs = []
-  for tool in install_tools:
-    if tool in xenial_tools().keys():
-      debs.extend(get_xenial_packages(xenial_tools()[tool]))
-    elif tool == tool_names.bazel:
-      debs.append(bazel_packages["bazel"])
-    else:
-      fail("Installation of %s was requested, but it is not supported for xenial distro" % tool)
-  # Remove duplicates using a depset
-  return depset(debs).to_list()