blob: 111bfc54ea1ae68187cfea7a6c23b5d3b6c83c8a [file] [log] [blame]
# Copyright 2019 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Artifacts service.
This service houses the high level business logic for all created artifacts.
"""
import collections
import glob
import logging
import os
import shutil
from typing import Dict, List, Optional, TYPE_CHECKING
from chromite.lib import autotest_util
from chromite.lib import constants
from chromite.lib import cros_build_lib
from chromite.lib import osutils
from chromite.lib import parallel
from chromite.lib import portage_util
from chromite.lib.paygen import paygen_payload_lib
from chromite.lib.paygen import paygen_provision_payload
from chromite.lib.paygen import paygen_stateful_payload_lib
if TYPE_CHECKING:
from chromite.lib import build_target_lib
from chromite.lib import chroot_lib
from chromite.lib import sysroot_lib
# Archive type constants.
ARCHIVE_CONTROL_FILES = "control"
ARCHIVE_PACKAGES = "packages"
ARCHIVE_SERVER_PACKAGES = "server_packages"
ARCHIVE_TEST_SUITES = "test_suites"
CPE_WARNINGS_FILE_TEMPLATE = "cpe-warnings-chromeos-%s.txt"
CPE_RESULT_FILE_TEMPLATE = "cpe-chromeos-%s.txt"
# The individual image archives for ArchiveImages.
IMAGE_TARS = {
constants.BASE_IMAGE_BIN: constants.BASE_IMAGE_TAR,
constants.TEST_IMAGE_BIN: constants.TEST_IMAGE_TAR,
constants.RECOVERY_IMAGE_BIN: constants.RECOVERY_IMAGE_TAR,
constants.TEST_GUEST_VM_DIR: constants.TEST_GUEST_VM_TAR,
constants.BASE_GUEST_VM_DIR: constants.BASE_GUEST_VM_TAR,
constants.KERNEL_IMAGE_BIN: constants.KERNEL_IMAGE_TAR,
constants.FLEXOR_KERNEL_IMAGE_TAR: constants.FLEXOR_KERNEL_IMAGE_TAR,
}
# Additional files under sysroot that will be archived with individual image.
IMAGE_ADDITIONAL_SYSROOT_FILES = {
constants.KERNEL_IMAGE_BIN: [
"usr/lib/debug/boot/%s" % constants.KERNEL_SYMBOL_NAME
],
}
TAST_BUNDLE_NAME = "tast_bundles.tar.bz2"
TAST_INTEL_BUNDLE_NAME = "tast_intel_bundles.tar.bz2"
TAST_COMPRESSOR = cros_build_lib.CompressionType.BZIP2
CpeResult = collections.namedtuple("CpeResult", ["report", "warnings"])
class Error(Exception):
"""Base module error."""
class ArchiveBaseDirNotFound(Error):
"""Raised when the archive base directory does not exist.
This error most likely indicates the board was not built.
"""
class CrosGenerateSysrootError(Error):
"""Error when running CrosGenerateSysroot."""
class NoFilesError(Error):
"""When there are no files to archive."""
def BuildFirmwareArchive(
chroot: "chroot_lib.Chroot",
sysroot: "sysroot_lib.Sysroot",
output_directory: str,
) -> Optional[str]:
"""Build firmware_from_source.tar.bz2 in sysroot's firmware directory.
Args:
chroot: The chroot to be used.
sysroot: The sysroot whose artifacts are being archived.
output_directory: The path were the completed archives should be put.
Returns:
The archive file path if created, None otherwise.
"""
firmware_root = chroot.full_path(sysroot.JoinPath("firmware"))
if not os.path.exists(firmware_root):
return None
# Private fingerprint libraries should not be uploaded.
private_fingerprint_dirs = glob.glob(
os.path.join(firmware_root, "**/ec-private/fingerprint"), recursive=True
)
source_list = []
for directory, _, filenames in os.walk(firmware_root):
if any(directory.startswith(e) for e in private_fingerprint_dirs):
continue
for filename in filenames:
source_list.append(
os.path.relpath(
os.path.join(directory, filename), firmware_root
)
)
if not source_list:
return None
archive_file = os.path.join(
output_directory, constants.FIRMWARE_ARCHIVE_NAME
)
cros_build_lib.CreateTarball(
archive_file,
firmware_root,
compression=cros_build_lib.CompressionType.BZIP2,
chroot=chroot.path,
inputs=source_list,
)
return archive_file
def BundleFpmcuUnittests(
chroot: "chroot_lib.Chroot",
sysroot: "sysroot_lib.Sysroot",
output_directory: "str",
) -> Optional[str]:
"""Create artifact tarball for fingerprint MCU on-device unittests.
Args:
chroot: The chroot containing the sysroot.
sysroot: The sysroot whose artifacts are being archived.
output_directory: The path were the completed archives should be put.
Returns:
The archive file path if created, None otherwise.
"""
fpmcu_unittests_root = chroot.full_path(
sysroot.JoinPath(
"firmware",
"chromeos-fpmcu-unittests",
)
)
files = [
os.path.relpath(f, fpmcu_unittests_root)
for f in glob.iglob(os.path.join(fpmcu_unittests_root, "*"))
]
if not files:
return None
archive_file = os.path.join(
output_directory, constants.FPMCU_UNITTESTS_ARCHIVE_NAME
)
cros_build_lib.CreateTarball(
archive_file,
fpmcu_unittests_root,
compression=cros_build_lib.CompressionType.BZIP2,
chroot=chroot.path,
inputs=files,
)
return archive_file
def BundleAutotestFiles(
chroot: "chroot_lib.Chroot",
sysroot: "sysroot_lib.Sysroot",
output_directory: str,
) -> Dict[str, str]:
"""Create the Autotest Hardware Test archives.
Args:
chroot: The chroot containing the sysroot.
sysroot: The sysroot whose artifacts are being archived.
output_directory: The path were the completed archives should be put.
Returns:
The paths of the files created in |output_directory| by their type.
"""
assert sysroot.Exists(chroot=chroot)
assert output_directory
logging.debug(
"Inside artifacts_service BundleAutotestFiles (%s %s %s)",
chroot.path,
sysroot.path,
output_directory,
)
# archive_basedir is the base directory where the archive commands are run.
# We want the folder containing the board's autotest folder.
archive_basedir = chroot.full_path(
sysroot.path, constants.AUTOTEST_BUILD_PATH
)
archive_basedir = os.path.dirname(archive_basedir)
if not os.path.exists(archive_basedir):
return {}
builder = autotest_util.AutotestTarballBuilder(
archive_basedir, output_directory, chroot, sysroot
)
results = {
ARCHIVE_CONTROL_FILES: builder.BuildAutotestControlFilesTarball(),
ARCHIVE_PACKAGES: builder.BuildAutotestPackagesTarball(),
ARCHIVE_SERVER_PACKAGES: builder.BuildAutotestServerPackageTarball(),
ARCHIVE_TEST_SUITES: builder.BuildAutotestTestSuitesTarball(),
}
# Strip the list down to just the successfully created archives.
return {k: v for k, v in results.items() if v}
def BundleEBuildLogsTarball(
chroot: "chroot_lib.Chroot",
sysroot: "sysroot_lib.Sysroot",
archive_dir: str,
) -> Optional[str]:
"""Builds a tarball containing ebuild logs.
Args:
chroot: The chroot to be used.
sysroot: Sysroot whose images are being fetched.
archive_dir: The directory to drop the tarball in.
Returns:
The file name of the output tarball, None if no package found.
"""
tarball_paths = []
logs_path = chroot.full_path(sysroot.path, "tmp/portage")
if not os.path.isdir(logs_path):
return None
if not os.path.exists(os.path.join(logs_path, "logs")):
return None
tarball_paths.append("logs")
tarball_output = os.path.join(archive_dir, "ebuild_logs.tar.xz")
try:
cros_build_lib.CreateTarball(
tarball_output,
cwd=logs_path,
chroot=chroot.path,
inputs=tarball_paths,
)
except cros_build_lib.TarballError:
logging.warning(
"Unable to create logs tarball; ignoring until "
"https://crbug.com/999933 is sorted out."
)
return None
return os.path.basename(tarball_output)
def BundleChromeOSConfig(
chroot: "chroot_lib.Chroot",
sysroot: "sysroot_lib.Sysroot",
archive_dir: str,
) -> Optional[str]:
"""Outputs the ChromeOS Config payload.
Args:
chroot: The chroot to be used.
sysroot: Sysroot whose config is being fetched.
archive_dir: The directory to drop the config in.
Returns:
The file name of the output config, None if no config found.
"""
config_path = chroot.full_path(
sysroot.path, "usr/share/chromeos-config/yaml/config.yaml"
)
if not os.path.exists(config_path):
return None
config_output = os.path.join(archive_dir, "config.yaml")
shutil.copy(config_path, config_output)
return os.path.basename(config_output)
def BundleSimpleChromeArtifacts(
chroot: "chroot_lib.Chroot",
sysroot: "sysroot_lib.Sysroot",
build_target: "build_target_lib.BuildTarget",
output_dir: str,
) -> List[str]:
"""Gather all of the simple chrome artifacts.
Args:
chroot: The chroot to be used.
sysroot: The sysroot.
build_target: The sysroot's build target.
output_dir: Where all result files should be stored.
"""
files = []
files.extend(CreateChromeRoot(chroot, build_target, output_dir))
files.append(ArchiveChromeEbuildEnv(sysroot, output_dir))
return files
def BundleVmFiles(
chroot: "chroot_lib.Chroot", test_results_dir: str, output_dir: str
) -> List[str]:
"""Gather all of the VM files.
Args:
chroot: The chroot to be used.
test_results_dir: Test directory relative to chroot.
output_dir: Where all result files should be stored.
"""
image_dir = chroot.full_path(test_results_dir)
archives = ArchiveFilesFromImageDir(image_dir, output_dir)
return archives
# TODO(mmortensen): Refactor ArchiveFilesFromImageDir to be part of a library
# module. I tried moving it to lib/vm.py but this causes a circular dependency.
def ArchiveFilesFromImageDir(images_dir: str, archive_path: str) -> List[str]:
"""Archives the files into tarballs if they match a prefix from prefix_list.
Create and return a list of tarballs from the images_dir of files that match
VM disk and memory prefixes.
Args:
images_dir: The directory containing the images to archive.
archive_path: The directory where the archives should be created.
Returns:
The paths to the tarballs.
"""
images = []
tar_files = []
for image_path in images:
image_rel_path = os.path.relpath(image_path, images_dir)
image_parent_dir = os.path.dirname(image_path)
image_file = os.path.basename(image_path)
tarball_path = os.path.join(
archive_path, "%s.tar" % image_rel_path.replace("/", "_")
)
# Note that tar will chdir to |image_parent_dir|, so that |image_file|
# is at the top-level of the tar file.
cros_build_lib.CreateTarball(
tarball_path,
image_parent_dir,
compression=cros_build_lib.CompressionType.BZIP2,
inputs=[image_file],
)
tar_files.append(tarball_path)
return tar_files
def ArchiveChromeEbuildEnv(
sysroot: "sysroot_lib.Sysroot", output_dir: str
) -> str:
"""Generate Chrome ebuild environment.
Args:
sysroot: The sysroot where the original environment archive can be
found.
output_dir: Where the result should be stored.
Returns:
The path to the archive.
Raises:
NoFilesException: When the package cannot be found.
"""
pkg_dir = os.path.join(sysroot.path, portage_util.VDB_PATH)
files = glob.glob(os.path.join(pkg_dir, constants.CHROME_CP) + "-*")
if not files:
raise NoFilesError("Failed to find package %s" % constants.CHROME_CP)
if len(files) > 1:
logging.warning(
"Expected one package for %s, found %d",
constants.CHROME_CP,
len(files),
)
chrome_dir = sorted(files)[-1]
env_bzip = os.path.join(chrome_dir, "environment.bz2")
result_path = os.path.join(output_dir, constants.CHROME_ENV_TAR)
with osutils.TempDir() as tempdir:
# Convert from bzip2 to tar format.
bzip2 = cros_build_lib.FindCompressor(
cros_build_lib.CompressionType.BZIP2
)
tempdir_tar_path = os.path.join(tempdir, constants.CHROME_ENV_FILE)
cros_build_lib.run(
[bzip2, "-d", env_bzip, "-c"], stdout=tempdir_tar_path
)
cros_build_lib.CreateTarball(result_path, tempdir)
return result_path
def ArchiveImages(
chroot: "chroot_lib.Chroot",
sysroot: "sysroot_lib.Sysroot",
image_dir: str,
output_dir: str,
) -> List[str]:
"""Create a .tar.xz archive for each image that has been created.
This copies already compressed images to output_dir.
Args:
chroot: The chroot containing the sysroot.
sysroot: The sysroot whose artifacts are being archived.
image_dir: The directory where the images are located.
output_dir: The location where the archives should be created.
Returns:
The list of created file names.
"""
files = os.listdir(image_dir)
archives = []
# Filter down to the ones that exist first.
images = {img: tar for img, tar in IMAGE_TARS.items() if img in files}
for img, tar in images.items():
tarball_path = os.path.join(output_dir, tar)
content = [img]
extra_args = []
if img in IMAGE_ADDITIONAL_SYSROOT_FILES:
additional_files = {
full_path
for f in IMAGE_ADDITIONAL_SYSROOT_FILES[img]
if os.path.isfile(
full_path := chroot.full_path(sysroot.path, f)
)
}
content.extend(additional_files)
# Remove path folders created for additional files
extra_args.append("--transform=s#.*/##")
if img == constants.FLEXOR_KERNEL_IMAGE_TAR:
# Flexor is already compressed, so copy it.
image_path = os.path.join(image_dir, img)
shutil.copy(image_path, tarball_path)
else:
# Otherwise create a tarball.
cros_build_lib.CreateTarball(
tarball_path,
image_dir,
inputs=content,
print_cmd=False,
extra_args=extra_args,
)
archives.append(tar)
return archives
def BundleImageZip(output_dir: str, image_dir: str) -> str:
"""Bundle image.zip.
Args:
output_dir: The location outside the chroot where the files should be
stored.
image_dir: The directory containing the image.
"""
filename = "image.zip"
zipfile = os.path.join(output_dir, filename)
cros_build_lib.run(
["7za", "a", "-tzip", "-mm=Deflate", "-mx=1", "-mmt=on", zipfile, "."],
cwd=image_dir,
capture_output=True,
)
return filename
def CreateChromeRoot(
chroot: "chroot_lib.Chroot",
build_target: "build_target_lib.BuildTarget",
output_dir: str,
) -> List[str]:
"""Create the chrome sysroot.
Args:
chroot: The chroot in which the sysroot should be built.
build_target: The build target.
output_dir: The location outside the chroot where the files should be
stored.
Returns:
The list of created files.
Raises:
CrosGenerateSysrootError: When cros_generate_sysroot does not complete
successfully.
"""
extra_env = {"USE": "chrome_internal"}
with chroot.tempdir() as tempdir:
in_chroot_path = chroot.chroot_path(tempdir)
cmd = [
"cros_generate_sysroot",
"--out-dir",
in_chroot_path,
"--board",
build_target.name,
"--deps-only",
"--package",
constants.CHROME_CP,
]
try:
chroot.run(
cmd,
extra_env=extra_env,
)
except cros_build_lib.RunCommandError as e:
raise CrosGenerateSysrootError(
"Error encountered when running cros_generate_sysroot: %s" % e,
e,
)
files = []
for path in osutils.DirectoryIterator(tempdir):
if os.path.isfile(path):
rel_path = os.path.relpath(path, tempdir)
files.append(os.path.join(output_dir, rel_path))
osutils.CopyDirContents(tempdir, output_dir, allow_nonempty=True)
return files
def BundleTestUpdatePayloads(
chroot: "chroot_lib.Chroot", image_path: str, output_dir: str
) -> List[str]:
"""Generate the test update payloads.
Args:
chroot: Chroot we're operating with.
image_path: The full path to an image file.
output_dir: The path where the payloads should be generated.
Returns:
The list of generated payloads.
"""
payloads = GenerateTestPayloads(
chroot,
image_path,
output_dir,
full=True,
stateful=True,
delta=True,
dlc=True,
)
payloads.extend(
paygen_provision_payload.GenerateProvisionPayloads(
image_path, output_dir
)
)
return payloads
def ExtendBinPaths(cros_payload_path: str) -> List[str]:
"""Return an array with corresponding .json and .log for a .bin."""
return [
cros_payload_path,
cros_payload_path + ".json",
cros_payload_path + ".log",
]
def GenerateTestPayloads(
chroot: "chroot_lib.Chroot",
target_image_path: str,
archive_dir: str,
full: bool = False,
delta: bool = False,
stateful: bool = False,
dlc: bool = False,
) -> List[str]:
"""Generates the payloads for hw testing.
Args:
chroot: The chroot to be used.
target_image_path: The path to the image to generate payloads to.
archive_dir: Where to store payloads we generated.
full: Generate full payloads.
delta: Generate delta payloads.
stateful: Generate stateful payload.
dlc: Generate sample-dlc payload if available.
Returns:
The list of artifacts that were generated.
"""
real_target = os.path.realpath(target_image_path)
# The path to the target should look something like this:
# .../link/R37-5952.0.2014_06_12_2302-a1/chromiumos_test_image.bin
board, os_version = real_target.split("/")[-3:-1]
cros_prefix = "chromeos"
minios_prefix = "minios"
suffix = "dev.bin"
# DLC constants.
dlc_prefix = "dlc"
dlc_id = "sample-dlc"
dlc_package = "package"
sample_dlc_image = os.path.join(
os.path.dirname(target_image_path),
dlc_prefix,
dlc_id,
dlc_package,
"dlc.img",
)
def _do_full():
# Names for full payloads look something like this:
# chromeos_R37-5952.0.2014_06_12_2302-a1_link_full_dev.bin
cros_name = "_".join([cros_prefix, os_version, board, "full", suffix])
cros_payload_path = os.path.join(archive_dir, cros_name)
result_paths = paygen_payload_lib.GenerateUpdatePayload(
chroot, target_image_path, cros_payload_path
)
if not result_paths:
logging.info("CrOS full payload generation skipped.")
return sum((ExtendBinPaths(path) for path in result_paths), [])
def _do_full_minios():
# Names for full payloads look something like this:
# minios_R37-5952.0.2014_06_12_2302-a1_link_full_dev.bin
minios_name = "_".join(
[minios_prefix, os_version, board, "full", suffix]
)
minios_payload_path = os.path.join(archive_dir, minios_name)
result_paths = paygen_payload_lib.GenerateUpdatePayload(
chroot, target_image_path, minios_payload_path, minios=True
)
if not result_paths:
logging.info("MiniOS full payload generation skipped.")
return sum((ExtendBinPaths(path) for path in result_paths), [])
def _do_delta():
# Names for delta payloads look something like this:
# chromeos_R37-5952.0.2014_06_12_2302-a1_R37-
# 5952.0.2014_06_12_2302-a1_link_delta_dev.bin
cros_name = "_".join(
[cros_prefix, os_version, os_version, board, "delta", suffix]
)
cros_payload_path = os.path.join(archive_dir, cros_name)
result_paths = paygen_payload_lib.GenerateUpdatePayload(
chroot,
target_image_path,
cros_payload_path,
src_image=target_image_path,
)
if not result_paths:
logging.info("CrOS delta payload generation skipped.")
return sum((ExtendBinPaths(path) for path in result_paths), [])
def _do_delta_minios():
# Names for delta payloads look something like this:
# minios_R37-5952.0.2014_06_12_2302-a1_R37-
# 5952.0.2014_06_12_2302-a1_link_delta_dev.bin
minios_name = "_".join(
[minios_prefix, os_version, os_version, board, "delta", suffix]
)
minios_payload_path = os.path.join(archive_dir, minios_name)
result_paths = paygen_payload_lib.GenerateUpdatePayload(
chroot,
target_image_path,
minios_payload_path,
src_image=target_image_path,
minios=True,
)
if not result_paths:
logging.info("MiniOS delta payload generation skipped.")
return sum((ExtendBinPaths(path) for path in result_paths), [])
def _do_full_dlc():
# pylint: disable=line-too-long
# Names for full sample-dlc payloads look something like this:
# dlc_sample-dlc_package_R37-5952.0.2014_06_12_2302-a1_link_full_dev.bin
# pylint: enable=line-too-long
name = "_".join(
[
dlc_prefix,
dlc_id,
dlc_package,
os_version,
board,
"full",
suffix,
]
)
payload_path = os.path.join(archive_dir, name)
result_paths = paygen_payload_lib.GenerateUpdatePayload(
chroot, sample_dlc_image, payload_path
)
if not result_paths:
logging.info("DLC (%s) full payload generation skipped.", dlc_id)
return sum((ExtendBinPaths(path) for path in result_paths), [])
def _do_delta_dlc():
# Names for delta payloads look something like this:
# dlc_sample-dlc_package_R37-5952.0.2014_06_12_2302-a1_R37-
# 5952.0.2014_06_12_2302-a1_link_delta_dev.bin
name = "_".join(
[
dlc_prefix,
dlc_id,
dlc_package,
os_version,
os_version,
board,
"delta",
suffix,
]
)
payload_path = os.path.join(archive_dir, name)
result_paths = paygen_payload_lib.GenerateUpdatePayload(
chroot, sample_dlc_image, payload_path, src_image=sample_dlc_image
)
if not result_paths:
logging.info("DLC (%s) delta payload generation skipped.", dlc_id)
return sum((ExtendBinPaths(path) for path in result_paths), [])
def _do_stateful():
return [
paygen_stateful_payload_lib.GenerateStatefulPayload(
target_image_path, archive_dir
),
paygen_stateful_payload_lib.GenerateZstdStatefulPayload(
target_image_path, archive_dir
),
]
steps = []
if full:
steps.append(_do_full)
steps.append(_do_full_minios)
if delta:
steps.append(_do_delta)
steps.append(_do_delta_minios)
if dlc and "dlc" in portage_util.GetBoardUseFlags(board):
if full:
steps.append(_do_full_dlc)
if delta:
steps.append(_do_delta_dlc)
if stateful:
steps.append(_do_stateful)
# In theory we should do them all in parallel, this resulted in runtimes
# around 7 minutes on builders. We have space constraints and this is
# taking up 80ish GB at peak while running fully parallel. Until we can
# land some mitigation, reduce the parallelism at the expense of runtime.
# BUG=b:273941464.
gen_files = parallel.RunParallelSteps(
steps, return_values=True, max_parallel=2
)
return [i for sl in gen_files for i in sl]
def BundleTastFiles(
chroot: "chroot_lib.Chroot", sysroot: "sysroot_lib.Sysroot", output_dir: str
) -> Optional[str]:
"""Tar up the Tast private test bundles.
Args:
chroot: Chroot containing the sysroot.
sysroot: Sysroot whose files are being archived.
output_dir: Location for storing the result tarball.
Returns:
Path of the generated tarball, or None if there is no private test
bundles.
"""
cwd = chroot.full_path(sysroot.JoinPath("build"))
dirs = []
srcs = [
"libexec/tast/bundles/local/crosint",
"share/tast/metadata/local/crosint.pb",
"share/tast/data/go.chromium.org/tast-tests-private",
]
for d in srcs:
if os.path.exists(os.path.join(cwd, d)):
dirs.append(d)
if not dirs:
return None
tarball = os.path.join(output_dir, TAST_BUNDLE_NAME)
cros_build_lib.CreateTarball(
tarball,
cwd,
compression=TAST_COMPRESSOR,
chroot=chroot.path,
inputs=dirs,
)
return tarball
def BundleTastIntelFiles(
chroot: "chroot_lib.Chroot", sysroot: "sysroot_lib.Sysroot", output_dir: str
) -> Optional[str]:
"""Tar up the Tast Intel private test bundles.
Args:
chroot: Chroot containing the sysroot.
sysroot: Sysroot whose files are being archived.
output_dir: Location for storing the result tarball.
Returns:
Path of the generated tarball, or None if there is no private test
bundles.
"""
cwd = chroot.full_path(sysroot.JoinPath("build"))
dirs = []
srcs = [
"libexec/tast/bundles/local/crosint_intel",
"share/tast/metadata/local/crosint_intel.pb",
"share/tast/data/go.chromium.org/partner-intel-private/",
]
for d in srcs:
if os.path.exists(os.path.join(cwd, d)):
dirs.append(d)
if not dirs:
return None
tarball = os.path.join(output_dir, TAST_INTEL_BUNDLE_NAME)
cros_build_lib.CreateTarball(
tarball,
cwd,
compression=TAST_COMPRESSOR,
chroot=chroot.path,
inputs=dirs,
)
return tarball
def BundleGceTarball(output_dir: str, image_dir: str) -> str:
"""Bundle the test image into a tarball suitable for importing into GCE.
Args:
output_dir: The location outside the chroot where the files should be
stored.
image_dir: The directory containing the image.
Returns:
Path to the generated tarball.
"""
test_image = os.path.join(image_dir, constants.TEST_IMAGE_BIN)
tarball = os.path.join(output_dir, constants.TEST_IMAGE_GCE_TAR)
with osutils.TempDir() as tempdir:
disk_raw = os.path.join(tempdir, "disk.raw")
osutils.SafeSymlink(test_image, disk_raw)
cros_build_lib.CreateTarball(
tarball,
tempdir,
compression=cros_build_lib.CompressionType.GZIP,
inputs=("disk.raw",),
extra_args=["--dereference", "--format=oldgnu"],
)
return tarball