summaryrefslogtreecommitdiffhomepage
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/images/BUILD68
-rwxr-xr-xtools/images/build.sh101
-rw-r--r--tools/images/defs.bzl178
-rwxr-xr-xtools/images/execute.sh152
-rw-r--r--tools/images/test.cc23
-rwxr-xr-xtools/images/ubuntu1604/10_core.sh30
-rwxr-xr-xtools/images/ubuntu1604/20_bazel.sh28
-rwxr-xr-xtools/images/ubuntu1604/25_docker.sh35
-rwxr-xr-xtools/images/ubuntu1604/30_containerd.sh76
-rwxr-xr-xtools/images/ubuntu1604/40_kokoro.sh57
-rw-r--r--tools/images/ubuntu1604/BUILD7
-rw-r--r--tools/images/ubuntu1804/BUILD7
-rw-r--r--tools/installers/BUILD22
-rwxr-xr-xtools/installers/head.sh21
-rwxr-xr-xtools/installers/master.sh20
-rwxr-xr-xtools/installers/shim.sh24
16 files changed, 849 insertions, 0 deletions
diff --git a/tools/images/BUILD b/tools/images/BUILD
new file mode 100644
index 000000000..2b77c2737
--- /dev/null
+++ b/tools/images/BUILD
@@ -0,0 +1,68 @@
+load("@rules_cc//cc:defs.bzl", "cc_binary")
+load("//tools/images:defs.bzl", "vm_image", "vm_test")
+
+package(
+ default_visibility = ["//:sandbox"],
+ licenses = ["notice"],
+)
+
+genrule(
+ name = "zone",
+ outs = ["zone.txt"],
+ cmd = "gcloud config get-value compute/zone > $@",
+ tags = [
+ "local",
+ "manual",
+ ],
+)
+
+sh_binary(
+ name = "builder",
+ srcs = ["build.sh"],
+)
+
+sh_binary(
+ name = "executer",
+ srcs = ["execute.sh"],
+)
+
+cc_binary(
+ name = "test",
+ testonly = 1,
+ srcs = ["test.cc"],
+ linkstatic = 1,
+ deps = [
+ "//test/util:test_main",
+ "@com_google_googletest//:gtest",
+ ],
+)
+
+vm_image(
+ name = "ubuntu1604",
+ family = "ubuntu-1604-lts",
+ project = "ubuntu-os-cloud",
+ scripts = [
+ "//tools/images/ubuntu1604",
+ ],
+)
+
+vm_test(
+ name = "ubuntu1604_test",
+ image = ":ubuntu1604",
+ targets = [":test"],
+)
+
+vm_image(
+ name = "ubuntu1804",
+ family = "ubuntu-1804-lts",
+ project = "ubuntu-os-cloud",
+ scripts = [
+ "//tools/images/ubuntu1804",
+ ],
+)
+
+vm_test(
+ name = "ubuntu1804_test",
+ image = ":ubuntu1804",
+ targets = [":test"],
+)
diff --git a/tools/images/build.sh b/tools/images/build.sh
new file mode 100755
index 000000000..be462d556
--- /dev/null
+++ b/tools/images/build.sh
@@ -0,0 +1,101 @@
+#!/bin/bash
+
+# Copyright 2019 The gVisor Authors.
+#
+# 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.
+
+# This script is responsible for building a new GCP image that: 1) has nested
+# virtualization enabled, and 2) has been completely set up with the
+# image_setup.sh script. This script should be idempotent, as we memoize the
+# setup script with a hash and check for that name.
+
+set -xeou pipefail
+
+# Parameters.
+declare -r USERNAME=${USERNAME:-test}
+declare -r IMAGE_PROJECT=${IMAGE_PROJECT:-ubuntu-os-cloud}
+declare -r IMAGE_FAMILY=${IMAGE_FAMILY:-ubuntu-1604-lts}
+declare -r ZONE=${ZONE:-us-central1-f}
+
+# Random names.
+declare -r DISK_NAME=$(mktemp -u disk-XXXXXX | tr A-Z a-z)
+declare -r SNAPSHOT_NAME=$(mktemp -u snapshot-XXXXXX | tr A-Z a-z)
+declare -r INSTANCE_NAME=$(mktemp -u build-XXXXXX | tr A-Z a-z)
+
+# Hash inputs in order to memoize the produced image.
+declare -r SETUP_HASH=$( (echo ${USERNAME} ${IMAGE_PROJECT} ${IMAGE_FAMILY} && cat "$@") | sha256sum - | cut -d' ' -f1 | cut -c 1-16)
+declare -r IMAGE_NAME=${IMAGE_FAMILY:-image-}${SETUP_HASH}
+
+# Does the image already exist? Skip the build.
+declare -r existing=$(gcloud compute images list --filter="name=(${IMAGE_NAME})" --format="value(name)")
+if ! [[ -z "${existing}" ]]; then
+ echo "${existing}"
+ exit 0
+fi
+
+# gcloud has path errors; is this a result of being a genrule?
+export PATH=${PATH:-/bin:/usr/bin:/usr/local/bin}
+
+# Start a unique instance. Note that this instance will have a unique persistent
+# disk as it's boot disk with the same name as the instance.
+gcloud compute instances create \
+ --quiet \
+ --image-project "${IMAGE_PROJECT}" \
+ --image-family "${IMAGE_FAMILY}" \
+ --boot-disk-size "200GB" \
+ --zone "${ZONE}" \
+ "${INSTANCE_NAME}" >/dev/null
+function cleanup {
+ gcloud compute instances delete --quiet --zone "${ZONE}" "${INSTANCE_NAME}"
+}
+trap cleanup EXIT
+
+# Wait for the instance to become available (up to 5 minutes).
+declare timeout=300
+declare success=0
+declare -r start=$(date +%s)
+declare -r end=$((${start}+${timeout}))
+while [[ "$(date +%s)" -lt "${end}" ]] && [[ "${success}" -lt 3 ]]; do
+ if gcloud compute ssh --zone "${ZONE}" "${USERNAME}"@"${INSTANCE_NAME}" -- env - true 2>/dev/null; then
+ success=$((${success}+1))
+ fi
+done
+if [[ "${success}" -eq "0" ]]; then
+ echo "connect timed out after ${timeout} seconds."
+ exit 1
+fi
+
+# Run the install scripts provided.
+for arg; do
+ gcloud compute ssh --zone "${ZONE}" "${USERNAME}"@"${INSTANCE_NAME}" -- sudo bash - <"${arg}" >/dev/null
+done
+
+# Stop the instance; required before creating an image.
+gcloud compute instances stop --quiet --zone "${ZONE}" "${INSTANCE_NAME}" >/dev/null
+
+# Create a snapshot of the instance disk.
+gcloud compute disks snapshot \
+ --quiet \
+ --zone "${ZONE}" \
+ --snapshot-names="${SNAPSHOT_NAME}" \
+ "${INSTANCE_NAME}" >/dev/null
+
+# Create the disk image.
+gcloud compute images create \
+ --quiet \
+ --source-snapshot="${SNAPSHOT_NAME}" \
+ --licenses="https://www.googleapis.com/compute/v1/projects/vm-options/global/licenses/enable-vmx" \
+ "${IMAGE_NAME}" >/dev/null
+
+# Finish up.
+echo "${IMAGE_NAME}"
diff --git a/tools/images/defs.bzl b/tools/images/defs.bzl
new file mode 100644
index 000000000..d8e422a5d
--- /dev/null
+++ b/tools/images/defs.bzl
@@ -0,0 +1,178 @@
+"""Image configuration.
+
+Images can be generated by using the vm_image rule. For example,
+
+ vm_image(
+ name = "ubuntu",
+ project = "...",
+ family = "...",
+ scripts = [
+ "script.sh",
+ "other.sh",
+ ],
+ )
+
+This will always create an vm_image in the current default gcloud project. The
+rule has a text file as its output containing the image name. This will enforce
+serialization for all dependent rules.
+
+Images are always named per the hash of all the hermetic input scripts. This
+allows images to be memoized quickly and easily.
+
+The vm_test rule can be used to execute a command remotely. For example,
+
+ vm_test(
+ name = "mycommand",
+ image = ":myimage",
+ targets = [":test"],
+ )
+"""
+
+def _vm_image_impl(ctx):
+ script_paths = []
+ for script in ctx.files.scripts:
+ script_paths.append(script.short_path)
+
+ resolved_inputs, argv, runfiles_manifests = ctx.resolve_command(
+ command = "USERNAME=%s ZONE=$(cat %s) IMAGE_PROJECT=%s IMAGE_FAMILY=%s %s %s > %s" %
+ (
+ ctx.attr.username,
+ ctx.files.zone[0].path,
+ ctx.attr.project,
+ ctx.attr.family,
+ ctx.executable.builder.path,
+ " ".join(script_paths),
+ ctx.outputs.out.path,
+ ),
+ tools = [ctx.attr.builder] + ctx.attr.scripts,
+ )
+
+ ctx.actions.run_shell(
+ tools = resolved_inputs,
+ outputs = [ctx.outputs.out],
+ progress_message = "Building image...",
+ execution_requirements = {"local": "true"},
+ command = argv,
+ input_manifests = runfiles_manifests,
+ )
+ return [DefaultInfo(files = depset([ctx.outputs.out]))]
+
+_vm_image = rule(
+ attrs = {
+ "builder": attr.label(
+ executable = True,
+ default = "//tools/images:builder",
+ cfg = "host",
+ ),
+ "username": attr.string(default = "$(whoami)"),
+ "zone": attr.label(
+ default = "//tools/images:zone",
+ cfg = "host",
+ ),
+ "family": attr.string(mandatory = True),
+ "project": attr.string(mandatory = True),
+ "scripts": attr.label_list(allow_files = True),
+ },
+ outputs = {
+ "out": "%{name}.txt",
+ },
+ implementation = _vm_image_impl,
+)
+
+def vm_image(**kwargs):
+ _vm_image(
+ tags = [
+ "local",
+ "manual",
+ ],
+ **kwargs
+ )
+
+def _vm_test_impl(ctx):
+ runner = ctx.actions.declare_file("%s-executer" % ctx.label.name)
+
+ # Note that the remote execution case must actually generate an
+ # intermediate target in order to collect all the relevant runfiles so that
+ # they can be copied over for remote execution.
+ runner_content = "\n".join([
+ "#!/bin/bash",
+ "export ZONE=$(cat %s)" % ctx.files.zone[0].short_path,
+ "export USERNAME=%s" % ctx.attr.username,
+ "export IMAGE=$(cat %s)" % ctx.files.image[0].short_path,
+ "export SUDO=%s" % "true" if ctx.attr.sudo else "false",
+ "%s %s" % (
+ ctx.executable.executer.short_path,
+ " ".join([
+ target.files_to_run.executable.short_path
+ for target in ctx.attr.targets
+ ]),
+ ),
+ "",
+ ])
+ ctx.actions.write(runner, runner_content, is_executable = True)
+
+ # Return with all transitive files.
+ runfiles = ctx.runfiles(
+ transitive_files = depset(transitive = [
+ depset(target.data_runfiles.files)
+ for target in ctx.attr.targets
+ if hasattr(target, "data_runfiles")
+ ]),
+ files = ctx.files.executer + ctx.files.zone + ctx.files.image +
+ ctx.files.targets,
+ collect_default = True,
+ collect_data = True,
+ )
+ return [DefaultInfo(executable = runner, runfiles = runfiles)]
+
+_vm_test = rule(
+ attrs = {
+ "image": attr.label(
+ mandatory = True,
+ cfg = "host",
+ ),
+ "executer": attr.label(
+ executable = True,
+ default = "//tools/images:executer",
+ cfg = "host",
+ ),
+ "username": attr.string(default = "$(whoami)"),
+ "zone": attr.label(
+ default = "//tools/images:zone",
+ cfg = "host",
+ ),
+ "sudo": attr.bool(default = True),
+ "machine": attr.string(default = "n1-standard-1"),
+ "targets": attr.label_list(
+ mandatory = True,
+ allow_empty = False,
+ cfg = "target",
+ ),
+ },
+ test = True,
+ implementation = _vm_test_impl,
+)
+
+def vm_test(
+ installer = "//tools/installers:head",
+ **kwargs):
+ """Runs the given targets as a remote test.
+
+ Args:
+ installer: Script to run before all targets.
+ **kwargs: All test arguments. Should include targets and image.
+ """
+ targets = kwargs.pop("targets", [])
+ if installer:
+ targets = [installer] + targets
+ targets = [
+ ] + targets
+ _vm_test(
+ tags = [
+ "local",
+ "manual",
+ ],
+ targets = targets,
+ local = 1,
+ **kwargs
+ )
diff --git a/tools/images/execute.sh b/tools/images/execute.sh
new file mode 100755
index 000000000..ba4b1ac0e
--- /dev/null
+++ b/tools/images/execute.sh
@@ -0,0 +1,152 @@
+#!/bin/bash
+
+# Copyright 2019 The gVisor Authors.
+#
+# 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.
+
+set -xeo pipefail
+
+# Required input.
+if ! [[ -v IMAGE ]]; then
+ echo "no image provided: set IMAGE."
+ exit 1
+fi
+
+# Parameters.
+declare -r USERNAME=${USERNAME:-test}
+declare -r KEYNAME=$(mktemp --tmpdir -u key-XXXXXX)
+declare -r SSHKEYS=$(mktemp --tmpdir -u sshkeys-XXXXXX)
+declare -r INSTANCE_NAME=$(mktemp -u test-XXXXXX | tr A-Z a-z)
+declare -r MACHINE=${MACHINE:-n1-standard-1}
+declare -r ZONE=${ZONE:-us-central1-f}
+declare -r SUDO=${SUDO:-false}
+
+# This script is executed as a test rule, which will reset the value of HOME.
+# Unfortunately, it is needed to load the gconfig credentials. We will reset
+# HOME when we actually execute in the remote environment, defined below.
+export HOME=$(eval echo ~$(whoami))
+
+# Generate unique keys for this test.
+[[ -f "${KEYNAME}" ]] || ssh-keygen -t rsa -N "" -f "${KEYNAME}" -C "${USERNAME}"
+cat > "${SSHKEYS}" <<EOF
+${USERNAME}:$(cat ${KEYNAME}.pub)
+EOF
+
+# Start a unique instance. This means that we first generate a unique set of ssh
+# keys to ensure that only we have access to this instance. Note that we must
+# constrain ourselves to Haswell or greater in order to have nested
+# virtualization available.
+gcloud compute instances create \
+ --min-cpu-platform "Intel Haswell" \
+ --preemptible \
+ --no-scopes \
+ --metadata block-project-ssh-keys=TRUE \
+ --metadata-from-file ssh-keys="${SSHKEYS}" \
+ --machine-type "${MACHINE}" \
+ --image "${IMAGE}" \
+ --zone "${ZONE}" \
+ "${INSTANCE_NAME}"
+function cleanup {
+ gcloud compute instances delete --quiet --zone "${ZONE}" "${INSTANCE_NAME}"
+}
+trap cleanup EXIT
+
+# Wait for the instance to become available (up to 5 minutes).
+declare timeout=300
+declare success=0
+declare -r start=$(date +%s)
+declare -r end=$((${start}+${timeout}))
+while [[ "$(date +%s)" -lt "${end}" ]] && [[ "${success}" -lt 3 ]]; do
+ if gcloud compute ssh --ssh-key-file="${KEYNAME}" --zone "${ZONE}" "${USERNAME}"@"${INSTANCE_NAME}" -- true 2>/dev/null; then
+ success=$((${success}+1))
+ fi
+done
+if [[ "${success}" -eq "0" ]]; then
+ echo "connect timed out after ${timeout} seconds."
+ exit 1
+fi
+
+# Copy the local directory over.
+tar czf - --dereference --exclude=.git . |
+ gcloud compute ssh \
+ --ssh-key-file="${KEYNAME}" \
+ --zone "${ZONE}" \
+ "${USERNAME}"@"${INSTANCE_NAME}" -- tar xzf -
+
+# Execute the command remotely.
+for cmd; do
+ # Setup relevant environment.
+ #
+ # N.B. This is not a complete test environment, but is complete enough to
+ # provide rudimentary sharding and test output support.
+ declare -a PREFIX=( "env" )
+ if [[ -v TEST_SHARD_INDEX ]]; then
+ PREFIX+=( "TEST_SHARD_INDEX=${TEST_SHARD_INDEX}" )
+ fi
+ if [[ -v TEST_SHARD_STATUS_FILE ]]; then
+ SHARD_STATUS_FILE=$(mktemp -u test-shard-status-XXXXXX)
+ PREFIX+=( "TEST_SHARD_STATUS_FILE=/tmp/${SHARD_STATUS_FILE}" )
+ fi
+ if [[ -v TEST_TOTAL_SHARDS ]]; then
+ PREFIX+=( "TEST_TOTAL_SHARDS=${TEST_TOTAL_SHARDS}" )
+ fi
+ if [[ -v TEST_TMPDIR ]]; then
+ REMOTE_TMPDIR=$(mktemp -u test-XXXXXX)
+ PREFIX+=( "TEST_TMPDIR=/tmp/${REMOTE_TMPDIR}" )
+ # Create remotely.
+ gcloud compute ssh \
+ --ssh-key-file="${KEYNAME}" \
+ --zone "${ZONE}" \
+ "${USERNAME}"@"${INSTANCE_NAME}" -- \
+ mkdir -p "/tmp/${REMOTE_TMPDIR}"
+ fi
+ if [[ -v XML_OUTPUT_FILE ]]; then
+ TEST_XML_OUTPUT=$(mktemp -u xml-output-XXXXXX)
+ PREFIX+=( "XML_OUTPUT_FILE=/tmp/${TEST_XML_OUTPUT}" )
+ fi
+ if [[ "${SUDO}" == "true" ]]; then
+ PREFIX+=( "sudo" "-E" )
+ fi
+
+ # Execute the command.
+ gcloud compute ssh \
+ --ssh-key-file="${KEYNAME}" \
+ --zone "${ZONE}" \
+ "${USERNAME}"@"${INSTANCE_NAME}" -- \
+ "${PREFIX[@]}" "${cmd}"
+
+ # Collect relevant results.
+ if [[ -v TEST_SHARD_STATUS_FILE ]]; then
+ gcloud compute scp \
+ --ssh-key-file="${KEYNAME}" \
+ --zone "${ZONE}" \
+ "${USERNAME}"@"${INSTANCE_NAME}":/tmp/"${SHARD_STATUS_FILE}" \
+ "${TEST_SHARD_STATUS_FILE}" 2>/dev/null || true # Allowed to fail.
+ fi
+ if [[ -v XML_OUTPUT_FILE ]]; then
+ gcloud compute scp \
+ --ssh-key-file="${KEYNAME}" \
+ --zone "${ZONE}" \
+ "${USERNAME}"@"${INSTANCE_NAME}":/tmp/"${TEST_XML_OUTPUT}" \
+ "${XML_OUTPUT_FILE}" 2>/dev/null || true # Allowed to fail.
+ fi
+
+ # Clean up the temporary directory.
+ if [[ -v TEST_TMPDIR ]]; then
+ gcloud compute ssh \
+ --ssh-key-file="${KEYNAME}" \
+ --zone "${ZONE}" \
+ "${USERNAME}"@"${INSTANCE_NAME}" -- \
+ rm -rf "/tmp/${REMOTE_TMPDIR}"
+ fi
+done
diff --git a/tools/images/test.cc b/tools/images/test.cc
new file mode 100644
index 000000000..4f31d93c5
--- /dev/null
+++ b/tools/images/test.cc
@@ -0,0 +1,23 @@
+// Copyright 2020 The gVisor Authors.
+//
+// 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.
+
+#include "gtest/gtest.h"
+
+namespace {
+
+TEST(Image, Sanity) {
+ // Do nothing.
+}
+
+} // namespace
diff --git a/tools/images/ubuntu1604/10_core.sh b/tools/images/ubuntu1604/10_core.sh
new file mode 100755
index 000000000..46dda6bb1
--- /dev/null
+++ b/tools/images/ubuntu1604/10_core.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+# Copyright 2019 The gVisor Authors.
+#
+# 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.
+
+set -xeo pipefail
+
+# Install all essential build tools.
+apt-get update && apt-get -y install make git-core build-essential linux-headers-$(uname -r) pkg-config
+
+# Install a recent go toolchain.
+if ! [[ -d /usr/local/go ]]; then
+ wget https://dl.google.com/go/go1.13.5.linux-amd64.tar.gz
+ tar -xvf go1.13.5.linux-amd64.tar.gz
+ mv go /usr/local
+fi
+
+# Link the Go binary from /usr/bin; replacing anything there.
+(cd /usr/bin && rm -f go && sudo ln -fs /usr/local/go/bin/go go)
diff --git a/tools/images/ubuntu1604/20_bazel.sh b/tools/images/ubuntu1604/20_bazel.sh
new file mode 100755
index 000000000..b33e1656c
--- /dev/null
+++ b/tools/images/ubuntu1604/20_bazel.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+# Copyright 2019 The gVisor Authors.
+#
+# 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.
+
+set -xeo pipefail
+
+declare -r BAZEL_VERSION=2.0.0
+
+# Install bazel dependencies.
+apt-get update && apt-get install -y openjdk-8-jdk-headless unzip
+
+# Use the release installer.
+curl -L -o bazel-${BAZEL_VERSION}-installer-linux-x86_64.sh https://github.com/bazelbuild/bazel/releases/download/${BAZEL_VERSION}/bazel-${BAZEL_VERSION}-installer-linux-x86_64.sh
+chmod a+x bazel-${BAZEL_VERSION}-installer-linux-x86_64.sh
+./bazel-${BAZEL_VERSION}-installer-linux-x86_64.sh
+rm -f bazel-${BAZEL_VERSION}-installer-linux-x86_64.sh
diff --git a/tools/images/ubuntu1604/25_docker.sh b/tools/images/ubuntu1604/25_docker.sh
new file mode 100755
index 000000000..1d3defcd3
--- /dev/null
+++ b/tools/images/ubuntu1604/25_docker.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+# Copyright 2019 The gVisor Authors.
+#
+# 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.
+
+# Add dependencies.
+apt-get update && apt-get -y install \
+ apt-transport-https \
+ ca-certificates \
+ curl \
+ gnupg-agent \
+ software-properties-common
+
+# Install the key.
+curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
+
+# Add the repository.
+add-apt-repository \
+ "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
+ $(lsb_release -cs) \
+ stable"
+
+# Install docker.
+apt-get update && apt-get install -y docker-ce docker-ce-cli containerd.io
diff --git a/tools/images/ubuntu1604/30_containerd.sh b/tools/images/ubuntu1604/30_containerd.sh
new file mode 100755
index 000000000..a7472bd1c
--- /dev/null
+++ b/tools/images/ubuntu1604/30_containerd.sh
@@ -0,0 +1,76 @@
+#!/bin/bash
+
+# Copyright 2019 The gVisor Authors.
+#
+# 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.
+
+set -xeo pipefail
+
+# Helper for Go packages below.
+install_helper() {
+ PACKAGE="${1}"
+ TAG="${2}"
+ GOPATH="${3}"
+
+ # Clone the repository.
+ mkdir -p "${GOPATH}"/src/$(dirname "${PACKAGE}") && \
+ git clone https://"${PACKAGE}" "${GOPATH}"/src/"${PACKAGE}"
+
+ # Checkout and build the repository.
+ (cd "${GOPATH}"/src/"${PACKAGE}" && \
+ git checkout "${TAG}" && \
+ GOPATH="${GOPATH}" make && \
+ GOPATH="${GOPATH}" make install)
+}
+
+# Install dependencies for the crictl tests.
+apt-get install -y btrfs-tools libseccomp-dev
+
+# Install containerd & cri-tools.
+GOPATH=$(mktemp -d --tmpdir gopathXXXXX)
+install_helper github.com/containerd/containerd v1.2.2 "${GOPATH}"
+install_helper github.com/kubernetes-sigs/cri-tools v1.11.0 "${GOPATH}"
+
+# Install gvisor-containerd-shim.
+declare -r base="https://storage.googleapis.com/cri-containerd-staging/gvisor-containerd-shim"
+declare -r latest=$(mktemp --tmpdir gvisor-containerd-shim-latest.XXXXXX)
+declare -r shim_path=$(mktemp --tmpdir gvisor-containerd-shim.XXXXXX)
+wget --no-verbose "${base}"/latest -O ${latest}
+wget --no-verbose "${base}"/gvisor-containerd-shim-$(cat ${latest}) -O ${shim_path}
+chmod +x ${shim_path}
+mv ${shim_path} /usr/local/bin
+
+# Configure containerd-shim.
+declare -r shim_config_path=/etc/containerd
+declare -r shim_config_tmp_path=$(mktemp --tmpdir gvisor-containerd-shim.XXXXXX.toml)
+mkdir -p ${shim_config_path}
+cat > ${shim_config_tmp_path} <<-EOF
+ runc_shim = "/usr/local/bin/containerd-shim"
+
+[runsc_config]
+ debug = "true"
+ debug-log = "/tmp/runsc-logs/"
+ strace = "true"
+ file-access = "shared"
+EOF
+mv ${shim_config_tmp_path} ${shim_config_path}
+
+# Configure CNI.
+(cd "${GOPATH}" && GOPATH="${GOPATH}" \
+ src/github.com/containerd/containerd/script/setup/install-cni)
+
+# Cleanup the above.
+rm -rf "${GOPATH}"
+rm -rf "${latest}"
+rm -rf "${shim_path}"
+rm -rf "${shim_config_tmp_path}"
diff --git a/tools/images/ubuntu1604/40_kokoro.sh b/tools/images/ubuntu1604/40_kokoro.sh
new file mode 100755
index 000000000..5f2dfc858
--- /dev/null
+++ b/tools/images/ubuntu1604/40_kokoro.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+# Copyright 2019 The gVisor Authors.
+#
+# 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.
+
+set -xeo pipefail
+
+# Declare kokoro's required public keys.
+declare -r ssh_public_keys=(
+ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDg7L/ZaEauETWrPklUTky3kvxqQfe2Ax/2CsSqhNIGNMnK/8d79CHlmY9+dE1FFQ/RzKNCaltgy7XcN/fCYiCZr5jm2ZtnLuGNOTzupMNhaYiPL419qmL+5rZXt4/dWTrsHbFRACxT8j51PcRMO5wgbL0Bg2XXimbx8kDFaurL2gqduQYqlu4lxWCaJqOL71WogcimeL63Nq/yeH5PJPWpqE4P9VUQSwAzBWFK/hLeds/AiP3MgVS65qHBnhq0JsHy8JQsqjZbG7Iidt/Ll0+gqzEbi62gDIcczG4KC0iOVzDDP/1BxDtt1lKeA23ll769Fcm3rJyoBMYxjvdw1TDx sabujp@trigger.mtv.corp.google.com"
+ "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNgGK/hCdjmulHfRE3hp4rZs38NCR8yAh0eDsztxqGcuXnuSnL7jOlRrbcQpremJ84omD4eKrIpwJUs+YokMdv4= sabujp@trigger.svl.corp.google.com"
+)
+
+# Install dependencies.
+apt-get update && apt-get install -y rsync coreutils python-psutil qemu-kvm python-pip python3-pip zip
+
+# junitparser is used to merge junit xml files.
+pip install junitparser
+
+# We need a kbuilder user.
+if useradd -c "kbuilder user" -m -s /bin/bash kbuilder; then
+ # User was added successfully; we add the relevant SSH keys here.
+ mkdir -p ~kbuilder/.ssh
+ (IFS=$'\n'; echo "${ssh_public_keys[*]}") > ~kbuilder/.ssh/authorized_keys
+ chmod 0600 ~kbuilder/.ssh/authorized_keys
+ chown -R kbuilder ~kbuilder/.ssh
+fi
+
+# Give passwordless sudo access.
+cat > /etc/sudoers.d/kokoro <<EOF
+kbuilder ALL=(ALL) NOPASSWD:ALL
+EOF
+
+# Ensure we can run Docker without sudo.
+usermod -aG docker kbuilder
+
+# Ensure that we can access kvm.
+usermod -aG kvm kbuilder
+
+# Ensure that /tmpfs exists and is writable by kokoro.
+#
+# Note that kokoro will typically attach a second disk (sdb) to the instance
+# that is used for the /tmpfs volume. In the future we could setup an init
+# script that formats and mounts this here; however, we don't expect our build
+# artifacts to be that large.
+mkdir -p /tmpfs && chmod 0777 /tmpfs && touch /tmpfs/READY
diff --git a/tools/images/ubuntu1604/BUILD b/tools/images/ubuntu1604/BUILD
new file mode 100644
index 000000000..ab1df0c4c
--- /dev/null
+++ b/tools/images/ubuntu1604/BUILD
@@ -0,0 +1,7 @@
+package(licenses = ["notice"])
+
+filegroup(
+ name = "ubuntu1604",
+ srcs = glob(["*.sh"]),
+ visibility = ["//:sandbox"],
+)
diff --git a/tools/images/ubuntu1804/BUILD b/tools/images/ubuntu1804/BUILD
new file mode 100644
index 000000000..7aa1ecdf7
--- /dev/null
+++ b/tools/images/ubuntu1804/BUILD
@@ -0,0 +1,7 @@
+package(licenses = ["notice"])
+
+alias(
+ name = "ubuntu1804",
+ actual = "//tools/images/ubuntu1604",
+ visibility = ["//:sandbox"],
+)
diff --git a/tools/installers/BUILD b/tools/installers/BUILD
new file mode 100644
index 000000000..01bc4de8c
--- /dev/null
+++ b/tools/installers/BUILD
@@ -0,0 +1,22 @@
+# Installers for use by the tools/vm_test rules.
+
+package(
+ default_visibility = ["//:sandbox"],
+ licenses = ["notice"],
+)
+
+sh_binary(
+ name = "head",
+ srcs = ["head.sh"],
+ data = ["//runsc"],
+)
+
+sh_binary(
+ name = "master",
+ srcs = ["master.sh"],
+)
+
+sh_binary(
+ name = "shim",
+ srcs = ["shim.sh"],
+)
diff --git a/tools/installers/head.sh b/tools/installers/head.sh
new file mode 100755
index 000000000..4435cb27a
--- /dev/null
+++ b/tools/installers/head.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# Copyright 2019 The gVisor Authors.
+#
+# 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.
+
+# Install our runtime.
+third_party/gvisor/runsc/runsc install
+
+# Restart docker.
+service docker restart || true
diff --git a/tools/installers/master.sh b/tools/installers/master.sh
new file mode 100755
index 000000000..7b1956454
--- /dev/null
+++ b/tools/installers/master.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright 2019 The gVisor Authors.
+#
+# 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.
+
+# Install runsc from the master branch.
+curl -fsSL https://gvisor.dev/archive.key | sudo apt-key add -
+add-apt-repository "deb https://storage.googleapis.com/gvisor/releases release main"
+apt-get update && apt-get install -y runsc
diff --git a/tools/installers/shim.sh b/tools/installers/shim.sh
new file mode 100755
index 000000000..f7dd790a1
--- /dev/null
+++ b/tools/installers/shim.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# Copyright 2019 The gVisor Authors.
+#
+# 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.
+
+# Reinstall the latest containerd shim.
+declare -r base="https://storage.googleapis.com/cri-containerd-staging/gvisor-containerd-shim"
+declare -r latest=$(mktemp --tmpdir gvisor-containerd-shim-latest.XXXXXX)
+declare -r shim_path=$(mktemp --tmpdir gvisor-containerd-shim.XXXXXX)
+wget --no-verbose "${base}"/latest -O ${latest}
+wget --no-verbose "${base}"/gvisor-containerd-shim-$(cat ${latest}) -O ${shim_path}
+chmod +x ${shim_path}
+mv ${shim_path} /usr/local/bin/gvisor-containerd-shim