Skip to content

Commit

Permalink
podman: fallback to normal bootstrap
Browse files Browse the repository at this point in the history
When Podman isn't installed, or the image can not be downloaded, or the
image isn't compatible with host arch - Mock newly falls-back to a
normal 'dnf --installroot' when bootstrap_image_fallback=True (default).

Closes: #1200
  • Loading branch information
praiskup committed Aug 30, 2023
1 parent a2c94f5 commit 79c36e8
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 15 deletions.
7 changes: 7 additions & 0 deletions mock/docs/site-defaults.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,13 @@
# options invalidate the effect of this option.
#config_opts['bootstrap_image_ready'] = False

# If 'use_bootstrap_image' is True, Mock is instructed download the configured
# container image from image registry. This option controls the behavior when
# the image can not be downloaded. When set to False, Mock fails hard. When
# set to True, Mock falls-back to normal bootstrap chroot installation using
# package manager (e.g. using dnf --installroot).
#config_opts['bootstrap_image_fallback'] = True

# anything you specify with 'bootstrap_*' will be copied to bootstrap config
# e.g. config_opts['bootstrap_system_yum_command'] = '/usr/bin/yum-deprecated' will become
# config_opts['system_yum_command'] = '/usr/bin/yum-deprecated' for bootstrap config
Expand Down
44 changes: 36 additions & 8 deletions mock/py/mockbuild/buildroot.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# vim: noai:ts=4:sw=4:expandtab

from contextlib import contextmanager
import errno
import fcntl
import glob
Expand All @@ -19,7 +20,7 @@
from . import uid
from . import util
from .exception import (BuildRootLocked, Error, ResultDirNotAccessible,
BadCmdline)
BadCmdline, BootstrapError)
from .package_manager import package_manager
from .trace_decorator import getLog, traceLog
from .podman import Podman
Expand Down Expand Up @@ -211,6 +212,39 @@ def _init_locked(self):
# Detect what package manager to use.
self.set_package_manager()

@traceLog()
def _load_from_container_image(self):
if not self.uses_bootstrap_image or self.chroot_was_initialized:
return

class _FallbackException(Exception):
pass

@contextmanager
def _fallback(message):
try:
yield
except BootstrapError as exc:
if not self.config["image_fallback"]:
raise
raise _FallbackException(
f"{message}, falling back to bootstrap installation: {exc}"
) from exc

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

with _fallback("Can't initialize from bootstrap image"):
podman.pull_image()
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


@traceLog()
def _init(self, prebuild):

Expand All @@ -229,13 +263,7 @@ def _init(self, prebuild):
self.plugins.call_hooks('preinit')
# intentionally we do not call bootstrap hook here - it does not have sense
self.chroot_was_initialized = self.chroot_is_initialized()
if self.uses_bootstrap_image and not self.chroot_was_initialized:
podman = Podman(self, self.bootstrap_image)
podman.pull_image()
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"))

self._load_from_container_image()
self._setup_dirs()

# /dev is later overwritten by systemd-nspawn, but we need this for
Expand Down
1 change: 1 addition & 0 deletions mock/py/mockbuild/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ def setup_default_config_opts():
config_opts['use_bootstrap_image'] = True
config_opts['bootstrap_image'] = 'fedora:latest'
config_opts['bootstrap_image_ready'] = False
config_opts['bootstrap_image_fallback'] = True

config_opts['internal_dev_setup'] = True

Expand Down
20 changes: 13 additions & 7 deletions mock/py/mockbuild/podman.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# vim: noai:ts=4:sw=4:expandtab

import os
import logging
import subprocess
from contextlib import contextmanager
Expand All @@ -10,7 +11,7 @@
from mockbuild.exception import BootstrapError


def podman_check_native_image_architecture(image, logger=None):
def podman_check_native_image_architecture(image, logger=None, podman_binary=None):
"""
Return True if image's architecture is "native" for this host.
Relates:
Expand All @@ -19,10 +20,11 @@ def podman_check_native_image_architecture(image, logger=None):
"""

logger = logger or logging.getLogger()
podman = podman_binary or "/usr/bin/podman"
logger.info("Checking that %s image matches host's architecture", image)
sys_check_cmd = ["podman", "version", "--format", "{{.OsArch}}"]
image_check_cmd = ["podman", "image", "inspect",
"--format", "{{.Os}}/{{.Architecture}}", image]
sys_check_cmd = [podman, "version", "--format", "{{.OsArch}}"]
image_check_cmd = [podman, "image", "inspect",
"--format", "{{.Os}}/{{.Architecture}}", image]

def _podman_query(cmd):
return subprocess.check_output(cmd, encoding="utf8").strip()
Expand All @@ -46,6 +48,10 @@ class Podman:

@traceLog()
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")

self.buildroot = buildroot
self.image = image
self.container_id = None
Expand All @@ -56,7 +62,7 @@ def pull_image(self):
""" pull the latest image """
logger = getLog()
logger.info("Pulling image: %s", self.image)
cmd = ["podman", "pull", self.image]
cmd = [self.podman_binary, "pull", self.image]
out, exit_status = util.do_with_status(cmd, env=self.buildroot.env,
raiseExc=False, returnOutput=1)
if exit_status:
Expand All @@ -74,8 +80,8 @@ def mounted_image(self):
bootstrap chroot directory.
"""
logger = getLog()
cmd_mount = ["podman", "image", "mount", self.image]
cmd_umount = ["podman", "image", "umount", self.image]
cmd_mount = [self.podman_binary, "image", "mount", self.image]
cmd_umount = [self.podman_binary, "image", "umount", self.image]
result = subprocess.run(cmd_mount, capture_output=True, check=False, encoding="utf8")
if result.returncode:
message = "Podman mount failed: " + result.stderr
Expand Down

0 comments on commit 79c36e8

Please sign in to comment.