Fix build.sh and VM targets.

PiperOrigin-RevId: 306289643
This commit is contained in:
Adin Scannell
2020-04-13 12:48:38 -07:00
committed by gVisor bot
parent 6a4d17a31d
commit aa75a3da51
8 changed files with 161 additions and 190 deletions
+3 -3
View File
@@ -19,6 +19,7 @@ import logging
import pkgutil
import pydoc
import re
import subprocess
import sys
import types
from typing import List
@@ -125,9 +126,8 @@ def run_gcp(ctx, image_file: str, zone_file: str, internal: bool,
"""Runs all benchmarks on GCP instances."""
# Resolve all files.
image = open(image_file).read().rstrip()
zone = open(zone_file).read().rstrip()
image = subprocess.check_output([image_file]).rstrip()
zone = subprocess.check_output([zone_file]).rstrip()
key_file = harness.make_key()
producer = gcloud_producer.GCloudProducer(
+4 -4
View File
@@ -101,15 +101,15 @@ class GCPCommand(RunCommand):
image_file = click.core.Option(
("--image_file",),
help="The file containing the image for VMs.",
help="The binary that emits the GCP image.",
default=os.path.join(
os.path.dirname(__file__), "../../tools/images/ubuntu1604.txt"),
os.path.dirname(__file__), "../../tools/images/ubuntu1604"),
)
zone_file = click.core.Option(
("--zone_file",),
help="The file containing the GCP zone.",
help="The binary that emits the GCP zone.",
default=os.path.join(
os.path.dirname(__file__), "../../tools/images/zone.txt"),
os.path.dirname(__file__), "../../tools/images/zone"),
)
internal = click.core.Option(
("--internal/--no-internal",),
-98
View File
@@ -1,98 +0,0 @@
#!/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.
#
# The GCP project name should be defined via a gcloud config.
set -xeo pipefail
# Parameters.
declare -r ZONE=${ZONE:-us-central1-f}
declare -r USERNAME=${USERNAME:-test}
declare -r IMAGE_PROJECT=${IMAGE_PROJECT:-ubuntu-os-cloud}
declare -r IMAGE_FAMILY=${IMAGE_FAMILY:-ubuntu-1604-lts}
# 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)
# Hashes inputs.
declare -r SETUP_BLOB=$(echo ${ZONE} ${USERNAME} ${IMAGE_PROJECT} ${IMAGE_FAMILY} && sha256sum "$@")
declare -r SETUP_HASH=$(echo ${SETUP_BLOB} | sha256sum - | cut -d' ' -f1 | cut -c 1-16)
declare -r IMAGE_NAME=${IMAGE_NAME:-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
# Set the zone for all actions.
gcloud config set compute/zone "${ZONE}"
# 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" \
"${INSTANCE_NAME}"
function cleanup {
gcloud compute instances delete --quiet "${INSTANCE_NAME}"
}
trap cleanup EXIT
# Wait for the instance to become available.
declare attempts=0
while [[ "${attempts}" -lt 30 ]]; do
attempts=$((${attempts}+1))
if gcloud compute ssh "${USERNAME}"@"${INSTANCE_NAME}" -- true; then
break
fi
done
if [[ "${attempts}" -ge 30 ]]; then
echo "too many attempts: failed"
exit 1
fi
# Run the install scripts provided.
for arg; do
gcloud compute ssh "${USERNAME}"@"${INSTANCE_NAME}" -- sudo bash - <"${arg}"
done
# Stop the instance; required before creating an image.
gcloud compute instances stop --quiet "${INSTANCE_NAME}"
# Create a snapshot of the instance disk.
gcloud compute disks snapshot \
--quiet \
--zone="${ZONE}" \
--snapshot-names="${SNAPSHOT_NAME}" \
"${INSTANCE_NAME}"
# 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}"
+2 -7
View File
@@ -6,14 +6,9 @@ package(
licenses = ["notice"],
)
genrule(
sh_binary(
name = "zone",
outs = ["zone.txt"],
cmd = "gcloud config get-value compute/zone > \"$@\"",
tags = [
"local",
"manual",
],
srcs = ["zone.sh"],
)
sh_binary(
+42
View File
@@ -0,0 +1,42 @@
# Images
All commands in this directory require the `gcloud` project to be set.
For example: `gcloud config set project gvisor-kokoro-testing`.
Images can be generated by using the `vm_image` rule. This rule will generate a
binary target that builds an image in an idempotent way, and can be referenced
from other rules.
For example:
```
vm_image(
name = "ubuntu",
project = "ubuntu-1604-lts",
family = "ubuntu-os-cloud",
scripts = [
"script.sh",
"other.sh",
],
)
```
These images can be built manually by executing the target. The output on
`stdout` will be the image id (in the current project).
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. This is still
under development however, and will likely change over time.
For example:
```
vm_test(
name = "mycommand",
image = ":ubuntu",
targets = [":test"],
)
```
+21 -14
View File
@@ -19,7 +19,7 @@
# 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
set -eou pipefail
# Parameters.
declare -r USERNAME=${USERNAME:-test}
@@ -34,10 +34,10 @@ 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}
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)")
declare -r existing=$(set -x; gcloud compute images list --filter="name=(${IMAGE_NAME})" --format="value(name)")
if ! [[ -z "${existing}" ]]; then
echo "${existing}"
exit 0
@@ -48,28 +48,30 @@ 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 \
(set -x; gcloud compute instances create \
--quiet \
--image-project "${IMAGE_PROJECT}" \
--image-family "${IMAGE_FAMILY}" \
--boot-disk-size "200GB" \
--zone "${ZONE}" \
"${INSTANCE_NAME}" >/dev/null
"${INSTANCE_NAME}" >/dev/null)
function cleanup {
gcloud compute instances delete --quiet --zone "${ZONE}" "${INSTANCE_NAME}"
(set -x; gcloud compute instances delete --quiet --zone "${ZONE}" "${INSTANCE_NAME}")
}
trap cleanup EXIT
# Wait for the instance to become available (up to 5 minutes).
echo -n "Waiting for ${INSTANCE_NAME}"
declare timeout=300
declare success=0
declare internal=""
declare -r start=$(date +%s)
declare -r end=$((${start}+${timeout}))
while [[ "$(date +%s)" -lt "${end}" ]] && [[ "${success}" -lt 3 ]]; do
if gcloud compute ssh --zone "${internal}" "${ZONE}" "${USERNAME}"@"${INSTANCE_NAME}" -- env - true 2>/dev/null; then
echo -n "."
if gcloud compute ssh --zone "${ZONE}" "${USERNAME}"@"${INSTANCE_NAME}" -- env - true 2>/dev/null; then
success=$((${success}+1))
elif gcloud compute ssh --zone --internal-ip "${ZONE}" "${USERNAME}"@"${INSTANCE_NAME}" -- env - true 2>/dev/null; then
elif gcloud compute ssh --internal-ip --zone "${ZONE}" "${USERNAME}"@"${INSTANCE_NAME}" -- env - true 2>/dev/null; then
success=$((${success}+1))
internal="--internal-ip"
fi
@@ -78,29 +80,34 @@ done
if [[ "${success}" -eq "0" ]]; then
echo "connect timed out after ${timeout} seconds."
exit 1
else
echo "done."
fi
# Run the install scripts provided.
for arg; do
gcloud compute ssh --zone "${internal}" "${ZONE}" "${USERNAME}"@"${INSTANCE_NAME}" -- sudo bash - <"${arg}" >/dev/null
(set -x; gcloud compute ssh ${internal} \
--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
(set -x; gcloud compute instances stop --quiet --zone "${ZONE}" "${INSTANCE_NAME}" >/dev/null)
# Create a snapshot of the instance disk.
gcloud compute disks snapshot \
(set -x; gcloud compute disks snapshot \
--quiet \
--zone "${ZONE}" \
--snapshot-names="${SNAPSHOT_NAME}" \
"${INSTANCE_NAME}" >/dev/null
"${INSTANCE_NAME}" >/dev/null)
# Create the disk image.
gcloud compute images create \
(set -x; 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
"${IMAGE_NAME}" >/dev/null)
# Finish up.
echo "${IMAGE_NAME}"
+72 -64
View File
@@ -1,76 +1,49 @@
"""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"],
)
"""
"""Image configuration. See README.md."""
load("//tools:defs.bzl", "default_installer")
def _vm_image_impl(ctx):
# vm_image_builder is a rule that will construct a shell script that actually
# generates a given VM image. Note that this does not _run_ the shell script
# (although it can be run manually). It will be run manually during generation
# of the vm_image target itself. This level of indirection is used so that the
# build system itself only runs the builder once when multiple targets depend
# on it, avoiding a set of races and conflicts.
def _vm_image_builder_impl(ctx):
# Generate a binary that actually builds the image.
builder = ctx.actions.declare_file(ctx.label.name)
script_paths = []
for script in ctx.files.scripts:
script_paths.append(script.short_path)
builder_content = "\n".join([
"#!/bin/bash",
"export ZONE=$(%s)" % ctx.files.zone[0].short_path,
"export USERNAME=%s" % ctx.attr.username,
"export IMAGE_PROJECT=%s" % ctx.attr.project,
"export IMAGE_FAMILY=%s" % ctx.attr.family,
"%s %s" % (ctx.files._builder[0].short_path, " ".join(script_paths)),
"",
])
ctx.actions.write(builder, builder_content, is_executable = True)
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,
)
# Note that the scripts should only be files, and should not include any
# indirect transitive dependencies. The build script wouldn't work.
return [DefaultInfo(
files = depset([ctx.outputs.out]),
runfiles = ctx.runfiles(files = [ctx.outputs.out]),
executable = builder,
runfiles = ctx.runfiles(
files = ctx.files.scripts + ctx.files._builder + ctx.files.zone,
),
)]
_vm_image = rule(
vm_image_builder = rule(
attrs = {
"builder": attr.label(
"_builder": attr.label(
executable = True,
default = "//tools/images:builder",
cfg = "host",
),
"username": attr.string(default = "$(whoami)"),
"zone": attr.label(
executable = True,
default = "//tools/images:zone",
cfg = "host",
),
@@ -78,20 +51,55 @@ _vm_image = rule(
"project": attr.string(mandatory = True),
"scripts": attr.label_list(allow_files = True),
},
outputs = {
"out": "%{name}.txt",
executable = True,
implementation = _vm_image_builder_impl,
)
# See vm_image_builder above.
def _vm_image_impl(ctx):
# Run the builder to generate our output.
echo = ctx.actions.declare_file(ctx.label.name)
resolved_inputs, argv, runfiles_manifests = ctx.resolve_command(
command = "echo -ne \"#!/bin/bash\\necho $(%s)\\n\" > %s && chmod 0755 %s" % (
ctx.files.builder[0].path,
echo.path,
echo.path,
),
tools = [ctx.attr.builder],
)
ctx.actions.run_shell(
tools = resolved_inputs,
outputs = [echo],
progress_message = "Building image...",
execution_requirements = {"local": "true"},
command = argv,
input_manifests = runfiles_manifests,
)
# Return just the echo command. All of the builder runfiles have been
# resolved and consumed in the generation of the trivial echo script.
return [DefaultInfo(executable = echo)]
_vm_image = rule(
attrs = {
"builder": attr.label(
executable = True,
cfg = "host",
),
},
executable = True,
implementation = _vm_image_impl,
)
def vm_image(**kwargs):
_vm_image(
tags = [
"local",
"manual",
],
def vm_image(name, **kwargs):
vm_image_builder(
name = name + "_builder",
**kwargs
)
_vm_image(
name = name,
builder = ":" + name + "_builder",
)
def _vm_test_impl(ctx):
runner = ctx.actions.declare_file("%s-executer" % ctx.label.name)
+17
View File
@@ -0,0 +1,17 @@
#!/bin/bash
# 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.
exec gcloud config get-value compute/zone