Skip to content

Commit

Permalink
podman: de-bootstrap the logic
Browse files Browse the repository at this point in the history
We plan to use the podman.py "library" for buildroot (not just
bootstrap) logic, too.  Let's make it chroot agnostic.

Relates: rpm-software-management#1482
  • Loading branch information
praiskup committed Dec 12, 2024
1 parent cb90110 commit 5a2c2c2
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 29 deletions.
32 changes: 18 additions & 14 deletions mock/py/mockbuild/buildroot.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
from . import uid
from . import util
from .exception import (BuildRootLocked, Error, ResultDirNotAccessible,
BadCmdline, BootstrapError)
BadCmdline, BootstrapError, RootError)
from .package_manager import package_manager
from .trace_decorator import getLog, traceLog
from .podman import Podman
from .podman import Podman, PodmanError
from .shadow_utils import ShadowUtils


Expand Down Expand Up @@ -110,8 +110,8 @@ def __init__(self, config, uid_manager, state, plugins, bootstrap_buildroot=None
self.env.update(proxy_env)
os.environ.update(proxy_env)

self.use_bootstrap_image = self.config['use_bootstrap_image']
self.bootstrap_image = self.config['bootstrap_image']
self.use_chroot_image = self.config['use_bootstrap_image']
self.chroot_image = self.config['bootstrap_image']

self.pkg_manager = None
self.mounts = mounts.Mounts(self)
Expand Down Expand Up @@ -246,7 +246,7 @@ def _load_from_container_image(self):
getLog().info(
"It seems that you run Mock in a Docker container. Mock "
"though uses container tooling itself (namely Podman) for "
"downloading bootstrap image. This might require you to "
"downloading container image. This might require you to "
"run Mock in 'docker run --privileged'.")

class _FallbackException(Exception):
Expand All @@ -256,25 +256,26 @@ class _FallbackException(Exception):
def _fallback(message):
try:
yield
except BootstrapError as exc:
except PodmanError as exc:
if not self.config["image_fallback"]:
raise
err = BootstrapError if self.is_bootstrap else RootError
raise err from exc
raise _FallbackException(
f"{message}, falling back to bootstrap installation: {exc}"
f"{message}, falling back to chroot installation: {exc}"
) from exc

try:
with _fallback("Can't work with Podman"):
podman = Podman(self, self.bootstrap_image)
podman = Podman(self, self.chroot_image)

with _fallback("Can't initialize from bootstrap image"):
with _fallback("Can't initialize from container image"):
if not self.config["image_skip_pull"]:
podman.retry_image_pull(self.config["image_keep_getting"])
else:
getLog().info("Using local image %s (pull skipped)",
self.bootstrap_image)
self.chroot_image)

if self.config["hermetic_build"]:
if self.is_bootstrap and self.config["hermetic_build"]:
tarball = os.path.join(self.config["offline_local_repository"],
"bootstrap.tar")
podman.import_tarball(tarball)
Expand All @@ -289,12 +290,15 @@ def _fallback(message):
f"Expected digest for image {podman.image} is"
f"{digest_expected}, but {digest} found.")

if self.is_bootstrap and not podman.check_native_image_architecture():
raise BootstrapError("Container image architecture check failed")

podman.cp(self.make_chroot_path(), self.config["tar_binary"])
file_util.unlink_if_exists(os.path.join(self.make_chroot_path(),
"etc/rpm/macros.image-language-conf"))
except _FallbackException as exc:
getLog().warning("%s", exc)
self.use_bootstrap_image = False
self.use_chroot_image = False


@traceLog()
Expand Down Expand Up @@ -1063,7 +1067,7 @@ def delete(self):

@property
def uses_bootstrap_image(self):
return self.is_bootstrap and self.use_bootstrap_image
return self.is_bootstrap and self.use_chroot_image

@property
def bootstrap_image_is_ready(self):
Expand Down
2 changes: 1 addition & 1 deletion mock/py/mockbuild/package_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ def _do(comment, *args, **kwargs):

