9d02f87c99
When a compose doesn't build any images, it won't produce any metadata file for them, and thus it makes no sense to validate it. Signed-off-by: Lubomír Sedlář <lsedlar@redhat.com> Fixes: https://pagure.io/pungi/issue/1565
135 lines
4.3 KiB
Python
135 lines
4.3 KiB
Python
# -*- 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 <https://gnu.org/licenses/>.
|
|
|
|
|
|
import os
|
|
|
|
from pungi.phases.base import PhaseBase
|
|
from pungi.util import failable, get_arch_variant_data
|
|
import productmd.compose
|
|
|
|
|
|
class TestPhase(PhaseBase):
|
|
name = "test"
|
|
|
|
def run(self):
|
|
check_image_sanity(self.compose)
|
|
check_image_metadata(self.compose)
|
|
|
|
|
|
def check_image_sanity(compose):
|
|
"""
|
|
Go through all images in manifest and make basic sanity tests on them. If
|
|
any check fails for a failable deliverable, a message will be printed and
|
|
logged. Otherwise the compose will be aborted.
|
|
"""
|
|
im = compose.im
|
|
for variant in compose.get_variants():
|
|
if variant.uid not in im.images:
|
|
continue
|
|
for arch in variant.arches:
|
|
if arch not in im.images[variant.uid]:
|
|
continue
|
|
for img in im.images[variant.uid][arch]:
|
|
check_sanity(compose, variant, arch, img)
|
|
check_size_limit(compose, variant, arch, img)
|
|
|
|
|
|
def check_image_metadata(compose):
|
|
"""
|
|
Check the images metadata for entries that cannot be serialized.
|
|
Often caused by isos with duplicate metadata.
|
|
Accessing the `images` attribute will raise an exception if there's a problem
|
|
"""
|
|
if compose.im.images:
|
|
compose = productmd.compose.Compose(compose.paths.compose.topdir())
|
|
return compose.images
|
|
|
|
|
|
def check_sanity(compose, variant, arch, image):
|
|
path = os.path.join(compose.paths.compose.topdir(), image.path)
|
|
deliverable = getattr(image, "deliverable")
|
|
can_fail = getattr(image, "can_fail", False)
|
|
with failable(
|
|
compose, can_fail, variant, arch, deliverable, subvariant=image.subvariant
|
|
):
|
|
with open(path, "rb") as f:
|
|
iso = is_iso(f)
|
|
if image.format == "iso" and not iso:
|
|
raise RuntimeError("%s does not look like an ISO file" % path)
|
|
if (
|
|
image.arch in ("x86_64", "i386")
|
|
and image.bootable
|
|
and not has_mbr(f)
|
|
and not has_gpt(f)
|
|
and not (iso and has_eltorito(f))
|
|
):
|
|
raise RuntimeError(
|
|
"%s is supposed to be bootable, but does not have MBR nor "
|
|
"GPT nor is it a bootable ISO" % path
|
|
)
|
|
# If exception is raised above, failable may catch it, in which case
|
|
# nothing else will happen.
|
|
|
|
|
|
def _check_magic(f, offset, bytes):
|
|
"""Check that the file has correct magic number at correct offset."""
|
|
f.seek(offset)
|
|
return f.read(len(bytes)) == bytes
|
|
|
|
|
|
def is_iso(f):
|
|
return _check_magic(f, 0x8001, b"CD001")
|
|
|
|
|
|
def has_mbr(f):
|
|
return _check_magic(f, 0x1FE, b"\x55\xAA")
|
|
|
|
|
|
def has_gpt(f):
|
|
return _check_magic(f, 0x200, b"EFI PART")
|
|
|
|
|
|
def has_eltorito(f):
|
|
return _check_magic(f, 0x8801, b"CD001\1EL TORITO SPECIFICATION")
|
|
|
|
|
|
def check_size_limit(compose, variant, arch, img):
|
|
"""If a size of the ISO image is over the configured limit, report a
|
|
warning. Do nothing for other types of images.
|
|
"""
|
|
if img.format != "iso":
|
|
return
|
|
limits = get_arch_variant_data(compose.conf, "createiso_max_size", arch, variant)
|
|
if not limits and not getattr(img, "_max_size", None):
|
|
return
|
|
# For ISOs created in extra_isos phase we add an attribute with the limit,
|
|
# and there is a global option otherwise.
|
|
limit = getattr(img, "_max_size", None) or limits[0]
|
|
|
|
if img.size > limit:
|
|
is_strict = get_arch_variant_data(
|
|
compose.conf, "createiso_max_size_is_strict", arch, variant
|
|
)
|
|
msg = "ISO %s is too big. Expected max %dB, got %dB" % (
|
|
img.path,
|
|
limit,
|
|
img.size,
|
|
)
|
|
if any(is_strict):
|
|
raise RuntimeError(msg)
|
|
else:
|
|
compose.log_warning(msg)
|