Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion behave/steps/other.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ def step_impl(context):

schemafile = os.path.join(os.path.dirname(__file__), '..', '..',
"mock", "docs",
"buildroot-lock-schema-1.0.0.json")
"buildroot-lock-schema-1.1.0.json")
with open(schemafile, "r", encoding="utf-8") as fd:
schema = json.load(fd)

Expand Down
1 change: 1 addition & 0 deletions docs/Plugin-BuildrootLock.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ installed together with the Mock RPM package:

rpm -ql mock | grep schema
/usr/share/doc/mock/buildroot-lock-schema-1.0.0.json
/usr/share/doc/mock/buildroot-lock-schema-1.1.0.json

Currently, we do not provide a compatibility promise. Only the exact same
version of Mock that produced the file is guaranteed to read and process it.
Expand Down
120 changes: 120 additions & 0 deletions mock/docs/buildroot-lock-schema-1.1.0.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
{
"$id": "https://raw.githubusercontent.com/rpm-software-management/mock/main/mock/docs/buildroot-lock-schema-1.1.0.json",
"$schema": "http://json-schema.org/draft-06/schema#",
"type": "object",
"title": "Mock buildroot_lock.json file specification",
"description": "Version 1.1.0; last updated 2025-02-03",
"additionalProperties": false,
"properties": {
"version": {
"description": "Version of the https://raw.githubusercontent.com/rpm-software-management/mock/main/mock/docs/buildroot-lock-schema.json schema the document conforms to. Semantic versioned. Mock that implements v2.Y.Z versions no longer reads v1.Y.Z.",
"const": "1.1.0"
},
"buildroot": {
"description": "The object that describes the Mock buildroot",
"type": "object",
"additionalProperties": false,
"properties": {
"rpms": {
"description": "List of RPM packages installed in the buildroot",
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"arch": {
"description": "Architecture for which the package was built, 'noarch' for arch agnostic packages",
"type": "string"
},
"epoch": {
"description": "Epoch number of the package",
"type": ["string", "null"]
},
"license": {
"description": "The distribution license(s) of the package",
"type": "string"
},
"name": {
"description": "Name of the package",
"type": "string"
},
"release": {
"description": "Release (downstream) number of the package",
"type": "string"
},
"sigmd5": {
"description": "The SIGMD5 tag from the rpm header.",
"type": "string"
},
"signature": {
"description": "The signature used to sign the rpm (if any), last 8 characters from the \"rpm -q --qf '%{sigpgp:pgpsig}\n'\" output",
"type": ["string", "null"]
},
"url": {
"description": "Uniform Resource Locator that points to additional information on the packaged software",
"type": "string"
},
"version": {
"description": "Version (upstream) of the package",
"type": "string"
}
},
"required": [
"arch",
"epoch",
"license",
"name",
"release",
"sigmd5",
"signature",
"url",
"version"
]
}
}
},
"required": [
"rpms"
]
},
"bootstrap": {
"description": "The object that describes the Mock bootstrap chroot. Optional, only provided when bootstrap (image) is used.",
"type": "object",
"additionalProperties": false,
"required": [
"image_digest",
"pull_digest",
"architecture",
"id"
],
"properties": {
"image_digest": {
"description": "SHA256 digest concatenated RootFS layer digests and Config section from 'podman image inspect' command, sha256 string",
"type": "string"
},
"pull_digest": {
"description": "Image digest, as reported by podman inspect, can be used for podman pull.",
"type": "string"
},
"architecture": {
"description": "OCI architecture string, as reported by podman inspect .Architecture field.",
"type": "string"
},
"id": {
"type": "string",
"description": "Image ID, as reported by podman inspect .Id"
}
}
},
"config": {
"description": "A set of important Mock configuration options used when the buildroot was generated (Mock's internal)",
"type": "object",
"properties": {}
}
},
"required": [
"buildroot",
"config",
"version"
]
}
14 changes: 11 additions & 3 deletions mock/py/mock-hermetic-repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,18 @@ def _argparser():
return parser


def prepare_image(image_specification, outputdir):
def prepare_image(image_specification, bootstrap_data, outputdir):
"""
Store the tarball into the same directory where the RPMs are
"""
subprocess.check_output(["podman", "pull", image_specification])
pull_cmd = ["podman", "pull"]
if "pull_digest" in bootstrap_data:
image_specification += "@" + bootstrap_data["pull_digest"]
if "architecture" in bootstrap_data:
pull_cmd += ["--arch", bootstrap_data["architecture"]]
pull_cmd += [image_specification]
log.info("Pulling like: %s", ' '.join(pull_cmd))
subprocess.check_output(pull_cmd)
subprocess.check_output(["podman", "save", "--format=oci-archive", "--quiet",
"-o", os.path.join(outputdir, "bootstrap.tar"),
image_specification])
Expand Down Expand Up @@ -136,7 +143,8 @@ def _main():

