From b821b6604096369479b6ee197e5f68e2c7c1a89a Mon Sep 17 00:00:00 2001 From: Ben Howard Date: Tue, 4 Feb 2020 10:23:19 -0700 Subject: [PATCH] qemuvariants: fix regression in GCP tarball format Fixes BZ #1796632. GCP is picky about the tarball contents. They must: - be in a tarball with gzip compression - use the Old GNU format - be sized correctly - members must be sized right With the new method of generating qemu variant images, CLI tar commands were replaced with pythonic methods; this produces invalid tarballs from GCP's perspective. To support GCP, the method of tarball creation is reverted to using the CLI. --- src/cosalib/qemuvariants.py | 55 +++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/src/cosalib/qemuvariants.py b/src/cosalib/qemuvariants.py index 17536dd021..22b60885c6 100644 --- a/src/cosalib/qemuvariants.py +++ b/src/cosalib/qemuvariants.py @@ -6,7 +6,6 @@ import os.path import shutil import sys -import tarfile cosa_dir = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, f"{cosa_dir}/cosalib") @@ -26,6 +25,15 @@ # BASEARCH is the current machine architecture BASEARCH = get_basearch() +# Default flags for the creation of tarfiles +# The default flags were selected because: +# -S: always create a sparse file +# -c: create a new tarball +# -h: derefence symlinks +# These flags were selected from prior commits for +# tarball creation. +DEFAULT_TAR_FLAGS = '-Sch' + # Variant are disk types that are derived from qemu images. # To define new variants that use the QCOW2 disk image, simply, # add its definition below: @@ -52,11 +60,20 @@ "platform": "openstack", }, "gcp": { + # See https://cloud.google.com/compute/docs/import/import-existing-image#requirements_for_the_image_file "image_format": "raw", "platform": "gcp", "image_suffix": "tar.gz", + "convert_options": { + '-o': 'preallocation=off' + }, "tar_members": [ "disk.raw" + ], + "tar_flags": [ + DEFAULT_TAR_FLAGS, + "-z", + "--format=oldgnu" ] }, "vmware_vmdk": { @@ -117,6 +134,7 @@ def __init__(self, *args, **kwargs): self.platform = kwargs.pop("platform", "qemu") self.force = kwargs.get("force", False) self.tar_members = kwargs.pop("tar_members", None) + self.tar_flags = kwargs.pop("tar_flags", [DEFAULT_TAR_FLAGS]) # this is used in case the image has a different disk # name than the platform @@ -191,22 +209,25 @@ def mutate_image(self, callback=None): callback(work_img) if self.tar_members: - wmode = 'w' - if final_img.endswith('gz'): - wmode = 'w:gz' - elif final_img.endswith('xz'): - wmode = 'w:xz' - log.info(f"Preparing tarball with mode '{wmode}': {final_img}") - with tarfile.open(final_img, wmode) as tar: - base_disk = self.tar_members.pop(0) - base_name = os.path.basename(base_disk) - log.info(f" - adding base disk '{work_img}' as '{base_name}'") - tar.add(work_img, arcname=base_name) - - for te in self.tar_members: - te_name = os.path.basename(te) - log.info(f" - adding additional file: {te_name}") - tar.add(te, arcname=te_name) + # Python does not create sparse Tarfiles, so we have do it via + # the CLI here. + base_name = self.tar_members.pop(0) + tarlist = [base_name] + # in the case of several clouds, the disk is named `disk.raw` or + # `disk.vmdk`. When creating a tarball, we rename the disk to + # the in-tar name if the name does not match the defaulting naming. + if base_name != os.path.basename(work_img): + os.rename(work_img, os.path.join(self._tmpdir, base_name)) + + for member in self.tar_members: + member_name = os.path.basename(member) + tarlist.append(member_name) + + tar_cmd = ['tar', '-C', self._tmpdir] + tar_cmd.extend(self.tar_flags) + tar_cmd.extend(['-f', final_img]) + tar_cmd.extend(tarlist) + run_verbose(tar_cmd) else: log.info(f"Moving {work_img} to {final_img}")