Add OCI platform support for x86_64 sub-architecture containers
Add cache_key option to yum_cache plugin for shared package cache across configs
This commit is contained in:
parent
b965f54059
commit
1cf5696bf9
234
SOURCES/1000-add-oci-platform-support-for-x86_64-sub-arch.patch
Normal file
234
SOURCES/1000-add-oci-platform-support-for-x86_64-sub-arch.patch
Normal file
@ -0,0 +1,234 @@
|
||||
From f18ddbae4f900b54b3e2c3abe8270cf37f889e33 Mon Sep 17 00:00:00 2001
|
||||
From: Andrew Lukoshko <andrew.lukoshko@gmail.com>
|
||||
Date: Mon, 6 Apr 2026 19:12:45 +0200
|
||||
Subject: [PATCH 1/2] feat: add OCI platform support for x86_64
|
||||
sub-architecture containers
|
||||
|
||||
Add oci_platform_map config dict mapping x86_64_v2/v3/v4 to OCI
|
||||
platform strings (linux/amd64/v2, v3, v4).
|
||||
|
||||
When target_arch has a mapping, podman pull receives --platform so
|
||||
the correct image variant is fetched from multi-arch manifests.
|
||||
|
||||
Preserve target repo_arch for bootstrap chroot when target_arch has
|
||||
an oci_platform_map entry, so the bootstrap uses the correct
|
||||
sub-architecture repos instead of falling back to the base arch.
|
||||
|
||||
Includes 7 unit tests covering platform injection, architecture
|
||||
check, and error paths.
|
||||
|
||||
Fixes: #1732
|
||||
---
|
||||
py/mock.py | 8 +-
|
||||
py/mockbuild/config.py | 7 +
|
||||
py/mockbuild/podman.py | 7 +-
|
||||
tests/test_podman.py | 130 ++++++++++++++++++
|
||||
.../oci-platform-sub-arch.feature.md | 5 +
|
||||
5 files changed, 155 insertions(+), 2 deletions(-)
|
||||
create mode 100644 tests/test_podman.py
|
||||
create mode 100644 releng/release-notes-next/oci-platform-sub-arch.feature.md
|
||||
|
||||
diff --git a/py/mock.py b/py/mock.py
|
||||
index 270043611..0ed4b746a 100755
|
||||
--- a/py/mock.py
|
||||
+++ b/py/mock.py
|
||||
@@ -773,8 +773,14 @@ def main():
|
||||
# Enforce host-native repo architecture for bootstrap chroot (unless
|
||||
# bootstrap_forcearch=True, which should never be the case). This
|
||||
# decision affects condPersonality() for DNF calls!
|
||||
+ #
|
||||
+ # Exception: sub-architecture variants (e.g. x86_64_v2) listed in
|
||||
+ # oci_platform_map run natively on the host — keep the target's
|
||||
+ # repo_arch so the bootstrap uses the correct sub-arch repos.
|
||||
host_arch = config_opts["host_arch"]
|
||||
- if config_opts["use_bootstrap_image"]:
|
||||
+ target_arch = config_opts["target_arch"]
|
||||
+ if config_opts["use_bootstrap_image"] \
|
||||
+ and target_arch not in config_opts.get('oci_platform_map', {}):
|
||||
# with bootstrap image, bootstrap is always native
|
||||
bootstrap_buildroot_config['repo_arch'] = config_opts['repo_arch_map'].get(host_arch, host_arch)
|
||||
elif host_arch not in config_opts.get("legal_host_arches", []) \
|
||||
diff --git a/py/mockbuild/config.py b/py/mockbuild/config.py
|
||||
index 34244fa45..cc80cec84 100644
|
||||
--- a/py/mockbuild/config.py
|
||||
+++ b/py/mockbuild/config.py
|
||||
@@ -429,6 +429,13 @@ def setup_default_config_opts():
|
||||
'i686': 'i386',
|
||||
}
|
||||
|
||||
+ # mapping from target_arch to OCI --platform string for podman pull
|
||||
+ config_opts['oci_platform_map'] = {
|
||||
+ 'x86_64_v2': 'linux/amd64/v2',
|
||||
+ 'x86_64_v3': 'linux/amd64/v3',
|
||||
+ 'x86_64_v4': 'linux/amd64/v4',
|
||||
+ }
|
||||
+
|
||||
config_opts["recursion_limit"] = 5000
|
||||
|
||||
config_opts["calculatedeps"] = None
|
||||
diff --git a/py/mockbuild/podman.py b/py/mockbuild/podman.py
|
||||
index 286d5fae3..67369534a 100644
|
||||
--- a/py/mockbuild/podman.py
|
||||
+++ b/py/mockbuild/podman.py
|
||||
@@ -117,7 +117,12 @@ def pull_image(self):
|
||||
""" pull the latest image, return True if successful """
|
||||
logger = getLog()
|
||||
logger.info("Pulling image: %s", self.image)
|
||||
- cmd = [self.podman_binary, "pull", self.image]
|
||||
+ cmd = [self.podman_binary, "pull"]
|
||||
+ target_arch = self.buildroot.config.get('target_arch', '')
|
||||
+ oci_platform = self.buildroot.config.get('oci_platform_map', {}).get(target_arch)
|
||||
+ if oci_platform:
|
||||
+ cmd += ["--platform", oci_platform]
|
||||
+ cmd.append(self.image)
|
||||
|
||||
res = subprocess.run(cmd, env=self.buildroot.env,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||
diff --git a/tests/test_podman.py b/tests/test_podman.py
|
||||
new file mode 100644
|
||||
index 000000000..ec2187cc8
|
||||
--- /dev/null
|
||||
+++ b/tests/test_podman.py
|
||||
@@ -0,0 +1,130 @@
|
||||
+# -*- coding: utf-8 -*-
|
||||
+"""Unit tests for mockbuild.podman — OCI platform pull and architecture check."""
|
||||
+
|
||||
+import subprocess
|
||||
+from unittest.mock import MagicMock, patch
|
||||
+
|
||||
+from mockbuild.podman import (
|
||||
+ Podman,
|
||||
+ podman_check_native_image_architecture,
|
||||
+)
|
||||
+
|
||||
+
|
||||
+# ---------------------------------------------------------------------------
|
||||
+# Helpers
|
||||
+# ---------------------------------------------------------------------------
|
||||
+
|
||||
+def _make_buildroot(target_arch, oci_platform_map=None):
|
||||
+ """Return a minimal mock buildroot with the given config values."""
|
||||
+ config = {"target_arch": target_arch}
|
||||
+ if oci_platform_map is not None:
|
||||
+ config["oci_platform_map"] = oci_platform_map
|
||||
+ br = MagicMock()
|
||||
+ br.config = config
|
||||
+ br.env = {}
|
||||
+ return br
|
||||
+
|
||||
+
|
||||
+def _make_podman(buildroot, image="registry.example.com/image:latest"):
|
||||
+ """Instantiate Podman while bypassing the os.path.exists check."""
|
||||
+ with patch("os.path.exists", return_value=True):
|
||||
+ return Podman(buildroot, image)
|
||||
+
|
||||
+
|
||||
+# ===================================================================
|
||||
+# R002 — pull_image injects --platform when mapping matches
|
||||
+# ===================================================================
|
||||
+
|
||||
+class TestPullImagePlatform:
|
||||
+ """pull_image() must inject --platform from oci_platform_map."""
|
||||
+
|
||||
+ @patch("mockbuild.podman.subprocess.run")
|
||||
+ def test_platform_injected_when_mapped(self, mock_run):
|
||||
+ """--platform linux/amd64/v2 appears when target_arch is x86_64_v2."""
|
||||
+ mock_run.return_value = MagicMock(returncode=0, stdout=b"sha256:abc")
|
||||
+ br = _make_buildroot(
|
||||
+ "x86_64_v2",
|
||||
+ oci_platform_map={"x86_64_v2": "linux/amd64/v2"},
|
||||
+ )
|
||||
+ pod = _make_podman(br)
|
||||
+ assert pod.pull_image() is True
|
||||
+
|
||||
+ cmd = mock_run.call_args[0][0]
|
||||
+ assert "--platform" in cmd
|
||||
+ plat_idx = cmd.index("--platform")
|
||||
+ assert cmd[plat_idx + 1] == "linux/amd64/v2"
|
||||
+ # --platform must precede image name
|
||||
+ assert plat_idx < cmd.index("registry.example.com/image:latest")
|
||||
+
|
||||
+ @patch("mockbuild.podman.subprocess.run")
|
||||
+ def test_no_platform_when_arch_not_in_map(self, mock_run):
|
||||
+ """--platform absent when target_arch has no mapping entry."""
|
||||
+ mock_run.return_value = MagicMock(returncode=0, stdout=b"sha256:abc")
|
||||
+ br = _make_buildroot(
|
||||
+ "x86_64",
|
||||
+ oci_platform_map={"x86_64_v2": "linux/amd64/v2"},
|
||||
+ )
|
||||
+ pod = _make_podman(br)
|
||||
+ assert pod.pull_image() is True
|
||||
+
|
||||
+ cmd = mock_run.call_args[0][0]
|
||||
+ assert "--platform" not in cmd
|
||||
+
|
||||
+ @patch("mockbuild.podman.subprocess.run")
|
||||
+ def test_no_platform_when_map_missing(self, mock_run):
|
||||
+ """--platform absent when oci_platform_map key is missing entirely."""
|
||||
+ mock_run.return_value = MagicMock(returncode=0, stdout=b"sha256:def")
|
||||
+ br = _make_buildroot("x86_64") # no oci_platform_map
|
||||
+ pod = _make_podman(br)
|
||||
+ assert pod.pull_image() is True
|
||||
+
|
||||
+ cmd = mock_run.call_args[0][0]
|
||||
+ assert "--platform" not in cmd
|
||||
+
|
||||
+ @patch("mockbuild.podman.subprocess.run")
|
||||
+ def test_empty_target_arch(self, mock_run):
|
||||
+ """Empty target_arch string never produces --platform."""
|
||||
+ mock_run.return_value = MagicMock(returncode=0, stdout=b"sha256:ghi")
|
||||
+ br = _make_buildroot(
|
||||
+ "",
|
||||
+ oci_platform_map={"x86_64_v2": "linux/amd64/v2"},
|
||||
+ )
|
||||
+ pod = _make_podman(br)
|
||||
+ assert pod.pull_image() is True
|
||||
+
|
||||
+ cmd = mock_run.call_args[0][0]
|
||||
+ assert "--platform" not in cmd
|
||||
+
|
||||
+
|
||||
+# ===================================================================
|
||||
+# R003 — podman_check_native_image_architecture
|
||||
+# ===================================================================
|
||||
+
|
||||
+class TestArchCheck:
|
||||
+ """Architecture check compares system vs image os/arch."""
|
||||
+
|
||||
+ @patch("mockbuild.podman.subprocess.check_output")
|
||||
+ def test_matching_arch_returns_true(self, mock_co):
|
||||
+ """Matching system and image arch returns True."""
|
||||
+ mock_co.side_effect = ["linux/amd64", "linux/amd64"]
|
||||
+ assert podman_check_native_image_architecture("img:latest") is True
|
||||
+
|
||||
+ @patch("mockbuild.podman.subprocess.check_output")
|
||||
+ def test_mismatched_arch_returns_false(self, mock_co):
|
||||
+ """Mismatched system and image arch returns False."""
|
||||
+ mock_co.side_effect = ["linux/amd64", "linux/arm64"]
|
||||
+ assert podman_check_native_image_architecture("img:latest") is False
|
||||
+
|
||||
+
|
||||
+# ===================================================================
|
||||
+# Negative / boundary tests
|
||||
+# ===================================================================
|
||||
+
|
||||
+class TestArchCheckErrorPaths:
|
||||
+ """Error handling in architecture check."""
|
||||
+
|
||||
+ @patch("mockbuild.podman.subprocess.check_output")
|
||||
+ def test_subprocess_error_returns_false(self, mock_co):
|
||||
+ """SubprocessError during check returns False (existing behavior)."""
|
||||
+ mock_co.side_effect = subprocess.SubprocessError("fail")
|
||||
+ assert podman_check_native_image_architecture("img:latest") is False
|
||||
diff --git a/releng/release-notes-next/oci-platform-sub-arch.feature.md b/releng/release-notes-next/oci-platform-sub-arch.feature.md
|
||||
new file mode 100644
|
||||
index 000000000..0dc976c3a
|
||||
--- /dev/null
|
||||
+++ b/releng/release-notes-next/oci-platform-sub-arch.feature.md
|
||||
@@ -0,0 +1,5 @@
|
||||
+Podman container image pulls now pass `--platform` for x86_64
|
||||
+sub-architecture variants (x86_64_v2, x86_64_v3, x86_64_v4). A new
|
||||
+`oci_platform_map` config option maps target architectures to OCI platform
|
||||
+strings (e.g. `linux/amd64/v2`), and the architecture check accepts
|
||||
+variant images on a matching base host.
|
||||
|
||||
@ -0,0 +1,267 @@
|
||||
From 38842ed9678d974309d879907d1f05fe211e539b Mon Sep 17 00:00:00 2001
|
||||
From: Andrew Lukoshko <andrew.lukoshko@gmail.com>
|
||||
Date: Wed, 8 Apr 2026 01:49:00 +0200
|
||||
Subject: [PATCH] yum_cache: add cache_key option for shared package cache
|
||||
across configs
|
||||
|
||||
When cache_key is set in yum_cache_opts, the plugin redirects the
|
||||
dnf/yum cache bind mounts and lock file to a shared directory at
|
||||
<cache_topdir>/yum_cache/<cache_key>/ instead of the per-config
|
||||
buildroot.cachedir. This allows multiple mock configs with different
|
||||
root names but the same cache_key to share cached RPM packages and
|
||||
repo metadata, avoiding redundant downloads.
|
||||
|
||||
Without cache_key the behavior is identical to the original plugin.
|
||||
|
||||
Usage in mock config:
|
||||
config_opts['plugin_conf']['yum_cache_opts']['cache_key'] = 'almalinux-kitten-10-x86_64'
|
||||
|
||||
The shared cache directory is not removed by --scrub=all (which only
|
||||
cleans per-root caches), matching the design intent that shared caches
|
||||
are host-level resources.
|
||||
---
|
||||
py/mockbuild/plugins/yum_cache.py | 25 +++-
|
||||
tests/plugins/test_yum_cache.py | 181 +++++++++++++++++++++++++
|
||||
2 files changed, 200 insertions(+), 6 deletions(-)
|
||||
create mode 100644 tests/plugins/test_yum_cache.py
|
||||
|
||||
diff --git a/py/mockbuild/plugins/yum_cache.py b/py/mockbuild/plugins/yum_cache.py
|
||||
index c732b439f..1e8aec494 100644
|
||||
--- a/py/mockbuild/plugins/yum_cache.py
|
||||
+++ b/py/mockbuild/plugins/yum_cache.py
|
||||
@@ -26,11 +26,12 @@ def init(plugins, conf, buildroot):
|
||||
|
||||
|
||||
class CacheDir:
|
||||
- def __init__(self, buildroot, pkg_manager):
|
||||
+ def __init__(self, buildroot, pkg_manager, cache_dir=None):
|
||||
self.buildroot = buildroot
|
||||
self.cache_path = os.path.join('/var/cache', pkg_manager)
|
||||
- self.host_cache_path = os.path.join(self.buildroot.cachedir,
|
||||
- pkg_manager + '_cache')
|
||||
+ self.host_cache_path = os.path.join(
|
||||
+ cache_dir if cache_dir else self.buildroot.cachedir,
|
||||
+ pkg_manager + '_cache')
|
||||
self.mount_path = self.buildroot.make_chroot_path(self.cache_path)
|
||||
self.buildroot.mounts.add(BindMountPoint(
|
||||
srcpath=self.host_cache_path,
|
||||
@@ -56,9 +57,21 @@ def __init__(self, plugins, conf, buildroot):
|
||||
self.config = buildroot.config
|
||||
self.state = buildroot.state
|
||||
self.yum_cache_opts = conf
|
||||
+
|
||||
+ cache_key = conf.get('cache_key')
|
||||
+ if cache_key:
|
||||
+ cache_topdir = self.config['cache_topdir']
|
||||
+ cache_dir = os.path.join(cache_topdir, 'yum_cache', cache_key)
|
||||
+ mockbuild.file_util.mkdirIfAbsent(cache_dir)
|
||||
+ lock_dir = cache_dir
|
||||
+ getLog().info("enabled package manager cache (shared, key=%s)", cache_key)
|
||||
+ else:
|
||||
+ cache_dir = None
|
||||
+ lock_dir = buildroot.cachedir
|
||||
+
|
||||
self.cache_dirs = [
|
||||
- CacheDir(buildroot, 'yum'),
|
||||
- CacheDir(buildroot, 'dnf'),
|
||||
+ CacheDir(buildroot, 'yum', cache_dir),
|
||||
+ CacheDir(buildroot, 'dnf', cache_dir),
|
||||
]
|
||||
self.yumSharedCachePath = self.cache_dirs[0].host_cache_path
|
||||
self.online = self.config['online']
|
||||
@@ -66,7 +79,7 @@ def __init__(self, plugins, conf, buildroot):
|
||||
plugins.add_hook("postyum", self._yumCachePostYumHook)
|
||||
plugins.add_hook("preinit", self._yumCachePreInitHook)
|
||||
|
||||
- self.yumCacheLock = open(os.path.join(buildroot.cachedir, "yumcache.lock"), "a+")
|
||||
+ self.yumCacheLock = open(os.path.join(lock_dir, "yumcache.lock"), "a+")
|
||||
|
||||
|
||||
# =============
|
||||
diff --git a/tests/plugins/test_yum_cache.py b/tests/plugins/test_yum_cache.py
|
||||
new file mode 100644
|
||||
index 000000000..a6aba45d0
|
||||
--- /dev/null
|
||||
+++ b/tests/plugins/test_yum_cache.py
|
||||
@@ -0,0 +1,181 @@
|
||||
+"""Unit tests for the yum_cache plugin cache_key shared-cache feature."""
|
||||
+
|
||||
+from copy import deepcopy
|
||||
+from unittest import mock
|
||||
+from unittest.mock import MagicMock, patch
|
||||
+
|
||||
+from mockbuild.plugins import yum_cache
|
||||
+
|
||||
+
|
||||
+# ---------------------------------------------------------------------------
|
||||
+# Helpers
|
||||
+# ---------------------------------------------------------------------------
|
||||
+
|
||||
+DEFAULT_CONF = {
|
||||
+ 'max_age_days': 30,
|
||||
+ 'max_metadata_age_days': 0.5,
|
||||
+}
|
||||
+
|
||||
+
|
||||
+def make_buildroot(extra_config=None):
|
||||
+ br = MagicMock()
|
||||
+ br.config = {
|
||||
+ 'cache_topdir': '/var/cache/mock',
|
||||
+ 'online': False,
|
||||
+ 'target_arch': 'x86_64',
|
||||
+ }
|
||||
+ if extra_config:
|
||||
+ br.config.update(extra_config)
|
||||
+ br.make_chroot_path.side_effect = lambda p: '/chroot' + p
|
||||
+ return br
|
||||
+
|
||||
+
|
||||
+# ---------------------------------------------------------------------------
|
||||
+# init() entry point
|
||||
+# ---------------------------------------------------------------------------
|
||||
+
|
||||
+@patch('mockbuild.plugins.yum_cache.YumCache')
|
||||
+def test_init(MockYumCache):
|
||||
+ plugins, conf, buildroot = object(), object(), object()
|
||||
+ yum_cache.init(plugins, conf, buildroot)
|
||||
+ MockYumCache.assert_called_once_with(plugins, conf, buildroot)
|
||||
+
|
||||
+
|
||||
+# ---------------------------------------------------------------------------
|
||||
+# Without cache_key — legacy behaviour unchanged
|
||||
+# ---------------------------------------------------------------------------
|
||||
+
|
||||
+@patch('mockbuild.plugins.yum_cache.mockbuild.file_util')
|
||||
+def test_no_cache_key_uses_buildroot_cachedir(mock_file_util):
|
||||
+ """Without cache_key, host_cache_path must be buildroot.cachedir/dnf_cache."""
|
||||
+ br = make_buildroot()
|
||||
+ br.cachedir = '/var/cache/mock/my-root'
|
||||
+ conf = deepcopy(DEFAULT_CONF)
|
||||
+
|
||||
+ with patch('builtins.open', mock.mock_open()):
|
||||
+ plugin = yum_cache.YumCache(MagicMock(), conf, br)
|
||||
+
|
||||
+ dnf_dir = next(d for d in plugin.cache_dirs if d.host_cache_path.endswith('dnf_cache'))
|
||||
+ assert dnf_dir.host_cache_path == '/var/cache/mock/my-root/dnf_cache'
|
||||
+
|
||||
+
|
||||
+@patch('mockbuild.plugins.yum_cache.mockbuild.file_util')
|
||||
+def test_no_cache_key_lock_in_buildroot_cachedir(mock_file_util):
|
||||
+ """Without cache_key, lock file must be in buildroot.cachedir."""
|
||||
+ br = make_buildroot()
|
||||
+ br.cachedir = '/var/cache/mock/my-root'
|
||||
+ conf = deepcopy(DEFAULT_CONF)
|
||||
+
|
||||
+ mock_open = mock.mock_open()
|
||||
+ with patch('builtins.open', mock_open):
|
||||
+ yum_cache.YumCache(MagicMock(), conf, br)
|
||||
+
|
||||
+ opened_paths = [c.args[0] for c in mock_open.call_args_list]
|
||||
+ assert any('my-root/yumcache.lock' in p for p in opened_paths), \
|
||||
+ f"lock not in cachedir; opened: {opened_paths}"
|
||||
+
|
||||
+
|
||||
+# ---------------------------------------------------------------------------
|
||||
+# With cache_key — shared cache behaviour
|
||||
+# ---------------------------------------------------------------------------
|
||||
+
|
||||
+@patch('mockbuild.plugins.yum_cache.mockbuild.file_util')
|
||||
+def test_cache_key_uses_shared_dir(mock_file_util):
|
||||
+ """With cache_key, host_cache_path must be cache_topdir/yum_cache/{key}/dnf_cache."""
|
||||
+ br = make_buildroot({'cache_topdir': '/var/cache/mock'})
|
||||
+ conf = {**DEFAULT_CONF, 'cache_key': 'centos-stream-10-x86_64'}
|
||||
+
|
||||
+ with patch('builtins.open', mock.mock_open()):
|
||||
+ plugin = yum_cache.YumCache(MagicMock(), conf, br)
|
||||
+
|
||||
+ dnf_dir = next(d for d in plugin.cache_dirs if d.host_cache_path.endswith('dnf_cache'))
|
||||
+ assert dnf_dir.host_cache_path == \
|
||||
+ '/var/cache/mock/yum_cache/centos-stream-10-x86_64/dnf_cache'
|
||||
+
|
||||
+
|
||||
+@patch('mockbuild.plugins.yum_cache.mockbuild.file_util')
|
||||
+def test_cache_key_lock_in_shared_dir(mock_file_util):
|
||||
+ """With cache_key, lock file must be in the shared cache dir, not buildroot.cachedir."""
|
||||
+ br = make_buildroot({'cache_topdir': '/var/cache/mock'})
|
||||
+ br.cachedir = '/var/cache/mock/my-root'
|
||||
+ conf = {**DEFAULT_CONF, 'cache_key': 'centos-stream-10-x86_64'}
|
||||
+
|
||||
+ mock_open = mock.mock_open()
|
||||
+ with patch('builtins.open', mock_open):
|
||||
+ yum_cache.YumCache(MagicMock(), conf, br)
|
||||
+
|
||||
+ opened_paths = [c.args[0] for c in mock_open.call_args_list]
|
||||
+ assert any('yum_cache/centos-stream-10-x86_64/yumcache.lock' in p
|
||||
+ for p in opened_paths), f"shared lock not found; opened: {opened_paths}"
|
||||
+ assert not any('my-root/yumcache.lock' in p
|
||||
+ for p in opened_paths), "lock must not be in buildroot.cachedir"
|
||||
+
|
||||
+
|
||||
+@patch('mockbuild.plugins.yum_cache.mockbuild.file_util')
|
||||
+def test_cache_key_shared_dir_created(mock_file_util):
|
||||
+ """With cache_key, the shared directory must be created via mkdirIfAbsent."""
|
||||
+ br = make_buildroot({'cache_topdir': '/var/cache/mock'})
|
||||
+ conf = {**DEFAULT_CONF, 'cache_key': 'centos-stream-10-x86_64'}
|
||||
+
|
||||
+ with patch('builtins.open', mock.mock_open()):
|
||||
+ yum_cache.YumCache(MagicMock(), conf, br)
|
||||
+
|
||||
+ created_dirs = [c.args[0] for c in mock_file_util.mkdirIfAbsent.call_args_list]
|
||||
+ assert any('yum_cache/centos-stream-10-x86_64' in d for d in created_dirs), \
|
||||
+ f"shared dir not created; mkdirIfAbsent called with: {created_dirs}"
|
||||
+
|
||||
+
|
||||
+@patch('mockbuild.plugins.yum_cache.mockbuild.file_util')
|
||||
+def test_both_dnf_and_yum_cache_dirs_registered(mock_file_util):
|
||||
+ """Both /var/cache/dnf and /var/cache/yum bind mounts must be added."""
|
||||
+ br = make_buildroot()
|
||||
+ conf = {**DEFAULT_CONF, 'cache_key': 'centos-stream-10-x86_64'}
|
||||
+
|
||||
+ with patch('builtins.open', mock.mock_open()):
|
||||
+ yum_cache.YumCache(MagicMock(), conf, br)
|
||||
+
|
||||
+ added_srcs = [c.args[0].srcpath for c in br.mounts.add.call_args_list]
|
||||
+ assert any('dnf_cache' in p for p in added_srcs), f"dnf_cache not mounted: {added_srcs}"
|
||||
+ assert any('yum_cache' in p for p in added_srcs), f"yum_cache not mounted: {added_srcs}"
|
||||
+
|
||||
+
|
||||
+# ---------------------------------------------------------------------------
|
||||
+# Hook registration (both modes)
|
||||
+# ---------------------------------------------------------------------------
|
||||
+
|
||||
+@patch('mockbuild.plugins.yum_cache.mockbuild.file_util')
|
||||
+def test_hooks_registered(mock_file_util):
|
||||
+ """Plugin must always register preyum, postyum, preinit hooks."""
|
||||
+ for conf in [deepcopy(DEFAULT_CONF), {**DEFAULT_CONF, 'cache_key': 'my-key'}]:
|
||||
+ plugins = MagicMock()
|
||||
+ br = make_buildroot()
|
||||
+ br.cachedir = '/var/cache/mock/root'
|
||||
+ with patch('builtins.open', mock.mock_open()):
|
||||
+ yum_cache.YumCache(plugins, conf, br)
|
||||
+ hook_names = [c.args[0] for c in plugins.add_hook.call_args_list]
|
||||
+ assert 'preyum' in hook_names
|
||||
+ assert 'postyum' in hook_names
|
||||
+ assert 'preinit' in hook_names
|
||||
+
|
||||
+
|
||||
+# ---------------------------------------------------------------------------
|
||||
+# Two configs, same cache_key — both resolve to same path
|
||||
+# ---------------------------------------------------------------------------
|
||||
+
|
||||
+@patch('mockbuild.plugins.yum_cache.mockbuild.file_util')
|
||||
+def test_two_roots_same_cache_key_share_path(mock_file_util):
|
||||
+ """Two buildroots with different cachedir but same cache_key must resolve identical paths."""
|
||||
+ conf = {**DEFAULT_CONF, 'cache_key': 'centos-stream-10-x86_64'}
|
||||
+
|
||||
+ br1 = make_buildroot({'cache_topdir': '/var/cache/mock'})
|
||||
+ br1.cachedir = '/var/cache/mock/build-root-1'
|
||||
+ br2 = make_buildroot({'cache_topdir': '/var/cache/mock'})
|
||||
+ br2.cachedir = '/var/cache/mock/build-root-2'
|
||||
+
|
||||
+ with patch('builtins.open', mock.mock_open()):
|
||||
+ p1 = yum_cache.YumCache(MagicMock(), deepcopy(conf), br1)
|
||||
+ p2 = yum_cache.YumCache(MagicMock(), deepcopy(conf), br2)
|
||||
+
|
||||
+ dnf1 = next(d.host_cache_path for d in p1.cache_dirs if d.host_cache_path.endswith('dnf_cache'))
|
||||
+ dnf2 = next(d.host_cache_path for d in p2.cache_dirs if d.host_cache_path.endswith('dnf_cache'))
|
||||
+ assert dnf1 == dnf2, f"paths differ: {dnf1} != {dnf2}"
|
||||
@ -19,7 +19,7 @@
|
||||
Summary: Builds packages inside chroots
|
||||
Name: mock
|
||||
Version: 6.7
|
||||
Release: 1%{?dist}
|
||||
Release: 1%{?dist}.alma.1
|
||||
License: GPL-2.0-or-later
|
||||
# Source is created by
|
||||
# git clone https://github.com/rpm-software-management/mock.git
|
||||
@ -27,6 +27,10 @@ License: GPL-2.0-or-later
|
||||
# git reset --hard %%{name}-%%{version}
|
||||
# tito build --tgz
|
||||
Source: https://github.com/rpm-software-management/%{name}/releases/download/%{name}-%{version}-1/%{name}-%{version}.tar.gz
|
||||
|
||||
# AlmaLinux Patch
|
||||
Patch: 1000-add-oci-platform-support-for-x86_64-sub-arch.patch
|
||||
Patch: 1001-yum-cache-add-cache-key-for-shared-package-cache.patch
|
||||
URL: https://github.com/rpm-software-management/mock/
|
||||
BuildArch: noarch
|
||||
Requires: tar
|
||||
@ -169,7 +173,7 @@ Requires(pre): shadow-utils
|
||||
Filesystem layout and group for Mock.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
%autosetup -p1
|
||||
for file in py/mock.py py/mock-parse-buildlog.py; do
|
||||
sed -i 1"s|#!/usr/bin/python3 |#!%{__python} |" $file
|
||||
done
|
||||
@ -330,6 +334,11 @@ pylint-3 py/mockbuild/ py/*.py py/mockbuild/plugins/* || :
|
||||
|
||||
|
||||
%changelog
|
||||
* Wed Apr 08 2026 Andrew Lukoshko <alukoshko@almalinux.org> - 6.7-1.alma.1
|
||||
- Add OCI platform support for x86_64 sub-architecture containers
|
||||
- Add cache_key option to yum_cache plugin for shared package cache across
|
||||
configs
|
||||
|
||||
* Tue Mar 03 2026 Pavel Raiskup <pavel@raiskup.cz> 6.7-1
|
||||
- mock: Use umask 0022 instead of 0002 to avoid strange permissions (ngompa@velocitylimitless.com)
|
||||
- expand_spec plugin: generating expanded-spec.txt in postdeps hook (yzhu@redhat.com)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user