subprocess.check_call(["createrepo_c", options.output_repo])

prepare_image(data["config"]["bootstrap_image"], options.output_repo)
prepare_image(data["config"]["bootstrap_image"], data["bootstrap"],
options.output_repo)


if __name__ == "__main__":
Expand Down
10 changes: 4 additions & 6 deletions mock/py/mockbuild/plugins/buildroot_lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def _executor(cmd):
# the latest Mock implementing with Major == 1 can read any
# version from the 1.Y.Z range. Mock implementing v2.Y.Z
# no longer reads v1.Y.Z variants.
"version": "1.0.0",
"version": "1.1.0",
"buildroot": {
"rpms": packages,
},
Expand Down Expand Up @@ -104,12 +104,10 @@ def _executor(cmd):
try:
podman = Podman(self.buildroot,
data["config"]["bootstrap_image"])
digest = podman.get_oci_digest()
data["bootstrap"] = podman.inspect_hermetic_metadata()
data["bootstrap"]["image_digest"] = podman.get_oci_digest()
except PodmanError:
digest = "unknown"
data["bootstrap"] = {
"image_digest": digest,
}
data["bootstrap"] = {}

with open(out_file, "w", encoding="utf-8") as fdlist:
fdlist.write(json.dumps(data, indent=4, sort_keys=True) + "\n")
Expand Down
16 changes: 15 additions & 1 deletion mock/py/mockbuild/podman.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,25 @@ def read_image_id(self):
"""
cmd = ["podman", "image", "inspect", self.image, "--format",
"{{ .Id }}"]
getLog().info("Removing image %s", self.image)
getLog().info("Reading image .ID from %s", self.image)
res = subprocess.run(cmd, env=self.buildroot.env,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
check=True)
self.image_id = res.stdout.decode("utf-8").strip()


def inspect_hermetic_metadata(self):
"""
Get the image metadata needed for the subsequent hermetic build.
"""
get_query = '{"pull_digest": "{{ .Digest }}", "id": "{{.Id}}", "architecture": "{{ .Architecture }}"}'
getLog().info("Reading image %s from %s", get_query, self.image)
cmd = ["podman", "image", "inspect", "--format", get_query, self.image]
res = subprocess.run(cmd, env=self.buildroot.env,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
check=True)
return json.loads(res.stdout.decode("utf-8").strip())


def __repr__(self):
return "Podman({}({}))".format(self.image, self.image_id)
13 changes: 12 additions & 1 deletion mock/tests/test_buildroot_lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Test the methods that generate buildroot_lock.json
"""

import copy
import json
import os
import tempfile
Expand Down Expand Up @@ -32,7 +33,7 @@
"""

EXPECTED_OUTPUT = {
'version': '1.0.0',
'version': '1.1.0',
'buildroot': {
'rpms': [{
'arch': 'x86_64',
Expand Down Expand Up @@ -61,6 +62,9 @@
},
"bootstrap": {
"image_digest": "sha256:ba1067bef190fbe88f085bd019464a8c0803b7cd1e3f",
"pull_digest": "sha256:1d9f0eaec60b59a669b285d1e775d970061b9694d4998b5bdb9626c9f33685cd",
"id": "b222730c2ba32385173fe3351026c316c9a294fa8d8c3c0f80d8afdb1b1aeef7",
"architecture": "amd64",
},
'config': {
'bootstrap_image': 'foo',
Expand All @@ -72,6 +76,12 @@
}


def _exp_output_bootstrap(exp_data):
data = copy.deepcopy(exp_data["bootstrap"])
del data["image_digest"]
return data


def _mock_vars(rpm_out, repoquery_out):
tc = TestCase()
tc.maxDiff = None
Expand Down Expand Up @@ -100,6 +110,7 @@ def _call_method(plugins, buildroot):

podman_obj = MagicMock()
podman_obj.get_oci_digest.return_value = EXPECTED_OUTPUT["bootstrap"]["image_digest"]
podman_obj.inspect_hermetic_metadata.return_value = _exp_output_bootstrap(EXPECTED_OUTPUT)
podman_cls = MagicMock(return_value=podman_obj)
with patch("mockbuild.plugins.buildroot_lock.Podman", side_effect=podman_cls):
method()
Expand Down
9 changes: 9 additions & 0 deletions releng/release-notes-next/hermetic-arch-specific.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
The buildroot lockfile generator [has been modified][PR#1548] to include
additional bootstrap image metadata that can be later used for a precise image
pulling.

The mock-hermetic-repo script has also been modified, to respect the additional
metadata. This allows us to, e.g., download bootstrap image of a different
(cross) architecture then the platform/host architecture is. In turn, the
script is now fully arch-agnostic (any host arch may be used for downloading
files from any arch specific lockfile).
Loading