# We want to know the package versions in bootstrap
if self.bootstrap_buildroot:
if self.bootstrap_buildroot.use_bootstrap_image:
if self.bootstrap_buildroot.use_chroot_image:
# rpm installed from the bootstrap image
_do("downloaded with a bootstrap image", cmd,
chrootPath=self.bootstrap_buildroot.make_chroot_path())
Expand Down
36 changes: 22 additions & 14 deletions mock/py/mockbuild/podman.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@
import backoff
from mockbuild.trace_decorator import getLog, traceLog
from mockbuild import util
from mockbuild.exception import BootstrapError


class PodmanError(Exception):
"""
Exception raised by mockbuild.podman.Podman
"""


def podman_check_native_image_architecture(image, logger=None, podman_binary=None):
Expand Down Expand Up @@ -51,12 +56,12 @@ class Podman:
def __init__(self, buildroot, image):
self.podman_binary = "/usr/bin/podman"
if not os.path.exists(self.podman_binary):
raise BootstrapError(f"'{self.podman_binary}' not installed")
raise PodmanError(f"'{self.podman_binary}' not installed")

self.buildroot = buildroot
self.image = image
self.container_id = None
getLog().info("Using bootstrap image: %s", image)
getLog().info("Using container image: %s", image)

@traceLog()
def pull_image(self):
Expand All @@ -74,7 +79,7 @@ def import_tarball(self, tarball):
"""
Import tarball using podman into the local database.
"""
getLog().info("Loading bootstrap image from %s", tarball)
getLog().info("Loading container image from %s", tarball)
cmd = [self.podman_binary, "load", "-i", tarball]
util.do_with_status(cmd, env=self.buildroot.env)

Expand All @@ -91,7 +96,7 @@ def mounted_image(self):
"""
Using the "podman image mount" command, mount the image as a temporary
read-only directory so we can copy-paste the contents into the final
bootstrap chroot directory.
chroot directory.
"""
logger = getLog()
cmd_mount = [self.podman_binary, "image", "mount", self.image]
Expand All @@ -101,7 +106,7 @@ def mounted_image(self):
encoding="utf8")
if result.returncode:
message = "Podman mount failed: " + result.stderr
raise BootstrapError(message)
raise PodmanError(message)

mountpoint = result.stdout.strip()
logger.info("mounting %s with podman image mount", self.image)
Expand All @@ -124,27 +129,30 @@ def get_image_digest(self):
stderr=subprocess.PIPE, check=False,
encoding="utf8")
if result.returncode:
raise BootstrapError(f"Can't get {self.image} podman image digest: {result.stderr}")
raise PodmanError(f"Can't get {self.image} podman image digest: {result.stderr}")
result = result.stdout.strip()
if len(result.splitlines()) != 1:
raise BootstrapError(f"The digest of {self.image} image is not a single-line string")
raise PodmanError(f"The digest of {self.image} image is not a single-line string")
return result

def check_native_image_architecture(self):
"""
Check that self.image has been generated for the current
host's architecture.
"""
return podman_check_native_image_architecture(self.image, getLog())

@traceLog()
def cp(self, destination, tar_cmd):
""" copy content of container to destination directory """
logger = getLog()
logger.info("Copy content of container %s to %s", self.image, destination)

if not podman_check_native_image_architecture(self.image, logger):
raise BootstrapError("Container image architecture check failed")
getLog().info("Copy content of container %s to %s", self.image, destination)

with self.mounted_image() as mount_path:
# pipe-out the temporary mountpoint with the help of tar utility
cmd_podman = [tar_cmd, "-C", mount_path, "-c", "."]
with subprocess.Popen(cmd_podman, stdout=subprocess.PIPE) as podman:
# read the tarball from stdin, and extract to the destination
# directory (bootstrap chroot directory)
# directory (chroot directory)
cmd_tar = [tar_cmd, "-xC", destination, "-f", "-"]
with subprocess.Popen(cmd_tar, stdin=podman.stdout) as tar:
tar.communicate()
Expand Down

0 comments on commit 5a2c2c2

Please sign in to comment.