Skip to content

Commit 139cdf9

Browse files
committed
osbuild: backport patches for container-storage usage
This backports osbuild/osbuild#2222 which will allow us to drop at least one skopeo copy to help speed things up.
1 parent 56398e6 commit 139cdf9

6 files changed

+477
-19
lines changed

build.sh

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -192,25 +192,30 @@ write_archive_info() {
192192
}
193193

194194
patch_osbuild() {
195-
# Add a few patches that either haven't made it into a release or
196-
# that will be obsoleted with other work that will be done soon.
197-
return 0
198-
199-
# # To make it easier to apply patches we'll move around the osbuild
200-
# # code on the system first:
201-
# rmdir /usr/lib/osbuild/osbuild
202-
# mv /usr/lib/python3.13/site-packages/osbuild /usr/lib/osbuild/
203-
# mkdir /usr/lib/osbuild/tools
204-
# mv /usr/bin/osbuild-mpp /usr/lib/osbuild/tools/
205-
206-
# # Now all the software is under the /usr/lib/osbuild dir and we can patch
207-
# patch -d /usr/lib/osbuild -p1 < /usr/lib/coreos-assembler/src/my-git-commit.patch
208-
209-
# # And then move the files back; supermin appliance creation will need it back
210-
# # in the places delivered by the RPM.
211-
# mv /usr/lib/osbuild/tools/osbuild-mpp /usr/bin/osbuild-mpp
212-
# mv /usr/lib/osbuild/osbuild /usr/lib/python3.13/site-packages/osbuild
213-
# mkdir -p /usr/lib/osbuild/osbuild
195+
# Add a few patches that either haven't made it into a release or
196+
# that will be obsoleted with other work that will be done soon.
197+
198+
# To make it easier to apply patches we'll move around the osbuild
199+
# code on the system first:
200+
rmdir /usr/lib/osbuild/osbuild
201+
mv /usr/lib/python3.13/site-packages/osbuild /usr/lib/osbuild/
202+
mkdir -p /usr/lib/osbuild/tools
203+
mv /usr/bin/osbuild-mpp /usr/lib/osbuild/tools/
204+
205+
# Now all the software is under the /usr/lib/osbuild dir and we can patch
206+
cat \
207+
/usr/lib/coreos-assembler/0001-osbuild-util-containers.py-add-container_mount-funct.patch \
208+
/usr/lib/coreos-assembler/0002-osbuild-util-containers.py-rename-variable.patch \
209+
/usr/lib/coreos-assembler/0003-osbuild-util-containers.py-drop-copy-when-using-cont.patch \
210+
/usr/lib/coreos-assembler/0004-drop-remove_signatures-from-org.osbuild.container-de.patch \
211+
/usr/lib/coreos-assembler/0005-tools-osbuild-mpp-support-mpp-resolve-for-org.osbuil.patch |
212+
patch -d /usr/lib/osbuild -p1
213+
214+
# And then move the files back; supermin appliance creation will need it back
215+
# in the places delivered by the RPM.
216+
mv /usr/lib/osbuild/tools/osbuild-mpp /usr/bin/osbuild-mpp
217+
mv /usr/lib/osbuild/osbuild /usr/lib/python3.13/site-packages/osbuild
218+
mkdir -p /usr/lib/osbuild/osbuild
214219
}
215220

