diff --git a/mock/docs/site-defaults.cfg b/mock/docs/site-defaults.cfg index f60456079..c1aee963a 100644 --- a/mock/docs/site-defaults.cfg +++ b/mock/docs/site-defaults.cfg @@ -165,6 +165,12 @@ # package manager (e.g. using dnf --installroot). #config_opts['bootstrap_image_fallback'] = True +# When 'use_bootstrap_image' is True, bootstrap image must be downloaded. +# Attempt to download the image may fail, and Mock will retry. Here you can +# configure how long should Mock keep re-trying (using exponential full jitter, +# see python-backoff docs for more info). +#config_opts['bootstrap_image_keep_getting'] = 120 # seconds + # 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 diff --git a/mock/py/mockbuild/buildroot.py b/mock/py/mockbuild/buildroot.py index cd5737ddb..81545ddcd 100644 --- a/mock/py/mockbuild/buildroot.py +++ b/mock/py/mockbuild/buildroot.py @@ -231,7 +231,7 @@ def _fallback(message): podman = Podman(self, self.bootstrap_image) with _fallback("Can't initialize from bootstrap image"): - podman.pull_image() + podman.retry_image_pull(self.config["image_keep_getting"]) 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")) diff --git a/mock/py/mockbuild/config.py b/mock/py/mockbuild/config.py index f4648d065..fd3ce62dc 100644 --- a/mock/py/mockbuild/config.py +++ b/mock/py/mockbuild/config.py @@ -86,6 +86,7 @@ def setup_default_config_opts(): config_opts['bootstrap_image'] = 'fedora:latest' config_opts['bootstrap_image_ready'] = False config_opts['bootstrap_image_fallback'] = True + config_opts['bootstrap_image_keep_getting'] = 120 config_opts['internal_dev_setup'] = True diff --git a/mock/py/mockbuild/podman.py b/mock/py/mockbuild/podman.py index 6ce541bf6..3a1a1a3e3 100644 --- a/mock/py/mockbuild/podman.py +++ b/mock/py/mockbuild/podman.py @@ -6,6 +6,7 @@ import subprocess from contextlib import contextmanager +import backoff from mockbuild.trace_decorator import getLog, traceLog from mockbuild import util from mockbuild.exception import BootstrapError @@ -59,7 +60,7 @@ def __init__(self, buildroot, image): @traceLog() def pull_image(self): - """ pull the latest image """ + """ pull the latest image, return True if successful """ logger = getLog() logger.info("Pulling image: %s", self.image) cmd = [self.podman_binary, "pull", self.image] @@ -67,10 +68,15 @@ def pull_image(self): raiseExc=False, returnOutput=1) if exit_status: logger.error(out) + return not int(exit_status) - if not podman_check_native_image_architecture(self.image, logger): - raise BootstrapError("Pulled image has invalid architecture") - + def retry_image_pull(self, max_time): + """ Try pulling the image multiple times """ + @backoff.on_predicate(backoff.expo, lambda x: not x, + max_time=max_time, jitter=backoff.full_jitter) + def _inner(): + self.pull_image() + _inner() @contextmanager def mounted_image(self): @@ -100,7 +106,12 @@ def mounted_image(self): @traceLog() def cp(self, destination, tar_cmd): """ copy content of container to destination directory """ - getLog().info("Copy content of container %s to %s", self.image, destination) + 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("Pulled image has invalid architecture") + 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", "."]