treewide: add product info to OCI OS images
Add the product info to the OCI OS image config.
Change-Id: I70c572f2698c8d8bb0edc0ba969d8c6b37ae4c00
Reviewed-on: https://review.monogon.dev/c/monogon/+/4193
Reviewed-by: Tim Windelschmidt <tim@monogon.tech>
Tested-by: Jenkins CI
diff --git a/osbase/build/genproductinfo/test.bzl b/osbase/build/genproductinfo/test.bzl
new file mode 100644
index 0000000..e639867
--- /dev/null
+++ b/osbase/build/genproductinfo/test.bzl
@@ -0,0 +1,39 @@
+# Copyright The Monogon Project Authors.
+# SPDX-License-Identifier: Apache-2.0
+
+def _test_product_info_impl(ctx):
+ raw_product_info = json.encode({
+ "id": ctx.attr.os_id,
+ "name": ctx.attr.os_name,
+ "version": "0.0.0",
+ "variant": ctx.attr.architecture,
+ "architecture": ctx.attr.architecture,
+ })
+ product_info_file = ctx.actions.declare_file(ctx.label.name + ".json")
+ ctx.actions.write(product_info_file, raw_product_info)
+ return [DefaultInfo(files = depset([product_info_file]))]
+
+_test_product_info = rule(
+ implementation = _test_product_info_impl,
+ attrs = {
+ "os_name": attr.string(mandatory = True),
+ "os_id": attr.string(mandatory = True),
+ "architecture": attr.string(mandatory = True),
+ },
+)
+
+def _test_product_info_macro_impl(**kwargs):
+ _test_product_info(
+ architecture = select({
+ "@platforms//cpu:x86_64": "x86_64",
+ "@platforms//cpu:aarch64": "aarch64",
+ }),
+ **kwargs
+ )
+
+test_product_info = macro(
+ inherit_attrs = _test_product_info,
+ attrs = {"architecture": None},
+ implementation = _test_product_info_macro_impl,
+ doc = "This is a simplified variant of product_info for use in tests.",
+)
diff --git a/osbase/build/mkoci/def.bzl b/osbase/build/mkoci/def.bzl
index f7f491d..8b688a7 100644
--- a/osbase/build/mkoci/def.bzl
+++ b/osbase/build/mkoci/def.bzl
@@ -1,6 +1,10 @@
def _oci_os_image_impl(ctx):
inputs = []
arguments = []
+
+ inputs.append(ctx.file.product_info)
+ arguments += ["-product_info", ctx.file.product_info.path]
+
for name, label in ctx.attr.srcs.items():
files = label[DefaultInfo].files.to_list()
if len(files) != 1:
@@ -42,6 +46,13 @@
Build an OS image OCI artifact.
""",
attrs = {
+ "product_info": attr.label(
+ doc = """
+ Product info of the OS in JSON format.
+ """,
+ mandatory = True,
+ allow_single_file = True,
+ ),
"srcs": attr.string_keyed_label_dict(
doc = """
Payloads to include in the OCI artifact.
diff --git a/osbase/build/mkoci/main.go b/osbase/build/mkoci/main.go
index 16dd268..1fb2254 100644
--- a/osbase/build/mkoci/main.go
+++ b/osbase/build/mkoci/main.go
@@ -28,6 +28,7 @@
var payloadNameRegexp = regexp.MustCompile(`^[0-9A-Za-z-](?:[0-9A-Za-z._-]{0,78}[0-9A-Za-z_-])?$`)
var (
+ productInfoPath = flag.String("product_info", "", "Path to the product info JSON file")
payloadName = flag.String("payload_name", "", "Payload name for the next payload_file flag")
compressionLevel = flag.Int("compression_level", int(zstd.SpeedDefault), "Compression level")
outPath = flag.String("out", "", "Output OCI Image Layout directory path")
@@ -201,9 +202,19 @@
})
flag.Parse()
+ rawProductInfo, err := os.ReadFile(*productInfoPath)
+ if err != nil {
+ log.Fatalf("Failed to read product info file: %v", err)
+ }
+ var productInfo osimage.ProductInfo
+ err = json.Unmarshal(rawProductInfo, &productInfo)
+ if err != nil {
+ log.Fatal(err)
+ }
+
// Create blobs directory.
blobsPath := filepath.Join(*outPath, "blobs", "sha256")
- err := os.MkdirAll(blobsPath, 0755)
+ err = os.MkdirAll(blobsPath, 0755)
if err != nil {
log.Fatal(err)
}
@@ -223,6 +234,7 @@
// Write the OS image config.
imageConfig := osimage.Config{
FormatVersion: osimage.ConfigVersion,
+ ProductInfo: productInfo,
Payloads: payloadInfos,
}
imageConfigBytes, err := json.MarshalIndent(imageConfig, "", "\t")
diff --git a/osbase/oci/osimage/BUILD.bazel b/osbase/oci/osimage/BUILD.bazel
index f54cdfb..62a1401 100644
--- a/osbase/oci/osimage/BUILD.bazel
+++ b/osbase/oci/osimage/BUILD.bazel
@@ -23,6 +23,7 @@
# the chunking, but also not too large.
"test": "//third_party/linux",
},
+ product_info = ":test_product_info.json",
visibility = ["//osbase/oci:__subpackages__"],
)
@@ -32,6 +33,7 @@
"test": "//third_party/linux",
},
compression_level = 0,
+ product_info = ":test_product_info.json",
visibility = ["//osbase/oci:__subpackages__"],
)
diff --git a/osbase/oci/osimage/test_product_info.json b/osbase/oci/osimage/test_product_info.json
new file mode 100644
index 0000000..ba6096f
--- /dev/null
+++ b/osbase/oci/osimage/test_product_info.json
@@ -0,0 +1,10 @@
+{
+ "id": "testos",
+ "name": "Test OS",
+ "version": "0.0.0",
+ "variant": "x86_64",
+ "architecture": "x86_64",
+ "commit_hash": "",
+ "commit_date": "",
+ "build_tree_dirty": false
+}
diff --git a/osbase/oci/osimage/types.go b/osbase/oci/osimage/types.go
index d251a74..c94ae35 100644
--- a/osbase/oci/osimage/types.go
+++ b/osbase/oci/osimage/types.go
@@ -21,6 +21,8 @@
// FormatVersion should be incremented when making breaking changes to the
// image format. Readers must stop when they see an unknown version.
FormatVersion string `json:"format_version"`
+ // ProductInfo contains information about the content of the image.
+ ProductInfo ProductInfo `json:"product_info"`
// Payloads describes the payloads contained in the image. It has the same
// length and order as the layers list in the image manifest.
Payloads []PayloadInfo `json:"payloads"`