216221
if [ $# -ne 0 ]; then
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
From 701fa40dfce9a70c1dafdee32907a154463a1a44 Mon Sep 17 00:00:00 2001
2+
From: Dusty Mabe <[email protected]>
3+
Date: Thu, 9 Oct 2025 22:05:44 -0400
4+
Subject: [PATCH 1/5] osbuild/util/containers.py: add container_mount()
5+
functionality
6+
7+
As prep for a later patch this moves the container image mounting code
8+
from stages/org.osbuild.container-deploy into the containers library.
9+
10+
This commit also removes the associated unit test since it wasn't doing
11+
much and also there doesn't seem to be a place for unit testing
12+
osbuild/util/containers.py.
13+
---
14+
osbuild/util/containers.py | 42 ++++++++++++++++++++++++++-
15+
stages/org.osbuild.container-deploy | 45 ++---------------------------
16+
2 files changed, 43 insertions(+), 44 deletions(-)
17+
18+
diff --git a/osbuild/util/containers.py b/osbuild/util/containers.py
19+
index 49eceee2..352c5c8e 100644
20+
--- a/osbuild/util/containers.py
21+
+++ b/osbuild/util/containers.py
22+
@@ -1,8 +1,10 @@
23+
import json
24+
import os
25+
+import random
26+
+import string
27+
import subprocess
28+
import tempfile
29+
-from contextlib import contextmanager
30+
+from contextlib import ExitStack, contextmanager
31+
32+
from osbuild.util.mnt import MountGuard, MountPermissions
33+
34+
@@ -184,3 +186,41 @@ def container_source(image):
35+
# that the inner ctx manager won't be cleaned up until the execution returns to this ctx manager.
36+
with container_source_fn(image, image_filepath, container_format) as image_source:
37+
yield image_name, image_source
38+
+
39+
+
40+
+@contextmanager
41+
+def container_mount(image, remove_signatures=False):
42+
+ # Helper function for doing the `podman image mount`
43+
+ @contextmanager
44+
+ def _mount_container(image_tag):
45+
+ cmd = ["podman", "image", "mount", image_tag],
46+
+ result = subprocess.run(cmd, encoding="utf-8", check=False,
47+
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
48+
+ if result.returncode != 0:
49+
+ code = result.returncode
50+
+ msg = result.stderr.strip()
51+
+ raise RuntimeError(f"Failed to mount image ({code}): {msg}")
52+
+ try:
53+
+ yield result.stdout.strip()
54+
+ finally:
55+
+ cmd = ["podman", "image", "umount", image_tag],
56+
+ subprocess.run(cmd, check=True)
57+
+
58+
+ # We cannot use a tmpdir as storage here because of
59+
+ # https://github.com/containers/storage/issues/1779 so instead
60+
+ # just pick a random suffix. This runs inside bwrap which gives a
61+
+ # tmp /var so it does not really matter much.
62+
+ tmp_image_tag = "tmp-container-mount-" + "".join(random.choices(string.digits, k=14))
63+
+ with container_source(image) as (_, source):
64+
+ with ExitStack() as cm:
65+
+ cm.callback(subprocess.run, ["podman", "rmi", tmp_image_tag], check=True)
66+
+ # skopeo needs /var/tmp but the bwrap env is minimal and may not have it
67+
+ os.makedirs("/var/tmp", mode=0o1777, exist_ok=True)
68+
+ cmd = ["skopeo", "copy"]
69+
+ if remove_signatures:
70+
+ cmd.append("--remove-signatures")
71+
+ cmd.extend([source, f"containers-storage:{tmp_image_tag}"])
72+
+ subprocess.run(cmd, check=True)
73+
+
74+
+ with _mount_container(tmp_image_tag) as container_mountpoint:
75+
+ yield container_mountpoint
76+
diff --git a/stages/org.osbuild.container-deploy b/stages/org.osbuild.container-deploy
77+
index 29fe7b97..f933ac73 100755
78+
--- a/stages/org.osbuild.container-deploy
79+
+++ b/stages/org.osbuild.container-deploy
80+
@@ -1,8 +1,4 @@
81+
#!/usr/bin/python3
82+
-import contextlib
83+
-import os
84+
-import random
85+
-import string
86+
import subprocess
87+
import sys
88+
89+
@@ -10,51 +6,14 @@ import osbuild.api
90+
from osbuild.util import containers
91+
92+
93+
-@contextlib.contextmanager
94+
-def mount_container(image_tag):
95+
- result = subprocess.run(
96+
- ["podman", "image", "mount", image_tag],
97+
- stdout=subprocess.PIPE,
98+
- stderr=subprocess.PIPE,
99+
- encoding="utf-8",
100+
- check=False,
101+
- )
102+
- if result.returncode != 0:
103+
- code = result.returncode
104+
- msg = result.stderr.strip()
105+
- raise RuntimeError(f"Failed to mount image ({code}): {msg}")
106+
- try:
107+
- yield result.stdout.strip()
108+
- finally:
109+
- subprocess.run(
110+
- ["podman", "image", "umount", image_tag],
111+
- check=True,
112+
- )
113+
-
114+
-
115+
def main(inputs, tree, options):
116+
images = containers.parse_containers_input(inputs)
117+
assert len(images) == 1
118+
image = list(images.values())[0]
119+
remove_signatures = options.get("remove-signatures")
120+
121+
- # skopeo needs /var/tmp but the bwrap env is minimal and may not have it
122+
- os.makedirs("/var/tmp", mode=0o1777, exist_ok=True)
123+
- # We cannot use a tmpdir as storage here because of
124+
- # https://github.com/containers/storage/issues/1779 so instead
125+
- # just pick a random suffix. This runs inside bwrap which gives a
126+
- # tmp /var so it does not really matter much.
127+
- image_tag = "tmp-container-deploy-" + "".join(random.choices(string.digits, k=14))
128+
- with contextlib.ExitStack() as cm:
129+
- cm.callback(subprocess.run, ["podman", "rmi", image_tag], check=True)
130+
- with containers.container_source(image) as (_, source):
131+
- cmd = ["skopeo", "copy"]
132+
- if remove_signatures:
133+
- cmd.append("--remove-signatures")
134+
- cmd.extend([source, f"containers-storage:{image_tag}"])
135+
- subprocess.run(cmd, check=True)
136+
- with mount_container(image_tag) as img:
137+
- subprocess.run(["cp", "-a", f"{img}/.", f"{tree}/"], check=True)
138+
+ with containers.container_mount(image, remove_signatures) as container_mountpoint:
139+
+ subprocess.run(["cp", "-a", f"{container_mountpoint}/.", f"{tree}/"], check=True)
140+
# postprocess the tree, would be nicer to filter before already
141+
for exclude in options.get("exclude", []):
142+
subprocess.run(["rm", "-rf", f"{tree}/{exclude}"], check=True)
143+
--
144+
2.51.0
145+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
From 81b9bd29075c004c48a5de685904328f657f33bd Mon Sep 17 00:00:00 2001
2+
From: Dusty Mabe <[email protected]>
3+
Date: Fri, 10 Oct 2025 09:04:21 -0400
4+
Subject: [PATCH 2/5] osbuild/util/containers.py: rename variable
5+
6+
This is really a name and not a tag (it doesn't include :tag) so let's
7+
rename the variable to be a little more clear.
8+
---
9+
osbuild/util/containers.py | 8 ++++----
10+
1 file changed, 4 insertions(+), 4 deletions(-)
11+
12+
diff --git a/osbuild/util/containers.py b/osbuild/util/containers.py
13+
index 352c5c8e..9e321315 100644
14+
--- a/osbuild/util/containers.py
15+
+++ b/osbuild/util/containers.py
16+
@@ -210,17 +210,17 @@ def container_mount(image, remove_signatures=False):
17+
# https://github.com/containers/storage/issues/1779 so instead
18+
# just pick a random suffix. This runs inside bwrap which gives a
19+
# tmp /var so it does not really matter much.
20+
- tmp_image_tag = "tmp-container-mount-" + "".join(random.choices(string.digits, k=14))
21+
+ tmp_image_name = "tmp-container-mount-" + "".join(random.choices(string.digits, k=14))
22+
with container_source(image) as (_, source):
23+
with ExitStack() as cm:
24+
- cm.callback(subprocess.run, ["podman", "rmi", tmp_image_tag], check=True)
25+
+ cm.callback(subprocess.run, ["podman", "rmi", tmp_image_name], check=True)
26+
# skopeo needs /var/tmp but the bwrap env is minimal and may not have it
27+
os.makedirs("/var/tmp", mode=0o1777, exist_ok=True)
28+
cmd = ["skopeo", "copy"]
29+
if remove_signatures:
30+
cmd.append("--remove-signatures")
31+
- cmd.extend([source, f"containers-storage:{tmp_image_tag}"])
32+
+ cmd.extend([source, f"containers-storage:{tmp_image_name}"])
33+
subprocess.run(cmd, check=True)
34+
35+
- with _mount_container(tmp_image_tag) as container_mountpoint:
36+
+ with _mount_container(tmp_image_name) as container_mountpoint:
37+
yield container_mountpoint
38+
--
39+
2.51.0
40+
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
From dd484a371b04bb384d6d189b038d6c7997a3da44 Mon Sep 17 00:00:00 2001
2+
From: Dusty Mabe <[email protected]>
3+
Date: Wed, 15 Oct 2025 15:13:38 -0400
4+
Subject: [PATCH 3/5] osbuild/util/containers.py: drop copy when using
5+
containers-storage input
6+
7+
If we are just mounting the container then there's really no reason
8+
we can't just `podman mount` it directly rather than copying it from
9+
one local containers-storage to another.
10+
---
11+
osbuild/util/containers.py | 62 ++++++++++++++++++++++----------------
12+
1 file changed, 36 insertions(+), 26 deletions(-)
13+
14+
diff --git a/osbuild/util/containers.py b/osbuild/util/containers.py
15+
index 9e321315..06a47a0d 100644
16+
--- a/osbuild/util/containers.py
17+
+++ b/osbuild/util/containers.py
18+
@@ -8,6 +8,9 @@ from contextlib import ExitStack, contextmanager
19+
20+
from osbuild.util.mnt import MountGuard, MountPermissions
21+
22+
+# use `/run/osbuild/containers/storage` for the host's containers-storage bind mount
23+
+HOST_CONTAINERS_STORAGE = os.path.join(os.sep, "run", "osbuild", "containers", "storage")
24+
+
25+
26+
def is_manifest_list(data):
27+
"""Inspect a manifest determine if it's a multi-image manifest-list."""
28+
@@ -118,10 +121,7 @@ def containers_storage_source(image, image_filepath, container_format):
29+
storage_conf = image["data"]["storage"]
30+
driver = storage_conf.get("driver", "overlay")
31+
32+
- # use `/run/osbuild/containers/storage` for the containers-storage bind mount
33+
- # since this ostree-compatible and the stage that uses this will be run
34+
- # inside a ostree-based build-root in `bootc-image-builder`
35+
- storage_path = os.path.join(os.sep, "run", "osbuild", "containers", "storage")
36+
+ storage_path = HOST_CONTAINERS_STORAGE
37+
os.makedirs(storage_path, exist_ok=True)
38+
39+
with MountGuard() as mg:
40+
@@ -192,10 +192,10 @@ def container_source(image):
41+
def container_mount(image, remove_signatures=False):
42+
# Helper function for doing the `podman image mount`
43+
@contextmanager
44+
- def _mount_container(image_tag):
45+
- cmd = ["podman", "image", "mount", image_tag],
46+
- result = subprocess.run(cmd, encoding="utf-8", check=False,
47+
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
48+
+ def _mount_container(img, imagestore=None):
49+
+ cmd = ["podman", f"--imagestore={imagestore}"] if imagestore else ["podman"]
50+
+ result = subprocess.run(cmd + ["image", "mount", img], encoding="utf-8",
51+
+ check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
52+
if result.returncode != 0:
53+
code = result.returncode
54+
msg = result.stderr.strip()
55+
@@ -203,24 +203,34 @@ def container_mount(image, remove_signatures=False):
56+
try:
57+
yield result.stdout.strip()
58+
finally:
59+
- cmd = ["podman", "image", "umount", image_tag],
60+
- subprocess.run(cmd, check=True)
61+
-
62+
- # We cannot use a tmpdir as storage here because of
63+
- # https://github.com/containers/storage/issues/1779 so instead
64+
- # just pick a random suffix. This runs inside bwrap which gives a
65+
- # tmp /var so it does not really matter much.
66+
- tmp_image_name = "tmp-container-mount-" + "".join(random.choices(string.digits, k=14))
67+
+ subprocess.run(cmd + ["image", "umount", img], check=True)
68+
+
69+
with container_source(image) as (_, source):
70+
with ExitStack() as cm:
71+
- cm.callback(subprocess.run, ["podman", "rmi", tmp_image_name], check=True)
72+
- # skopeo needs /var/tmp but the bwrap env is minimal and may not have it
73+
- os.makedirs("/var/tmp", mode=0o1777, exist_ok=True)
74+
- cmd = ["skopeo", "copy"]
75+
- if remove_signatures:
76+
- cmd.append("--remove-signatures")
77+
- cmd.extend([source, f"containers-storage:{tmp_image_name}"])
78+
- subprocess.run(cmd, check=True)
79+
-
80+
- with _mount_container(tmp_image_name) as container_mountpoint:
81+
+ img = ""
82+
+ imagestore = None
83+
+ if image["data"]["format"] == 'containers-storage':
84+
+ # In the case where we are container storage we don't need to
85+
+ # skopeo copy. We already have access to a mounted container storage
86+
+ # that has the image ready to use.
87+
+ image_id = image["checksum"].split(":")[1]
88+
+ img = image_id
89+
+ imagestore = HOST_CONTAINERS_STORAGE
90+
+ else:
91+
+ # We cannot use a tmpdir as storage here because of
92+
+ # https://github.com/containers/storage/issues/1779 so instead
93+
+ # just pick a random suffix. This runs inside bwrap which gives a
94+
+ # tmp /var so it does not really matter much.
95+
+ tmp_image_name = "tmp-container-mount-" + "".join(random.choices(string.digits, k=14))
96+
+ cm.callback(subprocess.run, ["podman", "rmi", tmp_image_name], check=True)
97+
+ # skopeo needs /var/tmp but the bwrap env is minimal and may not have it
98+
+ os.makedirs("/var/tmp", mode=0o1777, exist_ok=True)
99+
+ cmd = ["skopeo", "copy"]
100+
+ if remove_signatures:
101+
+ cmd.append("--remove-signatures")
102+
+ cmd.extend([source, f"containers-storage:{tmp_image_name}"])
103+
+ subprocess.run(cmd, check=True)
104+
+ img = tmp_image_name
105+
+
106+
+ with _mount_container(img, imagestore) as container_mountpoint:
107+
yield container_mountpoint
108+
--
109+
2.51.0
110+

0 commit comments

Comments
 (0)