bazel: rules to handle container images

This commit is contained in:
Malte Poll 2023-04-03 18:13:17 +02:00 committed by Malte Poll
parent 9dfad32e33
commit daf18052f9
9 changed files with 459 additions and 0 deletions

View File

@ -26,6 +26,11 @@ alias(
actual = "//bazel/devbuild:devbuild",
)
alias(
name = "push",
actual = "//bazel/release:push",
)
# These magic Gazelle commands need to be in the top-level BUILD file.
# gazelle:map_kind go_test go_test //bazel/go:go_test.bzl
# gazelle:prefix github.com/edgelesssys/constellation/v2

View File

@ -3,6 +3,7 @@ load("//bazel/sh:def.bzl", "sh_template")
sh_template(
name = "devbuild",
data = [
"//bazel/release:container_sums",
"//bootstrapper/cmd/bootstrapper:bootstrapper_linux_amd64",
"//cli:cli_oss_host",
"//debugd/cmd/cdbg:cdbg_host",
@ -12,6 +13,7 @@ sh_template(
"@@BOOTSTRAPPER@@": "$(rootpath //bootstrapper/cmd/bootstrapper:bootstrapper_linux_amd64)",
"@@CDBG@@": "$(rootpath //debugd/cmd/cdbg:cdbg_host)",
"@@CLI@@": "$(rootpath //cli:cli_oss_host)",
"@@CONTAINER_SUMS@@": "$(rootpath //bazel/release:container_sums)",
"@@UPGRADE_AGENT@@": "$(rootpath //upgrade-agent/cmd:upgrade_agent_linux_amd64)",
},
template = "prepare_developer_workspace.sh.in",

View File

@ -22,6 +22,8 @@ cli=$(realpath @@CLI@@)
stat "${cli}" >> /dev/null
cdbg=$(realpath @@CDBG@@)
stat "${cdbg}" >> /dev/null
container_sums=$(realpath @@CONTAINER_SUMS@@)
stat "${container_sums}" >> /dev/null
cd "${BUILD_WORKING_DIRECTORY}"
@ -53,3 +55,4 @@ ln -sf "$(replace_prefix "${host_cache}" "${builder_cache}" "${bootstrapper}")"
ln -sf "$(replace_prefix "${host_cache}" "${builder_cache}" "${upgrade_agent}")" "${workdir}/upgrade-agent"
ln -sf "$(replace_prefix "${host_cache}" "${builder_cache}" "${cli}")" "${workdir}/constellation"
ln -sf "$(replace_prefix "${host_cache}" "${builder_cache}" "${cdbg}")" "${workdir}/cdbg"
ln -sf "$(replace_prefix "${host_cache}" "${builder_cache}" "${container_sums}")" "${workdir}/container_sums.sha256"

0
bazel/oci/BUILD.bazel Normal file
View File

135
bazel/oci/containers.bzl Normal file
View File

@ -0,0 +1,135 @@
"""
This module holds the definitions of the containers that are built.
"""
load("@rules_oci//oci:defs.bzl", _oci_push = "oci_push", _oci_tarball = "oci_tarball")
load("//bazel/oci:pin.bzl", "oci_sum")
_default_registry = "ghcr.io"
_default_prefix = "edgelesssys/constellation"
def containers():
return [
{
"identifier": "joinService",
"image_name": "join-service",
"name": "joinservice",
"oci": "//joinservice/cmd:joinservice",
"prefix": _default_prefix,
"registry": _default_registry,
"tag_file": "//bazel/settings:tag",
"used_by": ["helm"],
},
{
"identifier": "keyService",
"image_name": "key-service",
"name": "keyservice",
"oci": "//keyservice/cmd:keyservice",
"prefix": _default_prefix,
"registry": _default_registry,
"tag_file": "//bazel/settings:tag",
"used_by": ["helm"],
},
{
"identifier": "verificationService",
"image_name": "verification-service",
"name": "verificationservice",
"oci": "//verify/cmd:verificationservice",
"prefix": _default_prefix,
"registry": _default_registry,
"tag_file": "//bazel/settings:tag",
"used_by": ["helm"],
},
{
"identifier": "constellationNodeOperator",
"image_name": "node-operator",
"name": "nodeoperator",
"oci": "//operators/constellation-node-operator:node_operator",
"prefix": _default_prefix,
"registry": _default_registry,
"tag_file": "//bazel/settings:tag",
"used_by": ["helm"],
},
{
"identifier": "qemuMetadata",
"image_name": "qemu-metadata-api",
"name": "qemumetadata",
"oci": "//hack/qemu-metadata-api:qemumetadata",
"prefix": _default_prefix,
"registry": _default_registry,
"tag_file": "//bazel/settings:tag",
"used_by": ["config"],
},
{
"identifier": "libvirt",
"image_name": "libvirt",
"name": "libvirt",
"oci": "//cli/internal/libvirt:constellation_libvirt",
"prefix": _default_prefix,
"registry": _default_registry,
"tag_file": "//bazel/settings:tag",
"used_by": ["config"],
},
]
def helm_containers():
return [container for container in containers() if "helm" in container["used_by"]]
def config_containers():
return [container for container in containers() if "config" in container["used_by"]]
def container_sum(name, oci, registry, prefix, image_name, **kwargs):
tag = kwargs.get("tag", None)
tag_file = kwargs.get("tag_file", None)
oci_sum(
name = name + "_sum",
oci = oci,
registry = registry,
prefix = prefix,
image_name = image_name,
tag = tag,
tag_file = tag_file,
visibility = ["//visibility:public"],
)
def oci_push(name, image, registry, image_name, **kwargs):
"""oci_push pushes an OCI image to a registry.
Args:
name: The name of the target.
image: The OCI image to push.
registry: The registry to push to.
image_name: The name of the image.
**kwargs: Additional arguments to pass to oci_push.
"""
prefix = kwargs.pop("prefix", None)
tag = kwargs.pop("tag", None)
tag_file = kwargs.pop("tag_file", None)
if prefix == None:
repository = registry + "/" + image_name
else:
repository = registry + "/" + prefix + "/" + image_name
_oci_push(
name = name,
image = image,
repository = repository,
tag = tag,
tag_file = tag_file,
visibility = ["//visibility:public"],
**kwargs
)
# TODO(malt3): allow repotags (registry + tag) to be read from a file.
def oci_tarball(name, image):
"""oci_tarball creates a tarball of an OCI image.
Args:
name: The name of the target.
image: The OCI image to create a tarball of.
"""
_oci_tarball(
name = name,
image = image,
repotags = [],
visibility = ["//visibility:public"],
)

230
bazel/oci/pin.bzl Normal file
View File

@ -0,0 +1,230 @@
"""
This module contains rules and macros for pinning oci images.
"""
load("@aspect_bazel_lib//lib:jq.bzl", "jq")
load("@bazel_skylib//lib:types.bzl", "types")
def stamp_tags(name, repotags, **kwargs):
if not types.is_list(repotags):
fail("repotags should be a list")
_maybe_quote = lambda x: x if "\"" in x else "\"{}\"".format(x)
jq(
name = name,
srcs = [],
out = "_{}.tags.txt".format(name),
args = ["--raw-output"],
filter = "|".join([
"$ARGS.named.STAMP as $stamp",
",".join([_maybe_quote(t) for t in repotags]),
]),
**kwargs
)
def _oci_go_source_impl(ctx):
oci = ctx.file.oci
inputs = [oci]
if ctx.attr.tag_file:
inputs.append(ctx.file.tag_file)
output = ctx.actions.declare_file(ctx.label.name + ".go")
args = [
"codegen",
"--image-registry",
ctx.attr.registry,
"--image-prefix",
ctx.attr.prefix,
"--image-name",
ctx.attr.image_name,
"--oci-path",
oci.path,
"--package",
ctx.attr.package,
"--identifier",
ctx.attr.identifier,
"--output",
output.path,
]
if ctx.attr.tag:
args.append("--image-tag")
args.append(ctx.attr.tag)
if ctx.attr.tag_file:
args.append("--image-tag-file")
args.append(ctx.file.tag_file.path)
ctx.actions.run(
inputs = inputs,
arguments = args,
outputs = [output],
executable = ctx.executable._oci_pin,
mnemonic = "OCIPin",
progress_message = "Generating OCI pin Go source %{label}",
)
return [DefaultInfo(
files = depset([output]),
)]
_go_source_attrs = {
"identifier": attr.string(
mandatory = True,
doc = "Identifier to use for the generated Go source.",
),
"image_name": attr.string(
mandatory = True,
doc = "Image name to use for the generated Go source.",
),
"oci": attr.label(
mandatory = True,
allow_single_file = True,
doc = "OCI image to extract the digest from.",
),
"package": attr.string(
mandatory = True,
doc = "Package to use for the generated Go source.",
),
"prefix": attr.string(
doc = "Prefix to use for the generated Go source.",
),
"registry": attr.string(
mandatory = True,
doc = "Registry to use for the generated Go source.",
),
"tag": attr.string(
doc = "OCI image tag to use for the generated Go source.",
),
"tag_file": attr.label(
allow_single_file = True,
doc = "OCI image tag file to use for the generated Go source.",
),
"_oci_pin": attr.label(
allow_single_file = True,
executable = True,
cfg = "exec",
default = Label("//hack/oci-pin"),
),
}
oci_go_source = rule(
implementation = _oci_go_source_impl,
attrs = _go_source_attrs,
)
def _oci_sum_impl(ctx):
oci = ctx.file.oci
inputs = [oci]
if ctx.attr.tag_file:
inputs.append(ctx.file.tag_file)
output = ctx.actions.declare_file(ctx.label.name + ".sha256")
args = [
"sum",
"--image-name",
ctx.attr.image_name,
"--oci-path",
oci.path,
"--output",
output.path,
"--registry",
ctx.attr.registry,
]
if ctx.attr.prefix:
args.append("--prefix")
args.append(ctx.attr.prefix)
if ctx.attr.tag:
args.append("--image-tag")
args.append(ctx.attr.tag)
if ctx.attr.tag_file:
args.append("--image-tag-file")
args.append(ctx.file.tag_file.path)
ctx.actions.run(
inputs = inputs,
arguments = args,
outputs = [output],
executable = ctx.executable._oci_pin,
mnemonic = "OCISum",
progress_message = "Generating OCI sum file %{label}",
)
return [DefaultInfo(
files = depset([output]),
)]
_sum_attrs = {
"image_name": attr.string(
mandatory = True,
doc = "Image name to use for the sum entry.",
),
"oci": attr.label(
mandatory = True,
allow_single_file = True,
doc = "OCI image to extract the digest from.",
),
"prefix": attr.string(
doc = "Prefix to use for the sum entry.",
),
"registry": attr.string(
mandatory = True,
doc = "Registry to use for the sum entry.",
),
"tag": attr.string(
doc = "OCI image tag to use for the sum entry.",
),
"tag_file": attr.label(
allow_single_file = True,
doc = "OCI image tag file to use for the sum entry.",
),
"_oci_pin": attr.label(
allow_single_file = True,
executable = True,
cfg = "exec",
default = Label("//hack/oci-pin"),
),
}
oci_sum = rule(
implementation = _oci_sum_impl,
attrs = _sum_attrs,
)
def _oci_sum_merge_impl(ctx):
# TODO: select list of labels
inputs = ctx.files.sums
output = ctx.actions.declare_file(ctx.label.name + ".sha256")
args = [
"merge",
"--output",
output.path,
]
for sum in ctx.files.sums:
args.append("--input")
args.append(sum.path)
ctx.actions.run(
inputs = inputs,
arguments = args,
outputs = [output],
executable = ctx.executable._oci_pin,
mnemonic = "OCISumMerge",
progress_message = "Merging OCI sum files %{label}",
)
return [DefaultInfo(
files = depset([output]),
)]
_sum_merge_attrs = {
"sums": attr.label_list(
doc = "Sum files to merge for the combined sum entry.",
),
"_oci_pin": attr.label(
allow_single_file = True,
executable = True,
cfg = "exec",
default = Label("//hack/oci-pin"),
),
}
oci_sum_merge = rule(
implementation = _oci_sum_merge_impl,
attrs = _sum_merge_attrs,
)

60
bazel/release/BUILD.bazel Normal file
View File

@ -0,0 +1,60 @@
"""
This folder contains labels used to collect release artifacts.
"""
load("@com_github_ash2k_bazel_tools//multirun:def.bzl", "multirun")
load("//bazel/oci:containers.bzl", "container_sum", "containers", "oci_push", "oci_tarball")
load("//bazel/oci:pin.bzl", "oci_sum_merge")
[
oci_tarball(
name = container["name"] + "_tar",
image = container["oci"],
)
for container in containers()
]
[
container_sum(
name = container["name"],
image_name = container["image_name"],
oci = container["oci"],
prefix = container["prefix"],
registry = container["registry"],
tag_file = container["tag_file"],
)
for container in containers()
]
oci_sum_merge(
name = "container_sums",
sums = [
":%s_sum" % container["name"]
for container in containers()
],
visibility = ["//visibility:public"],
)
# TODO(malt3): use config setting to allow devs the use of custom registries
# https://www.grahambrooks.com/software-development/2021/08/30/user-defined-bazel-arguments.html
[
oci_push(
name = container["name"] + "_push",
image = container["oci"],
image_name = container["image_name"],
prefix = container["prefix"],
registry = container["registry"],
repotags = container["tag_file"],
)
for container in containers()
]
multirun(
name = "push",
commands = [
":" + container["name"] + "_push"
for container in containers()
],
jobs = 0, # execute in parallel
visibility = ["//visibility:public"],
)

View File

@ -1,4 +1,5 @@
load("@bazel_skylib//rules:common_settings.bzl", "bool_flag", "string_flag")
load("//bazel/oci:pin.bzl", "stamp_tags")
bool_flag(
# tpm_simulator is used to decide if the TPM simulator should be enabled
@ -44,3 +45,10 @@ config_setting(
flag_values = {":select_never": "True"},
visibility = ["//visibility:public"],
)
stamp_tags(
# generates a container image version tag based on the version stamp
name = "tag",
repotags = [""""v"+($stamp.STABLE_STAMP_VERSION // "0.0.0")"""],
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,16 @@
"""
This file contains external container images used by the project.
"""
load("@rules_oci//oci:pull.bzl", "oci_pull")
def containter_image_deps():
oci_pull(
name = "distroless_static",
digest = "sha256:c3c3d0230d487c0ad3a0d87ad03ee02ea2ff0b3dcce91ca06a1019e07de05f12",
image = "gcr.io/distroless/static",
platforms = [
"linux/amd64",
"linux/arm64",
],
)