diff --git a/doc/configuration.rst b/doc/configuration.rst index 7325e8c9..a23d9dc1 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -292,8 +292,8 @@ There a couple common format specifiers available for both the options: format string. The pattern should not overlap, otherwise it is undefined which one will be used. - This format will be used for all phases generating images. Currently that - means ``createiso``, ``live_images`` and ``buildinstall``. + This format will be used for some phases generating images. Currently that + means ``createiso``, ``buildinstall`` and ``ostree_installer``. Available extra keys are: * ``disc_num`` @@ -323,7 +323,6 @@ There a couple common format specifiers available for both the options: Available keys are: * ``boot`` -- for ``boot.iso`` images created in *buildinstall* phase - * ``live`` -- for images created by *live_images* phase * ``dvd`` -- for images created by *createiso* phase * ``ostree`` -- for ostree installer images @@ -351,7 +350,6 @@ Example disc_types = { 'boot': 'netinst', - 'live': 'Live', 'dvd': 'DVD', } @@ -1368,8 +1366,8 @@ All non-``RC`` milestones from label get appended to the version. For release either label is used or date, type and respin. -Common options for Live Images, Live Media and Image Build -========================================================== +Common options for Live Media and Image Build +============================================= All images can have ``ksurl``, ``version``, ``release`` and ``target`` specified. Since this can create a lot of duplication, there are global options @@ -1385,14 +1383,12 @@ The kickstart URL is configured by these options. * ``global_ksurl`` -- global fallback setting * ``live_media_ksurl`` * ``image_build_ksurl`` - * ``live_images_ksurl`` Target is specified by these settings. * ``global_target`` -- global fallback setting * ``live_media_target`` * ``image_build_target`` - * ``live_images_target`` * ``osbuild_target`` Version is specified by these options. If no version is set, a default value @@ -1401,7 +1397,6 @@ will be provided according to :ref:`automatic versioning `. * ``global_version`` -- global fallback setting * ``live_media_version`` * ``image_build_version`` - * ``live_images_version`` * ``osbuild_version`` Release is specified by these options. If set to a magic value to @@ -1411,44 +1406,14 @@ to :ref:`automatic versioning `. * ``global_release`` -- global fallback setting * ``live_media_release`` * ``image_build_release`` - * ``live_images_release`` * ``osbuild_release`` -Each configuration block can also optionally specify a ``failable`` key. For -live images it should have a boolean value. For live media and image build it +Each configuration block can also optionally specify a ``failable`` key. It should be a list of strings containing architectures that are optional. If any deliverable fails on an optional architecture, it will not abort the whole compose. If the list contains only ``"*"``, all arches will be substituted. -Live Images Settings -==================== - -**live_images** - (*list*) -- Configuration for the particular image. The elements of the - list should be tuples ``(variant_uid_regex, {arch|*: config})``. The config - should be a dict with these keys: - - * ``kickstart`` (*str*) - * ``ksurl`` (*str*) [optional] -- where to get the kickstart from - * ``name`` (*str*) - * ``version`` (*str*) - * ``target`` (*str*) - * ``repo`` (*str|[str]*) -- repos specified by URL or variant UID - * ``specfile`` (*str*) -- for images wrapped in RPM - * ``scratch`` (*bool*) -- only RPM-wrapped images can use scratch builds, - but by default this is turned off - * ``type`` (*str*) -- what kind of task to start in Koji. Defaults to - ``live`` meaning ``koji spin-livecd`` will be used. Alternative option - is ``appliance`` corresponding to ``koji spin-appliance``. - * ``sign`` (*bool*) -- only RPM-wrapped images can be signed - -**live_images_no_rename** - (*bool*) -- When set to ``True``, filenames generated by Koji will be used. - When ``False``, filenames will be generated based on ``image_name_format`` - configuration option. - - Live Media Settings =================== @@ -2306,9 +2271,9 @@ Miscellaneous Settings format string accepting ``%(variant_name)s`` and ``%(arch)s`` placeholders. **symlink_isos_to** - (*str*) -- If set, the ISO files from ``buildinstall``, ``createiso`` and - ``live_images`` phases will be put into this destination, and a symlink - pointing to this location will be created in actual compose directory. + (*str*) -- If set, the ISO files from ``buildinstall`` and ``createiso`` + phases will be put into this destination, and a symlink pointing to this + location will be created in actual compose directory. **dogpile_cache_backend** (*str*) -- If set, Pungi will use the configured Dogpile cache backend to diff --git a/doc/examples.rst b/doc/examples.rst index 14eeaf12..4e86f656 100644 --- a/doc/examples.rst +++ b/doc/examples.rst @@ -294,30 +294,6 @@ This is a shortened configuration for Fedora Radhide compose as of 2019-10-14. }) ] - live_target = 'f32' - live_images_no_rename = True - live_images = [ - ('^Workstation$', { - 'armhfp': { - 'kickstart': 'fedora-arm-workstation.ks', - 'name': 'Fedora-Workstation-armhfp', - # Again workstation takes packages from Everything. - 'repo': 'Everything', - 'type': 'appliance', - 'failable': True, - } - }), - ('^Server$', { - # But Server has its own repo. - 'armhfp': { - 'kickstart': 'fedora-arm-server.ks', - 'name': 'Fedora-Server-armhfp', - 'type': 'appliance', - 'failable': True, - } - }), - ] - ostree = { "^Silverblue$": { "version": "!OSTREE_VERSION_FROM_LABEL_DATE_TYPE_RESPIN", diff --git a/pungi/checks.py b/pungi/checks.py index b606c048..d79291e6 100644 --- a/pungi/checks.py +++ b/pungi/checks.py @@ -553,26 +553,6 @@ def make_schema(): "list_of_strings": {"type": "array", "items": {"type": "string"}}, "strings": _one_or_list({"type": "string"}), "optional_string": {"anyOf": [{"type": "string"}, {"type": "null"}]}, - "live_image_config": { - "type": "object", - "properties": { - "kickstart": {"type": "string"}, - "ksurl": {"type": "url"}, - "name": {"type": "string"}, - "subvariant": {"type": "string"}, - "target": {"type": "string"}, - "version": {"type": "string"}, - "repo": {"$ref": "#/definitions/repos"}, - "specfile": {"type": "string"}, - "scratch": {"type": "boolean"}, - "type": {"type": "string"}, - "sign": {"type": "boolean"}, - "failable": {"type": "boolean"}, - "release": {"$ref": "#/definitions/optional_string"}, - }, - "required": ["kickstart"], - "additionalProperties": False, - }, "osbs_config": { "type": "object", "properties": { @@ -903,11 +883,6 @@ def make_schema(): }, "restricted_volid": {"type": "boolean", "default": False}, "volume_id_substitutions": {"type": "object", "default": {}}, - "live_images_no_rename": {"type": "boolean", "default": False}, - "live_images_ksurl": {"type": "url"}, - "live_images_target": {"type": "string"}, - "live_images_release": {"$ref": "#/definitions/optional_string"}, - "live_images_version": {"type": "string"}, "image_build_ksurl": {"type": "url"}, "image_build_target": {"type": "string"}, "image_build_release": {"$ref": "#/definitions/optional_string"}, @@ -940,8 +915,6 @@ def make_schema(): "product_id": {"$ref": "#/definitions/str_or_scm_dict"}, "product_id_allow_missing": {"type": "boolean", "default": False}, "product_id_allow_name_prefix": {"type": "boolean", "default": True}, - # Deprecated in favour of regular local/phase/global setting. - "live_target": {"type": "string"}, "tree_arches": {"$ref": "#/definitions/list_of_strings", "default": []}, "tree_variants": {"$ref": "#/definitions/list_of_strings", "default": []}, "translate_paths": {"$ref": "#/definitions/string_pairs", "default": []}, @@ -1163,9 +1136,6 @@ def make_schema(): "ostree_container_use_koji_plugin": {"type": "boolean", "default": False}, "ostree_installer_use_koji_plugin": {"type": "boolean", "default": False}, "ostree_installer_overwrite": {"type": "boolean", "default": False}, - "live_images": _variant_arch_mapping( - _one_or_list({"$ref": "#/definitions/live_image_config"}) - ), "image_build_allow_reuse": {"type": "boolean", "default": False}, "image_build": { "type": "object", diff --git a/pungi/phases/__init__.py b/pungi/phases/__init__.py index 7d94e485..fb930710 100644 --- a/pungi/phases/__init__.py +++ b/pungi/phases/__init__.py @@ -25,7 +25,6 @@ from .buildinstall import BuildinstallPhase # noqa from .extra_files import ExtraFilesPhase # noqa from .createiso import CreateisoPhase # noqa from .extra_isos import ExtraIsosPhase # noqa -from .live_images import LiveImagesPhase # noqa from .image_build import ImageBuildPhase # noqa from .image_container import ImageContainerPhase # noqa from .kiwibuild import KiwiBuildPhase # noqa diff --git a/pungi/phases/live_images.py b/pungi/phases/live_images.py deleted file mode 100644 index 3f1f158a..00000000 --- a/pungi/phases/live_images.py +++ /dev/null @@ -1,406 +0,0 @@ -# -*- coding: utf-8 -*- - - -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Library General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, see . - - -import os -import sys -import time -import shutil - -from kobo.threads import ThreadPool, WorkerThread -from kobo.shortcuts import run, save_to_file, force_list -from productmd.images import Image -from six.moves import shlex_quote - -from pungi.wrappers.kojiwrapper import KojiWrapper -from pungi.wrappers import iso -from pungi.phases import base -from pungi.util import makedirs, get_mtime, get_file_size, failable -from pungi.util import get_repo_urls - - -# HACK: define cmp in python3 -if sys.version_info[0] == 3: - - def cmp(a, b): - return (a > b) - (a < b) - - -class LiveImagesPhase( - base.PhaseLoggerMixin, base.ImageConfigMixin, base.ConfigGuardedPhase -): - name = "live_images" - - def __init__(self, compose): - super(LiveImagesPhase, self).__init__(compose) - self.pool = ThreadPool(logger=self.logger) - - def _get_repos(self, arch, variant, data): - repos = [] - if not variant.is_empty: - repos.append(variant.uid) - repos.extend(force_list(data.get("repo", []))) - return get_repo_urls(self.compose, repos, arch=arch) - - def run(self): - symlink_isos_to = self.compose.conf.get("symlink_isos_to") - commands = [] - - for variant in self.compose.all_variants.values(): - for arch in variant.arches + ["src"]: - for data in self.get_config_block(variant, arch): - subvariant = data.get("subvariant", variant.uid) - type = data.get("type", "live") - - if type == "live": - dest_dir = self.compose.paths.compose.iso_dir( - arch, variant, symlink_to=symlink_isos_to - ) - elif type == "appliance": - dest_dir = self.compose.paths.compose.image_dir( - variant, symlink_to=symlink_isos_to - ) - dest_dir = dest_dir % {"arch": arch} - makedirs(dest_dir) - else: - raise RuntimeError("Unknown live image type %s" % type) - if not dest_dir: - continue - - cmd = { - "name": data.get("name"), - "version": self.get_version(data), - "release": self.get_release(data), - "dest_dir": dest_dir, - "build_arch": arch, - "ks_file": data["kickstart"], - "ksurl": self.get_ksurl(data), - # Used for images wrapped in RPM - "specfile": data.get("specfile", None), - # Scratch (only taken in consideration if specfile - # specified) For images wrapped in rpm is scratch - # disabled by default For other images is scratch - # always on - "scratch": data.get("scratch", False), - "sign": False, - "type": type, - "label": "", # currently not used - "subvariant": subvariant, - "failable_arches": data.get("failable", []), - # First see if live_target is specified, then fall back - # to regular setup of local, phase and global setting. - "target": self.compose.conf.get("live_target") - or self.get_config(data, "target"), - } - - cmd["repos"] = self._get_repos(arch, variant, data) - - # Signing of the rpm wrapped image - if not cmd["scratch"] and data.get("sign"): - cmd["sign"] = True - - cmd["filename"] = self._get_file_name( - arch, variant, cmd["name"], cmd["version"] - ) - - commands.append((cmd, variant, arch)) - - for cmd, variant, arch in commands: - self.pool.add(CreateLiveImageThread(self.pool)) - self.pool.queue_put((self.compose, cmd, variant, arch)) - - self.pool.start() - - def _get_file_name(self, arch, variant, name=None, version=None): - if self.compose.conf["live_images_no_rename"]: - return None - - disc_type = self.compose.conf["disc_types"].get("live", "live") - - format = ( - "%(compose_id)s-%(variant)s-%(arch)s-%(disc_type)s%(disc_num)s%(suffix)s" - ) - # Custom name (prefix) - if name: - custom_iso_name = name - if version: - custom_iso_name += "-%s" % version - format = ( - custom_iso_name - + "-%(variant)s-%(arch)s-%(disc_type)s%(disc_num)s%(suffix)s" - ) - - # XXX: hardcoded disc_num - return self.compose.get_image_name( - arch, variant, disc_type=disc_type, disc_num=None, format=format - ) - - -class CreateLiveImageThread(WorkerThread): - EXTS = (".iso", ".raw.xz") - - def process(self, item, num): - compose, cmd, variant, arch = item - self.failable_arches = cmd.get("failable_arches", []) - self.can_fail = bool(self.failable_arches) - with failable( - compose, - self.can_fail, - variant, - arch, - "live", - cmd.get("subvariant"), - logger=self.pool._logger, - ): - self.worker(compose, cmd, variant, arch, num) - - def worker(self, compose, cmd, variant, arch, num): - self.basename = "%(name)s-%(version)s-%(release)s" % cmd - log_file = compose.paths.log.log_file(arch, "liveimage-%s" % self.basename) - - subvariant = cmd.pop("subvariant") - - imgname = "%s-%s-%s-%s" % ( - compose.ci_base.release.short, - subvariant, - "Live" if cmd["type"] == "live" else "Disk", - arch, - ) - - msg = "Creating ISO (arch: %s, variant: %s): %s" % ( - arch, - variant, - self.basename, - ) - self.pool.log_info("[BEGIN] %s" % msg) - - koji_wrapper = KojiWrapper(compose) - _, version = compose.compose_id.rsplit("-", 1) - name = cmd["name"] or imgname - version = cmd["version"] or version - archive = False - if cmd["specfile"] and not cmd["scratch"]: - # Non scratch build are allowed only for rpm wrapped images - archive = True - koji_cmd = koji_wrapper.get_create_image_cmd( - name, - version, - cmd["target"], - cmd["build_arch"], - cmd["ks_file"], - cmd["repos"], - image_type=cmd["type"], - wait=True, - archive=archive, - specfile=cmd["specfile"], - release=cmd["release"], - ksurl=cmd["ksurl"], - ) - - # avoid race conditions? - # Kerberos authentication failed: - # Permission denied in replay cache code (-1765328215) - time.sleep(num * 3) - - output = koji_wrapper.run_blocking_cmd(koji_cmd, log_file=log_file) - if output["retcode"] != 0: - raise RuntimeError( - "LiveImage task failed: %s. See %s for more details." - % (output["task_id"], log_file) - ) - - # copy finished image to isos/ - image_path = [ - path - for path in koji_wrapper.get_image_path(output["task_id"]) - if self._is_image(path) - ] - if len(image_path) != 1: - raise RuntimeError( - "Got %d images from task %d, expected 1." - % (len(image_path), output["task_id"]) - ) - image_path = compose.koji_downloader.get_file(image_path[0]) - filename = cmd.get("filename") or os.path.basename(image_path) - destination = os.path.join(cmd["dest_dir"], filename) - shutil.copy2(image_path, destination) - - # copy finished rpm to isos/ (if rpm wrapped ISO was built) - if cmd["specfile"]: - rpm_paths = koji_wrapper.get_wrapped_rpm_path(output["task_id"]) - - if cmd["sign"]: - # Sign the rpm wrapped images and get their paths - self.pool.log_info( - "Signing rpm wrapped images in task_id: %s (expected key ID: %s)" - % (output["task_id"], compose.conf.get("signing_key_id")) - ) - signed_rpm_paths = self._sign_image( - koji_wrapper, compose, cmd, output["task_id"] - ) - if signed_rpm_paths: - rpm_paths = signed_rpm_paths - - for rpm_path in rpm_paths: - shutil.copy2(rpm_path, cmd["dest_dir"]) - - if cmd["type"] == "live": - # ISO manifest only makes sense for live images - self._write_manifest(destination) - - self._add_to_images( - compose, - variant, - subvariant, - arch, - cmd["type"], - self._get_format(image_path), - destination, - ) - - self.pool.log_info("[DONE ] %s (task id: %s)" % (msg, output["task_id"])) - - def _add_to_images(self, compose, variant, subvariant, arch, type, format, path): - """Adds the image to images.json""" - img = Image(compose.im) - img.type = "raw-xz" if type == "appliance" else type - img.format = format - img.path = os.path.relpath(path, compose.paths.compose.topdir()) - img.mtime = get_mtime(path) - img.size = get_file_size(path) - img.arch = arch - img.disc_number = 1 # We don't expect multiple disks - img.disc_count = 1 - img.bootable = True - img.subvariant = subvariant - setattr(img, "can_fail", self.can_fail) - setattr(img, "deliverable", "live") - compose.im.add(variant=variant.uid, arch=arch, image=img) - - def _is_image(self, path): - for ext in self.EXTS: - if path.endswith(ext): - return True - return False - - def _get_format(self, path): - """Get format based on extension.""" - for ext in self.EXTS: - if path.endswith(ext): - return ext[1:] - raise RuntimeError("Getting format for unknown image %s" % path) - - def _write_manifest(self, iso_path): - """Generate manifest for ISO at given path. - - :param iso_path: (str) absolute path to the ISO - """ - dir, filename = os.path.split(iso_path) - run("cd %s && %s" % (shlex_quote(dir), iso.get_manifest_cmd(filename))) - - def _sign_image(self, koji_wrapper, compose, cmd, koji_task_id): - signing_key_id = compose.conf.get("signing_key_id") - signing_command = compose.conf.get("signing_command") - - if not signing_key_id: - self.pool.log_warning( - "Signing is enabled but signing_key_id is not specified" - ) - self.pool.log_warning("Signing skipped") - return None - if not signing_command: - self.pool.log_warning( - "Signing is enabled but signing_command is not specified" - ) - self.pool.log_warning("Signing skipped") - return None - - # Prepare signing log file - signing_log_file = compose.paths.log.log_file( - cmd["build_arch"], "live_images-signing-%s" % self.basename - ) - - # Sign the rpm wrapped images - try: - sign_builds_in_task( - koji_wrapper, - koji_task_id, - signing_command, - log_file=signing_log_file, - signing_key_password=compose.conf.get("signing_key_password"), - ) - except RuntimeError: - self.pool.log_error( - "Error while signing rpm wrapped images. See log: %s" % signing_log_file - ) - raise - - # Get pats to the signed rpms - signing_key_id = signing_key_id.lower() # Koji uses lowercase in paths - rpm_paths = koji_wrapper.get_signed_wrapped_rpms_paths( - koji_task_id, signing_key_id - ) - - # Wait until files are available - if wait_paths(rpm_paths, 60 * 15): - # Files are ready - return rpm_paths - - # Signed RPMs are not available - self.pool.log_warning("Signed files are not available: %s" % rpm_paths) - self.pool.log_warning("Unsigned files will be used") - return None - - -def wait_paths(paths, timeout=60): - started = time.time() - remaining = paths[:] - while True: - for path in remaining[:]: - if os.path.exists(path): - remaining.remove(path) - if not remaining: - break - time.sleep(1) - if timeout >= 0 and (time.time() - started) > timeout: - return False - return True - - -def sign_builds_in_task( - koji_wrapper, task_id, signing_command, log_file=None, signing_key_password=None -): - # Get list of nvrs that should be signed - nvrs = koji_wrapper.get_build_nvrs(task_id) - if not nvrs: - # No builds are available (scratch build, etc.?) - return - - # Append builds to sign_cmd - for nvr in nvrs: - signing_command += " '%s'" % nvr - - # Log signing command before password is filled in it - if log_file: - save_to_file(log_file, signing_command, append=True) - - # Fill password into the signing command - if signing_key_password: - signing_command = signing_command % { - "signing_key_password": signing_key_password - } - - # Sign the builds - run(signing_command, can_fail=False, show_cmd=False, logfile=log_file) diff --git a/pungi/scripts/config_validate.py b/pungi/scripts/config_validate.py index b4bdb1eb..8d47ff29 100644 --- a/pungi/scripts/config_validate.py +++ b/pungi/scripts/config_validate.py @@ -128,7 +128,6 @@ def run(config, topdir, has_old, offline, defined_variables, schema_overrides): pungi.phases.OSTreePhase(compose), pungi.phases.CreateisoPhase(compose, buildinstall_phase), pungi.phases.ExtraIsosPhase(compose, buildinstall_phase), - pungi.phases.LiveImagesPhase(compose), pungi.phases.LiveMediaPhase(compose), pungi.phases.ImageBuildPhase(compose), pungi.phases.ImageChecksumPhase(compose), diff --git a/pungi/scripts/pungi_koji.py b/pungi/scripts/pungi_koji.py index 84c9c9a8..ce88961c 100644 --- a/pungi/scripts/pungi_koji.py +++ b/pungi/scripts/pungi_koji.py @@ -420,7 +420,6 @@ def run_compose( ostree_container_phase = pungi.phases.OSTreeContainerPhase(compose, pkgset_phase) createiso_phase = pungi.phases.CreateisoPhase(compose, buildinstall_phase) extra_isos_phase = pungi.phases.ExtraIsosPhase(compose, buildinstall_phase) - liveimages_phase = pungi.phases.LiveImagesPhase(compose) livemedia_phase = pungi.phases.LiveMediaPhase(compose) image_build_phase = pungi.phases.ImageBuildPhase(compose, buildinstall_phase) kiwibuild_phase = pungi.phases.KiwiBuildPhase(compose) @@ -440,7 +439,6 @@ def run_compose( gather_phase, extrafiles_phase, createiso_phase, - liveimages_phase, livemedia_phase, image_build_phase, image_checksum_phase, @@ -477,7 +475,6 @@ def run_compose( "signing_key_password_file" in compose.conf and "signing_command" in compose.conf and "%(signing_key_password)s" in compose.conf["signing_command"] - and not liveimages_phase.skip() ): # TODO: Don't require key if signing is turned off # Obtain signing key password @@ -548,7 +545,6 @@ def run_compose( compose_images_schema = ( createiso_phase, extra_isos_phase, - liveimages_phase, image_build_phase, livemedia_phase, osbuild_phase, @@ -574,7 +570,6 @@ def run_compose( and ostree_installer_phase.skip() and createiso_phase.skip() and extra_isos_phase.skip() - and liveimages_phase.skip() and livemedia_phase.skip() and image_build_phase.skip() and kiwibuild_phase.skip() diff --git a/pungi/wrappers/kojiwrapper.py b/pungi/wrappers/kojiwrapper.py index 6495bd9b..18447683 100644 --- a/pungi/wrappers/kojiwrapper.py +++ b/pungi/wrappers/kojiwrapper.py @@ -413,92 +413,6 @@ class KojiWrapper(object): return cmd - def get_create_image_cmd( - self, - name, - version, - target, - arch, - ks_file, - repos, - image_type="live", - image_format=None, - release=None, - wait=True, - archive=False, - specfile=None, - ksurl=None, - ): - # Usage: koji spin-livecd [options] # noqa: E501 - # Usage: koji spin-appliance [options] # noqa: E501 - # Examples: - # * name: RHEL-7.0 - # * name: Satellite-6.0.1-RHEL-6 - # ** -. - # * version: YYYYMMDD[.n|.t].X - # * release: 1 - - cmd = self._get_cmd() - - if image_type == "live": - cmd.append("spin-livecd") - elif image_type == "appliance": - cmd.append("spin-appliance") - else: - raise ValueError("Invalid image type: %s" % image_type) - - if not archive: - cmd.append("--scratch") - - cmd.append("--noprogress") - - if wait: - cmd.append("--wait") - else: - cmd.append("--nowait") - - if specfile: - cmd.append("--specfile=%s" % specfile) - - if ksurl: - cmd.append("--ksurl=%s" % ksurl) - - if isinstance(repos, list): - for repo in repos: - cmd.append("--repo=%s" % repo) - else: - cmd.append("--repo=%s" % repos) - - if image_format: - if image_type != "appliance": - raise ValueError("Format can be specified only for appliance images'") - supported_formats = ["raw", "qcow", "qcow2", "vmx"] - if image_format not in supported_formats: - raise ValueError( - "Format is not supported: %s. Supported formats: %s" - % (image_format, " ".join(sorted(supported_formats))) - ) - cmd.append("--format=%s" % image_format) - - if release is not None: - cmd.append("--release=%s" % release) - - # IMPORTANT: all --opts have to be provided *before* args - # Usage: - # koji spin-livecd [options] - - cmd.append(name) - cmd.append(version) - cmd.append(target) - - # i686 -> i386 etc. - arch = getBaseArch(arch) - cmd.append(arch) - - cmd.append(ks_file) - - return cmd - def _has_connection_error(self, output): """Checks if output indicates connection error.""" return re.search("error: failed to connect\n$", output) diff --git a/tests/test_koji_wrapper.py b/tests/test_koji_wrapper.py index e203d5e3..3412a87d 100644 --- a/tests/test_koji_wrapper.py +++ b/tests/test_koji_wrapper.py @@ -441,96 +441,6 @@ class LiveMediaTestCase(KojiWrapperBaseTestCase): ) -class LiveImageKojiWrapperTest(KojiWrapperBaseTestCase): - def test_get_create_image_cmd_minimal(self): - cmd = self.koji.get_create_image_cmd( - "my_name", "1.0", "f24-candidate", "x86_64", "/path/to/ks", ["/repo/1"] - ) - self.assertEqual(cmd[0:3], ["koji", "--profile=custom-koji", "spin-livecd"]) - six.assertCountEqual( - self, cmd[3:7], ["--noprogress", "--scratch", "--wait", "--repo=/repo/1"] - ) - self.assertEqual( - cmd[7:], ["my_name", "1.0", "f24-candidate", "x86_64", "/path/to/ks"] - ) - - def test_get_create_image_cmd_full(self): - cmd = self.koji.get_create_image_cmd( - "my_name", - "1.0", - "f24-candidate", - "x86_64", - "/path/to/ks", - ["/repo/1", "/repo/2"], - release="1", - wait=False, - archive=True, - specfile="foo.spec", - ksurl="https://git.example.com/", - ) - self.assertEqual(cmd[0:3], ["koji", "--profile=custom-koji", "spin-livecd"]) - self.assertEqual( - cmd[-5:], ["my_name", "1.0", "f24-candidate", "x86_64", "/path/to/ks"] - ) - six.assertCountEqual( - self, - cmd[3:-5], - [ - "--noprogress", - "--nowait", - "--repo=/repo/1", - "--repo=/repo/2", - "--release=1", - "--specfile=foo.spec", - "--ksurl=https://git.example.com/", - ], - ) - - def test_spin_livecd_with_format(self): - with self.assertRaises(ValueError): - self.koji.get_create_image_cmd( - "my_name", - "1.0", - "f24-candidate", - "x86_64", - "/path/to/ks", - [], - image_format="qcow", - ) - - def test_spin_appliance_with_format(self): - cmd = self.koji.get_create_image_cmd( - "my_name", - "1.0", - "f24-candidate", - "x86_64", - "/path/to/ks", - [], - image_type="appliance", - image_format="qcow", - ) - self.assertEqual(cmd[0:3], ["koji", "--profile=custom-koji", "spin-appliance"]) - six.assertCountEqual( - self, cmd[3:7], ["--noprogress", "--scratch", "--wait", "--format=qcow"] - ) - self.assertEqual( - cmd[7:], ["my_name", "1.0", "f24-candidate", "x86_64", "/path/to/ks"] - ) - - def test_spin_appliance_with_wrong_format(self): - with self.assertRaises(ValueError): - self.koji.get_create_image_cmd( - "my_name", - "1.0", - "f24-candidate", - "x86_64", - "/path/to/ks", - [], - image_type="appliance", - image_format="pretty", - ) - - @mock.patch.dict("os.environ", {"FOO": "BAR"}, clear=True) class RunrootKojiWrapperTest(KojiWrapperBaseTestCase): def test_get_cmd_minimal(self): diff --git a/tests/test_liveimagesphase.py b/tests/test_liveimagesphase.py deleted file mode 100644 index 10fe06ea..00000000 --- a/tests/test_liveimagesphase.py +++ /dev/null @@ -1,1059 +0,0 @@ -# -*- coding: utf-8 -*- - -try: - from unittest import mock -except ImportError: - import mock - -import six - -from pungi.phases.live_images import LiveImagesPhase, CreateLiveImageThread -from tests.helpers import DummyCompose, PungiTestCase, boom - - -class TestLiveImagesPhase(PungiTestCase): - @mock.patch("pungi.phases.live_images.ThreadPool") - def test_live_image_build(self, ThreadPool): - compose = DummyCompose( - self.topdir, - { - "live_images": [ - ( - "^Client$", - { - "amd64": { - "kickstart": "test.ks", - "repo": [ - "http://example.com/repo/", - "Everything", - "Server-optional", - ], - "release": None, - "target": "f27", - } - }, - ) - ], - }, - ) - compose.setup_optional() - - self.assertValidConfig(compose.conf) - - phase = LiveImagesPhase(compose) - - phase.run() - - # assert at least one thread was started - phase.pool.add.assert_called() - self.maxDiff = None - six.assertCountEqual( - self, - phase.pool.queue_put.mock_calls, - [ - mock.call( - ( - compose, - { - "ks_file": "test.ks", - "build_arch": "amd64", - "dest_dir": self.topdir + "/compose/Client/amd64/iso", - "scratch": False, - "repos": [ - self.topdir + "/compose/Client/amd64/os", - "http://example.com/repo/", - self.topdir + "/compose/Everything/amd64/os", - self.topdir + "/compose/Server-optional/amd64/os", - ], - "label": "", - "name": None, - "filename": "image-name", - "version": "25", - "specfile": None, - "sign": False, - "type": "live", - "release": "20151203.t.0", - "subvariant": "Client", - "failable_arches": [], - "ksurl": None, - "target": "f27", - }, - compose.variants["Client"], - "amd64", - ) - ) - ], - ) - six.assertCountEqual( - self, - compose.get_image_name.mock_calls, - [ - mock.call( - "amd64", - compose.variants["Client"], - disc_num=None, - disc_type="live", - format="%(compose_id)s-%(variant)s-%(arch)s-%(disc_type)s%(disc_num)s%(suffix)s", # noqa: E501 - ) - ], - ) - - @mock.patch("pungi.phases.live_images.ThreadPool") - def test_live_image_build_single_repo_from(self, ThreadPool): - compose = DummyCompose( - self.topdir, - { - "live_images": [ - ( - "^Client$", - { - "amd64": { - "kickstart": "test.ks", - "repo": ["http://example.com/repo/", "Everything"], - "release": None, - "target": "f27", - } - }, - ) - ], - }, - ) - - self.assertValidConfig(compose.conf) - - phase = LiveImagesPhase(compose) - - phase.run() - - # assert at least one thread was started - phase.pool.add.assert_called() - self.maxDiff = None - six.assertCountEqual( - self, - phase.pool.queue_put.mock_calls, - [ - mock.call( - ( - compose, - { - "ks_file": "test.ks", - "build_arch": "amd64", - "dest_dir": self.topdir + "/compose/Client/amd64/iso", - "scratch": False, - "repos": [ - self.topdir + "/compose/Client/amd64/os", - "http://example.com/repo/", - self.topdir + "/compose/Everything/amd64/os", - ], - "label": "", - "name": None, - "filename": "image-name", - "version": "25", - "specfile": None, - "sign": False, - "type": "live", - "release": "20151203.t.0", - "subvariant": "Client", - "failable_arches": [], - "ksurl": None, - "target": "f27", - }, - compose.variants["Client"], - "amd64", - ) - ) - ], - ) - - @mock.patch("pungi.phases.live_images.ThreadPool") - def test_live_image_build_without_rename(self, ThreadPool): - compose = DummyCompose( - self.topdir, - { - "live_images_no_rename": True, - "live_images": [ - ( - "^Client$", - { - "amd64": { - "kickstart": "test.ks", - "repo": ["http://example.com/repo/", "Everything"], - "release": None, - "target": "f27", - } - }, - ) - ], - }, - ) - - self.assertValidConfig(compose.conf) - - phase = LiveImagesPhase(compose) - - phase.run() - - # assert at least one thread was started - phase.pool.add.assert_called() - self.maxDiff = None - six.assertCountEqual( - self, - phase.pool.queue_put.mock_calls, - [ - mock.call( - ( - compose, - { - "ks_file": "test.ks", - "build_arch": "amd64", - "dest_dir": self.topdir + "/compose/Client/amd64/iso", - "scratch": False, - "repos": [ - self.topdir + "/compose/Client/amd64/os", - "http://example.com/repo/", - self.topdir + "/compose/Everything/amd64/os", - ], - "label": "", - "name": None, - "filename": None, - "version": "25", - "specfile": None, - "sign": False, - "type": "live", - "release": "20151203.t.0", - "subvariant": "Client", - "failable_arches": [], - "ksurl": None, - "target": "f27", - }, - compose.variants["Client"], - "amd64", - ) - ) - ], - ) - - @mock.patch("pungi.phases.live_images.ThreadPool") - def test_live_image_build_two_images(self, ThreadPool): - compose = DummyCompose( - self.topdir, - { - "live_images": [ - ( - "^Client$", - { - "amd64": [ - { - "kickstart": "test.ks", - "repo": ["http://example.com/repo/", "Everything"], - "target": "f27", - }, - { - "kickstart": "another.ks", - "repo": ["http://example.com/repo/", "Everything"], - "target": "f27", - }, - ] - }, - ) - ], - }, - ) - - self.assertValidConfig(compose.conf) - - phase = LiveImagesPhase(compose) - - phase.run() - - # assert at least one thread was started - phase.pool.add.assert_called() - self.maxDiff = None - six.assertCountEqual( - self, - phase.pool.queue_put.mock_calls, - [ - mock.call( - ( - compose, - { - "ks_file": "test.ks", - "build_arch": "amd64", - "dest_dir": self.topdir + "/compose/Client/amd64/iso", - "scratch": False, - "repos": [ - self.topdir + "/compose/Client/amd64/os", - "http://example.com/repo/", - self.topdir + "/compose/Everything/amd64/os", - ], - "label": "", - "name": None, - "filename": "image-name", - "version": "25", - "specfile": None, - "sign": False, - "type": "live", - "release": None, - "subvariant": "Client", - "failable_arches": [], - "target": "f27", - "ksurl": None, - }, - compose.variants["Client"], - "amd64", - ) - ), - mock.call( - ( - compose, - { - "ks_file": "another.ks", - "build_arch": "amd64", - "dest_dir": self.topdir + "/compose/Client/amd64/iso", - "scratch": False, - "repos": [ - self.topdir + "/compose/Client/amd64/os", - "http://example.com/repo/", - self.topdir + "/compose/Everything/amd64/os", - ], - "label": "", - "name": None, - "filename": "image-name", - "version": "25", - "specfile": None, - "sign": False, - "type": "live", - "release": None, - "subvariant": "Client", - "failable_arches": [], - "target": "f27", - "ksurl": None, - }, - compose.variants["Client"], - "amd64", - ) - ), - ], - ) - - @mock.patch("pungi.phases.live_images.ThreadPool") - def test_spin_appliance(self, ThreadPool): - compose = DummyCompose( - self.topdir, - { - "live_images": [ - ( - "^Client$", - { - "amd64": { - "kickstart": "test.ks", - "ksurl": "https://git.example.com/kickstarts.git?#CAFEBABE", # noqa: E501 - "repo": ["http://example.com/repo/", "Everything"], - "type": "appliance", - "target": "f27", - } - }, - ) - ], - }, - ) - - self.assertValidConfig(compose.conf) - - phase = LiveImagesPhase(compose) - - phase.run() - - # assert at least one thread was started - phase.pool.add.assert_called() - self.maxDiff = None - six.assertCountEqual( - self, - phase.pool.queue_put.mock_calls, - [ - mock.call( - ( - compose, - { - "ks_file": "test.ks", - "build_arch": "amd64", - "dest_dir": self.topdir + "/compose/Client/amd64/images", - "scratch": False, - "repos": [ - self.topdir + "/compose/Client/amd64/os", - "http://example.com/repo/", - self.topdir + "/compose/Everything/amd64/os", - ], - "label": "", - "name": None, - "filename": "image-name", - "version": "25", - "specfile": None, - "sign": False, - "type": "appliance", - "release": None, - "subvariant": "Client", - "failable_arches": [], - "target": "f27", - "ksurl": "https://git.example.com/kickstarts.git?#CAFEBABE", - }, - compose.variants["Client"], - "amd64", - ) - ) - ], - ) - - @mock.patch("pungi.phases.live_images.ThreadPool") - def test_spin_appliance_phase_global_settings(self, ThreadPool): - compose = DummyCompose( - self.topdir, - { - "live_images_ksurl": "https://git.example.com/kickstarts.git?#CAFEBABE", - "live_images_release": None, - "live_images_version": "Rawhide", - "live_images_target": "f27", - "live_images": [ - ( - "^Client$", - { - "amd64": { - "kickstart": "test.ks", - "repo": ["http://example.com/repo/", "Everything"], - "type": "appliance", - } - }, - ) - ], - }, - ) - - self.assertValidConfig(compose.conf) - - phase = LiveImagesPhase(compose) - - phase.run() - - # assert at least one thread was started - phase.pool.add.assert_called() - self.maxDiff = None - six.assertCountEqual( - self, - phase.pool.queue_put.mock_calls, - [ - mock.call( - ( - compose, - { - "ks_file": "test.ks", - "build_arch": "amd64", - "dest_dir": self.topdir + "/compose/Client/amd64/images", - "scratch": False, - "repos": [ - self.topdir + "/compose/Client/amd64/os", - "http://example.com/repo/", - self.topdir + "/compose/Everything/amd64/os", - ], - "label": "", - "name": None, - "filename": "image-name", - "version": "Rawhide", - "specfile": None, - "sign": False, - "type": "appliance", - "release": "20151203.t.0", - "subvariant": "Client", - "failable_arches": [], - "target": "f27", - "ksurl": "https://git.example.com/kickstarts.git?#CAFEBABE", - }, - compose.variants["Client"], - "amd64", - ) - ) - ], - ) - - @mock.patch("pungi.phases.live_images.ThreadPool") - def test_spin_appliance_global_settings(self, ThreadPool): - compose = DummyCompose( - self.topdir, - { - "global_ksurl": "https://git.example.com/kickstarts.git?#CAFEBABE", - "global_release": None, - "global_version": "Rawhide", - "global_target": "f27", - "live_images": [ - ( - "^Client$", - { - "amd64": { - "kickstart": "test.ks", - "repo": ["http://example.com/repo/", "Everything"], - "type": "appliance", - } - }, - ) - ], - }, - ) - - self.assertValidConfig(compose.conf) - - phase = LiveImagesPhase(compose) - - phase.run() - - # assert at least one thread was started - phase.pool.add.assert_called() - self.maxDiff = None - six.assertCountEqual( - self, - phase.pool.queue_put.mock_calls, - [ - mock.call( - ( - compose, - { - "ks_file": "test.ks", - "build_arch": "amd64", - "dest_dir": self.topdir + "/compose/Client/amd64/images", - "scratch": False, - "repos": [ - self.topdir + "/compose/Client/amd64/os", - "http://example.com/repo/", - self.topdir + "/compose/Everything/amd64/os", - ], - "label": "", - "name": None, - "filename": "image-name", - "version": "Rawhide", - "specfile": None, - "sign": False, - "type": "appliance", - "release": "20151203.t.0", - "subvariant": "Client", - "failable_arches": [], - "target": "f27", - "ksurl": "https://git.example.com/kickstarts.git?#CAFEBABE", - }, - compose.variants["Client"], - "amd64", - ) - ) - ], - ) - - @mock.patch("pungi.phases.live_images.ThreadPool") - def test_live_image_build_custom_type(self, ThreadPool): - compose = DummyCompose( - self.topdir, - { - "disc_types": {"live": "Live"}, - "live_images": [ - ( - "^Client$", - { - "amd64": { - "kickstart": "test.ks", - "repo": ["http://example.com/repo/", "Everything"], - "release": None, - "target": "f27", - } - }, - ) - ], - }, - ) - - self.assertValidConfig(compose.conf) - - phase = LiveImagesPhase(compose) - - phase.run() - - # assert at least one thread was started - phase.pool.add.assert_called() - self.maxDiff = None - six.assertCountEqual( - self, - phase.pool.queue_put.mock_calls, - [ - mock.call( - ( - compose, - { - "ks_file": "test.ks", - "build_arch": "amd64", - "dest_dir": self.topdir + "/compose/Client/amd64/iso", - "scratch": False, - "repos": [ - self.topdir + "/compose/Client/amd64/os", - "http://example.com/repo/", - self.topdir + "/compose/Everything/amd64/os", - ], - "label": "", - "name": None, - "filename": "image-name", - "version": "25", - "specfile": None, - "sign": False, - "type": "live", - "release": "20151203.t.0", - "subvariant": "Client", - "failable_arches": [], - "target": "f27", - "ksurl": None, - }, - compose.variants["Client"], - "amd64", - ) - ) - ], - ) - six.assertCountEqual( - self, - compose.get_image_name.mock_calls, - [ - mock.call( - "amd64", - compose.variants["Client"], - disc_num=None, - disc_type="Live", - format="%(compose_id)s-%(variant)s-%(arch)s-%(disc_type)s%(disc_num)s%(suffix)s", # noqa: E501 - ) - ], - ) - - -class TestCreateLiveImageThread(PungiTestCase): - @mock.patch("pungi.phases.live_images.Image") - @mock.patch("shutil.copy2") - @mock.patch("pungi.phases.live_images.run") - @mock.patch("pungi.phases.live_images.KojiWrapper") - def test_process(self, KojiWrapper, run, copy2, Image): - compose = DummyCompose(self.topdir, {"koji_profile": "koji"}) - pool = mock.Mock() - cmd = { - "ks_file": "/path/to/ks_file", - "build_arch": "amd64", - "dest_dir": self.topdir + "/compose/Client/amd64/iso", - "scratch": False, - "repos": [ - "/repo/amd64/Client", - "http://example.com/repo/", - "/repo/amd64/Everything", - ], - "label": "", - "name": None, - "filename": "image-name", - "version": None, - "specfile": None, - "type": "live", - "ksurl": "https://git.example.com/kickstarts.git?#CAFEBABE", - "release": None, - "subvariant": "Something", - "target": "f27", - } - - koji_wrapper = KojiWrapper.return_value - koji_wrapper.get_create_image_cmd.return_value = "koji spin-livecd ..." - koji_wrapper.run_blocking_cmd.return_value = { - "retcode": 0, - "output": "some output", - "task_id": 123, - } - koji_wrapper.get_image_path.return_value = ["/path/to/image.iso"] - - t = CreateLiveImageThread(pool) - with mock.patch("pungi.phases.live_images.get_file_size") as get_file_size: - get_file_size.return_value = 1024 - with mock.patch("pungi.phases.live_images.get_mtime") as get_mtime: - get_mtime.return_value = 13579 - with mock.patch("time.sleep"): - t.process((compose, cmd, compose.variants["Client"], "amd64"), 1) - - self.assertEqual( - koji_wrapper.run_blocking_cmd.mock_calls, - [ - mock.call( - "koji spin-livecd ...", - log_file=self.topdir - + "/logs/amd64/liveimage-None-None-None.amd64.log", - ) - ], - ) - self.assertEqual(koji_wrapper.get_image_path.mock_calls, [mock.call(123)]) - self.assertEqual( - copy2.mock_calls, - [ - mock.call( - "/path/to/image.iso", - self.topdir + "/compose/Client/amd64/iso/image-name", - ) - ], - ) - - write_manifest_cmd = " && ".join( - [ - "cd " + self.topdir + "/compose/Client/amd64/iso", - "isoinfo -R -f -i image-name | grep -v '/TRANS.TBL$' | sort >> image-name.manifest", # noqa: E501 - ] - ) - self.assertEqual(run.mock_calls, [mock.call(write_manifest_cmd)]) - self.assertEqual( - koji_wrapper.get_create_image_cmd.mock_calls, - [ - mock.call( - "test-Something-Live-amd64", - "20151203.0.t", - "f27", - "amd64", - "/path/to/ks_file", - [ - "/repo/amd64/Client", - "http://example.com/repo/", - "/repo/amd64/Everything", - ], - image_type="live", - archive=False, - specfile=None, - wait=True, - release=None, - ksurl="https://git.example.com/kickstarts.git?#CAFEBABE", - ) - ], - ) - self.assertEqual(Image.return_value.type, "live") - self.assertEqual(Image.return_value.format, "iso") - self.assertEqual(Image.return_value.path, "Client/amd64/iso/image-name") - self.assertEqual(Image.return_value.size, 1024) - self.assertEqual(Image.return_value.mtime, 13579) - self.assertEqual(Image.return_value.arch, "amd64") - self.assertEqual(Image.return_value.disc_number, 1) - self.assertEqual(Image.return_value.disc_count, 1) - self.assertTrue(Image.return_value.bootable) - self.assertEqual( - compose.im.add.mock_calls, - [mock.call(variant="Client", arch="amd64", image=Image.return_value)], - ) - - @mock.patch("pungi.phases.live_images.Image") - @mock.patch("shutil.copy2") - @mock.patch("pungi.phases.live_images.run") - @mock.patch("pungi.phases.live_images.KojiWrapper") - def test_process_no_rename(self, KojiWrapper, run, copy2, Image): - compose = DummyCompose(self.topdir, {"koji_profile": "koji"}) - pool = mock.Mock() - cmd = { - "ks_file": "/path/to/ks_file", - "build_arch": "amd64", - "dest_dir": self.topdir + "/compose/Client/amd64/iso", - "scratch": False, - "repos": [ - "/repo/amd64/Client", - "http://example.com/repo/", - "/repo/amd64/Everything", - ], - "label": "", - "name": None, - "filename": None, - "version": None, - "specfile": None, - "type": "live", - "ksurl": "https://git.example.com/kickstarts.git?#CAFEBABE", - "release": None, - "subvariant": "Client", - "target": "f27", - } - - koji_wrapper = KojiWrapper.return_value - koji_wrapper.get_create_image_cmd.return_value = "koji spin-livecd ..." - koji_wrapper.run_blocking_cmd.return_value = { - "retcode": 0, - "output": "some output", - "task_id": 123, - } - koji_wrapper.get_image_path.return_value = ["/path/to/image.iso"] - - t = CreateLiveImageThread(pool) - with mock.patch("pungi.phases.live_images.get_file_size") as get_file_size: - get_file_size.return_value = 1024 - with mock.patch("pungi.phases.live_images.get_mtime") as get_mtime: - get_mtime.return_value = 13579 - with mock.patch("time.sleep"): - t.process((compose, cmd, compose.variants["Client"], "amd64"), 1) - - self.assertEqual( - koji_wrapper.run_blocking_cmd.mock_calls, - [ - mock.call( - "koji spin-livecd ...", - log_file=self.topdir - + "/logs/amd64/liveimage-None-None-None.amd64.log", - ) - ], - ) - self.assertEqual(koji_wrapper.get_image_path.mock_calls, [mock.call(123)]) - self.assertEqual( - copy2.mock_calls, - [ - mock.call( - "/path/to/image.iso", - self.topdir + "/compose/Client/amd64/iso/image.iso", - ) - ], - ) - - write_manifest_cmd = " && ".join( - [ - "cd " + self.topdir + "/compose/Client/amd64/iso", - "isoinfo -R -f -i image.iso | grep -v '/TRANS.TBL$' | sort >> image.iso.manifest", # noqa: E501 - ] - ) - self.assertEqual(run.mock_calls, [mock.call(write_manifest_cmd)]) - self.assertEqual( - koji_wrapper.get_create_image_cmd.mock_calls, - [ - mock.call( - "test-Client-Live-amd64", - "20151203.0.t", - "f27", - "amd64", - "/path/to/ks_file", - [ - "/repo/amd64/Client", - "http://example.com/repo/", - "/repo/amd64/Everything", - ], - image_type="live", - archive=False, - specfile=None, - wait=True, - release=None, - ksurl="https://git.example.com/kickstarts.git?#CAFEBABE", - ) - ], - ) - - self.assertEqual(Image.return_value.type, "live") - self.assertEqual(Image.return_value.format, "iso") - self.assertEqual(Image.return_value.path, "Client/amd64/iso/image.iso") - self.assertEqual(Image.return_value.size, 1024) - self.assertEqual(Image.return_value.mtime, 13579) - self.assertEqual(Image.return_value.arch, "amd64") - self.assertEqual(Image.return_value.disc_number, 1) - self.assertEqual(Image.return_value.disc_count, 1) - self.assertTrue(Image.return_value.bootable) - self.assertEqual( - compose.im.add.mock_calls, - [mock.call(variant="Client", arch="amd64", image=Image.return_value)], - ) - - @mock.patch("pungi.phases.live_images.Image") - @mock.patch("shutil.copy2") - @mock.patch("pungi.phases.live_images.run") - @mock.patch("pungi.phases.live_images.KojiWrapper") - def test_process_applicance(self, KojiWrapper, run, copy2, Image): - compose = DummyCompose(self.topdir, {"koji_profile": "koji"}) - pool = mock.Mock() - cmd = { - "ks_file": "/path/to/ks_file", - "build_arch": "amd64", - "dest_dir": self.topdir + "/compose/Client/amd64/iso", - "scratch": False, - "repos": [ - "/repo/amd64/Client", - "http://example.com/repo/", - "/repo/amd64/Everything", - ], - "label": "", - "name": None, - "filename": "image-name", - "version": None, - "specfile": None, - "type": "appliance", - "ksurl": None, - "release": None, - "subvariant": "Client", - "target": "f27", - } - - koji_wrapper = KojiWrapper.return_value - koji_wrapper.get_create_image_cmd.return_value = "koji spin-livecd ..." - koji_wrapper.run_blocking_cmd.return_value = { - "retcode": 0, - "output": "some output", - "task_id": 123, - } - koji_wrapper.get_image_path.return_value = ["/path/to/image-a.b-sda.raw.xz"] - - t = CreateLiveImageThread(pool) - with mock.patch("pungi.phases.live_images.get_file_size") as get_file_size: - get_file_size.return_value = 1024 - with mock.patch("pungi.phases.live_images.get_mtime") as get_mtime: - get_mtime.return_value = 13579 - with mock.patch("time.sleep"): - t.process((compose, cmd, compose.variants["Client"], "amd64"), 1) - - self.assertEqual( - koji_wrapper.run_blocking_cmd.mock_calls, - [ - mock.call( - "koji spin-livecd ...", - log_file=self.topdir - + "/logs/amd64/liveimage-None-None-None.amd64.log", - ) - ], - ) - self.assertEqual(koji_wrapper.get_image_path.mock_calls, [mock.call(123)]) - self.assertEqual( - copy2.mock_calls, - [ - mock.call( - "/path/to/image-a.b-sda.raw.xz", - self.topdir + "/compose/Client/amd64/iso/image-name", - ) - ], - ) - - self.assertEqual(run.mock_calls, []) - self.assertEqual( - koji_wrapper.get_create_image_cmd.mock_calls, - [ - mock.call( - "test-Client-Disk-amd64", - "20151203.0.t", - "f27", - "amd64", - "/path/to/ks_file", - [ - "/repo/amd64/Client", - "http://example.com/repo/", - "/repo/amd64/Everything", - ], - image_type="appliance", - archive=False, - specfile=None, - wait=True, - release=None, - ksurl=None, - ) - ], - ) - - self.assertEqual(Image.return_value.type, "raw-xz") - self.assertEqual(Image.return_value.format, "raw.xz") - self.assertEqual(Image.return_value.path, "Client/amd64/iso/image-name") - self.assertEqual(Image.return_value.size, 1024) - self.assertEqual(Image.return_value.mtime, 13579) - self.assertEqual(Image.return_value.arch, "amd64") - self.assertEqual(Image.return_value.disc_number, 1) - self.assertEqual(Image.return_value.disc_count, 1) - self.assertTrue(Image.return_value.bootable) - self.assertEqual( - compose.im.add.mock_calls, - [mock.call(variant="Client", arch="amd64", image=Image.return_value)], - ) - - @mock.patch("shutil.copy2") - @mock.patch("pungi.phases.live_images.run") - @mock.patch("pungi.phases.live_images.KojiWrapper") - def test_process_handles_fail(self, KojiWrapper, run, copy2): - compose = DummyCompose( - self.topdir, {"koji_profile": "koji", "koji_cache": "/tmp"} - ) - pool = mock.Mock() - cmd = { - "ks_file": "/path/to/ks_file", - "build_arch": "amd64", - "dest_dir": "/top/iso_dir/amd64/Client", - "scratch": False, - "repos": [ - "/repo/amd64/Client", - "http://example.com/repo/", - "/repo/amd64/Everything", - ], - "label": "", - "name": None, - "filename": "image-name", - "version": None, - "specfile": None, - "ksurl": None, - "subvariant": "Client", - "release": "xyz", - "type": "live", - "failable_arches": ["*"], - "target": "f27", - } - - koji_wrapper = KojiWrapper.return_value - koji_wrapper.get_create_image_cmd.return_value = "koji spin-livecd ..." - koji_wrapper.run_blocking_cmd.return_value = { - "retcode": 1, - "output": "some output", - "task_id": 123, - } - - t = CreateLiveImageThread(pool) - with mock.patch("time.sleep"): - t.process((compose, cmd, compose.variants["Client"], "amd64"), 1) - - pool._logger.error.assert_has_calls( - [ - mock.call( - "[FAIL] Live (variant Client, arch amd64, subvariant Client) failed, but going on anyway." # noqa: E501 - ), - mock.call( - "LiveImage task failed: 123. See %s/logs/amd64/liveimage-None-None-xyz.amd64.log for more details." # noqa: E501 - % self.topdir - ), - ] - ) - - @mock.patch("shutil.copy2") - @mock.patch("pungi.phases.live_images.run") - @mock.patch("pungi.phases.live_images.KojiWrapper") - def test_process_handles_exception(self, KojiWrapper, run, copy2): - compose = DummyCompose( - self.topdir, {"koji_profile": "koji", "koji_cache": "/tmp"} - ) - pool = mock.Mock() - cmd = { - "ks_file": "/path/to/ks_file", - "build_arch": "amd64", - "dest_dir": "/top/iso_dir/amd64/Client", - "scratch": False, - "repos": [ - "/repo/amd64/Client", - "http://example.com/repo/", - "/repo/amd64/Everything", - ], - "label": "", - "name": None, - "filename": "image-name", - "version": None, - "specfile": None, - "ksurl": None, - "subvariant": "Client", - "release": "xyz", - "type": "live", - "failable_arches": ["*"], - "target": "f27", - } - - koji_wrapper = KojiWrapper.return_value - koji_wrapper.get_create_image_cmd.side_effect = boom - - t = CreateLiveImageThread(pool) - with mock.patch("time.sleep"): - t.process((compose, cmd, compose.variants["Client"], "amd64"), 1) - - pool._logger.error.assert_has_calls( - [ - mock.call( - "[FAIL] Live (variant Client, arch amd64, subvariant Client) failed, but going on anyway." # noqa: E501 - ), - mock.call("BOOM"), - ] - )