202 lines
6.3 KiB
Python
202 lines
6.3 KiB
Python
"""Image configuration. See README.md."""
|
|
|
|
load("//tools:defs.bzl", "default_installer")
|
|
|
|
# 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)
|
|
|
|
# Note that the scripts should only be files, and should not include any
|
|
# indirect transitive dependencies. The build script wouldn't work.
|
|
return [DefaultInfo(
|
|
executable = builder,
|
|
runfiles = ctx.runfiles(
|
|
files = ctx.files.scripts + ctx.files._builder + ctx.files.zone,
|
|
),
|
|
)]
|
|
|
|
vm_image_builder = rule(
|
|
attrs = {
|
|
"_builder": attr.label(
|
|
executable = True,
|
|
default = "//tools/vm:builder",
|
|
cfg = "host",
|
|
),
|
|
"username": attr.string(default = "$(whoami)"),
|
|
"zone": attr.label(
|
|
executable = True,
|
|
default = "//tools/vm:zone",
|
|
cfg = "host",
|
|
),
|
|
"family": attr.string(mandatory = True),
|
|
"project": attr.string(mandatory = True),
|
|
"scripts": attr.label_list(allow_files = True),
|
|
},
|
|
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_test = rule(
|
|
attrs = {
|
|
"builder": attr.label(
|
|
executable = True,
|
|
cfg = "host",
|
|
),
|
|
},
|
|
test = True,
|
|
implementation = _vm_image_impl,
|
|
)
|
|
|
|
def vm_image(name, **kwargs):
|
|
vm_image_builder(
|
|
name = name + "_builder",
|
|
**kwargs
|
|
)
|
|
_vm_image_test(
|
|
name = name,
|
|
builder = ":" + name + "_builder",
|
|
tags = [
|
|
"local",
|
|
"manual",
|
|
],
|
|
)
|
|
|
|
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=$(%s)" % ctx.files.zone[0].short_path,
|
|
"export USERNAME=%s" % ctx.attr.username,
|
|
"export IMAGE=$(%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(
|
|
executable = True,
|
|
default = "//tools/vm:ubuntu1804",
|
|
cfg = "host",
|
|
),
|
|
"executer": attr.label(
|
|
executable = True,
|
|
default = "//tools/vm:executer",
|
|
cfg = "host",
|
|
),
|
|
"username": attr.string(default = "$(whoami)"),
|
|
"zone": attr.label(
|
|
executable = True,
|
|
default = "//tools/vm: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(
|
|
installers = None,
|
|
**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 installers == None:
|
|
installers = [
|
|
"//tools/installers:head",
|
|
"//tools/installers:images",
|
|
]
|
|
targets = installers + targets
|
|
if default_installer():
|
|
targets = [default_installer()] + targets
|
|
_vm_test(
|
|
tags = [
|
|
"local",
|
|
"manual",
|
|
],
|
|
targets = targets,
|
|
local = 1,
|
|
**kwargs
|
|
)
|