metropolis/node: generate OCI index
This adds a multi-architecture OCI index target for metropolis. mkoci is
extended to add the platform to the index.json. Product info is extended
to add the PlatformOS field, which mkoci puts into the platform.
The OCI spec requires the os field of the platform to be set. We don't
need this field so we could simply put "unknown" there, but I think
"uefi" is nicer. Registry UIs display the os and architecture fields.
Change-Id: I24acf3c01201b50238abd30cfd86da2eda43c1d9
Reviewed-on: https://review.monogon.dev/c/monogon/+/4477
Tested-by: Jenkins CI
Reviewed-by: Tim Windelschmidt <tim@monogon.tech>
diff --git a/metropolis/node/BUILD.bazel b/metropolis/node/BUILD.bazel
index 4c44608..1295f0b 100644
--- a/metropolis/node/BUILD.bazel
+++ b/metropolis/node/BUILD.bazel
@@ -2,6 +2,7 @@
load("//osbase/build/genproductinfo:defs.bzl", "product_info")
load("//osbase/build/mkerofs:def.bzl", "erofs_image")
load("//osbase/build/mkoci:def.bzl", "oci_os_image")
+load("//osbase/build/mkoci/index:def.bzl", "oci_index")
load("//osbase/build/mkpayload:def.bzl", "efi_unified_kernel_image")
load("//osbase/build/mkverity:def.bzl", "verity_image")
@@ -150,6 +151,15 @@
visibility = ["//metropolis/test/launch:__pkg__"],
)
+oci_index(
+ name = "oci_multiarch",
+ src = ":oci_image",
+ platforms = [
+ "//build/platforms:linux_x86_64",
+ "//build/platforms:linux_aarch64",
+ ],
+)
+
product_info(
name = "product_info",
components = [
@@ -159,6 +169,7 @@
os_id = "metropolis-node",
os_name = "Metropolis Node",
out_os_release = ":product_info_os_release",
+ platform_os = "uefi",
stamp_var = "STABLE_MONOGON_metropolis_version",
visibility = [":__subpackages__"],
)
diff --git a/osbase/build/genproductinfo/defs.bzl b/osbase/build/genproductinfo/defs.bzl
index c484bbb..f2dde7c 100644
--- a/osbase/build/genproductinfo/defs.bzl
+++ b/osbase/build/genproductinfo/defs.bzl
@@ -12,6 +12,7 @@
args.add("-id", ctx.attr.os_id)
args.add("-architecture", ctx.attr.architecture)
args.add("-build_flags", "-".join(ctx.attr.build_flags))
+ args.add("-platform_os", ctx.attr.platform_os)
args.add_all(ctx.attr.components, before_each = "-component")
ctx.actions.run(
mnemonic = "GenProductInfo",
@@ -29,6 +30,7 @@
"os_name": attr.string(mandatory = True),
"os_id": attr.string(mandatory = True),
"stamp_var": attr.string(mandatory = True),
+ "platform_os": attr.string(default = "unknown"),
"components": attr.string_list(),
"out_os_release": attr.output(
mandatory = True,
diff --git a/osbase/build/genproductinfo/main.go b/osbase/build/genproductinfo/main.go
index e4b566d..87d6e09 100644
--- a/osbase/build/genproductinfo/main.go
+++ b/osbase/build/genproductinfo/main.go
@@ -31,6 +31,7 @@
flagID = flag.String("id", "", "id parameter (see freedesktop spec)")
flagArchitecture = flag.String("architecture", "", "CPU architecture")
flagBuildFlags = flag.String("build_flags", "", "build flags joined by '-'")
+ flagPlatformOS = flag.String("platform_os", "", "platform OS")
)
var (
@@ -117,6 +118,7 @@
CommitHash: statusVars["STABLE_MONOGON_gitCommit"],
CommitDate: statusVars["STABLE_MONOGON_gitCommitDate"],
BuildTreeDirty: statusVars["STABLE_MONOGON_gitTreeState"] == "dirty",
+ PlatformOS: *flagPlatformOS,
Components: components,
}
productInfoBytes, err := json.MarshalIndent(productInfo, "", "\t")
diff --git a/osbase/build/mkoci/main.go b/osbase/build/mkoci/main.go
index 1fb2254..7077887 100644
--- a/osbase/build/mkoci/main.go
+++ b/osbase/build/mkoci/main.go
@@ -34,6 +34,11 @@
outPath = flag.String("out", "", "Output OCI Image Layout directory path")
)
+var architectureToOCI = map[string]string{
+ "x86_64": "amd64",
+ "aarch64": "arm64",
+}
+
type payload struct {
name string
path string
@@ -272,14 +277,30 @@
}
// Write the index.
+ platformArchitecture, ok := architectureToOCI[productInfo.Architecture()]
+ if !ok {
+ log.Fatalf("Missing architectureToOCI entry for %q", productInfo.Architecture())
+ }
+ platformOS := productInfo.PlatformOS
+ if platformOS == "" {
+ platformOS = "unknown"
+ }
imageIndex := ocispecv1.Index{
Versioned: ocispec.Versioned{SchemaVersion: 2},
MediaType: ocispecv1.MediaTypeImageIndex,
Manifests: []ocispecv1.Descriptor{{
MediaType: ocispecv1.MediaTypeImageManifest,
ArtifactType: osimage.MediaTypeOSImageConfig,
- Digest: digest.NewDigestFromEncoded(digest.SHA256, imageManifestHash),
- Size: int64(len(imageManifestBytes)),
+ // The platform set here is used when building an OCI index with Bazel.
+ // Other consumers cannot rely on it being present because it is not
+ // preserved when pushing to a registry. We don't use the OS field, but
+ // it's required by the OCI spec.
+ Platform: &ocispecv1.Platform{
+ Architecture: platformArchitecture,
+ OS: platformOS,
+ },
+ Digest: digest.NewDigestFromEncoded(digest.SHA256, imageManifestHash),
+ Size: int64(len(imageManifestBytes)),
}},
}
imageIndexBytes, err := json.MarshalIndent(imageIndex, "", "\t")
diff --git a/osbase/oci/osimage/types.go b/osbase/oci/osimage/types.go
index c94ae35..b42c701 100644
--- a/osbase/oci/osimage/types.go
+++ b/osbase/oci/osimage/types.go
@@ -62,6 +62,9 @@
// differs from the tree of the commit referenced by commit_hash.
BuildTreeDirty bool `json:"build_tree_dirty"`
+ // PlatformOS on top of which the product executes. This is put into the
+ // platform os field when building an OCI image index. Example: "uefi"
+ PlatformOS string `json:"platform_os"`
// Components contains versions of the most important components. These are
// mostly intended for human consumption, but could also be used for certain
// automations, e.g. automatically deriving Kubernetes compatibility