blob: ef660a1a0229b537de641bbee9b8546b4db3e58c [file] [log] [blame]
# -*- coding: utf-8 -*-
# Copyright 2019 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""The payload API is the entry point for payload functionality."""
from __future__ import print_function
from copy import deepcopy
import os
from chromite.lib import chroot_util
from chromite.lib.paygen import gspaths
from chromite.lib.paygen import paygen_payload_lib
from chromite.api.gen.chromiumos import common_pb2
from chromite.api.gen.chromite.api import payload_pb2
class Error(Exception):
"""Base module error."""
class ImageTypeUnknownError(Error):
"""An error raised when image type is unknown."""
class ImageMismatchError(Error):
"""An error raised when src and tgt aren't compatible."""
class PayloadConfig(object):
"""Value object to hold the GeneratePayload configuration options."""
def __init__(self,
tgt_image=None,
src_image=None,
dest_bucket=None,
verify=True,
keyset=None,
upload=True,
cache_dir=None):
"""Init method, sets up all the paths and configuration.
Args:
tgt_image (UnsignedImage, SignedImage, or DLCImage):
Proto for destination image.
src_image (UnsignedImage, SignedImage, DLCImage, or None):
Proto for source image.
dest_bucket (str): Destination bucket to place the final artifacts in.
verify (bool): If delta is made, verify the integrity of the payload.
keyset (str): The key to sign the image with.
upload (bool): Whether the payload generation results should be uploaded.
cache_dir (str): The cache dir for paygen to use or None for default.
"""
# Set when we call GeneratePayload on this object.
self.paygen = None
self.tgt_image = tgt_image
self.src_image = src_image
self.dest_bucket = dest_bucket
self.verify = verify
self.keyset = keyset
self.upload = upload
self.delta_type = 'delta' if self.src_image else 'full'
self.image_type = _ImageTypeToStr(tgt_image.image_type)
self.cache_dir = cache_dir
# This block ensures that we have paths to the correct perm of images.
src_image_path = None
if isinstance(self.tgt_image, payload_pb2.UnsignedImage):
tgt_image_path = _GenUnsignedGSPath(self.tgt_image, self.image_type)
elif isinstance(self.tgt_image, payload_pb2.SignedImage):
tgt_image_path = _GenSignedGSPath(self.tgt_image, self.image_type)
elif isinstance(self.tgt_image, payload_pb2.DLCImage):
tgt_image_path = _GenDLCImageGSPath(self.tgt_image)
if self.delta_type == 'delta':
if isinstance(self.tgt_image, payload_pb2.UnsignedImage):
src_image_path = _GenUnsignedGSPath(self.src_image, self.image_type)
if isinstance(self.tgt_image, payload_pb2.SignedImage):
src_image_path = _GenSignedGSPath(self.src_image, self.image_type)
elif isinstance(self.tgt_image, payload_pb2.DLCImage):
src_image_path = _GenDLCImageGSPath(self.src_image)
# Set your output location.
if self.upload:
payload_build = deepcopy(tgt_image_path.build)
payload_build.bucket = dest_bucket
payload_output_uri = gspaths.ChromeosReleases.PayloadUri(
build=payload_build,
random_str=None,
key=self.keyset,
src_version=src_image_path.build.version if src_image else None,
)
else:
payload_output_uri = None
self.payload = gspaths.Payload(
tgt_image=tgt_image_path, src_image=src_image_path,
uri=payload_output_uri)
def GeneratePayload(self):
"""Do payload generation (& maybe sign) on Google Storage CrOS images.
Returns:
A tuple of (string, string) containing:
The location of the local generated artifact.
(e.g. /tmp/wdjaio/delta.bin)
The remote location that the payload was uploaded or None.
(e.g. 'gs://cr/beta-channel/coral/12345.0.1/payloads/...')
"""
should_sign = self.keyset != ''
# Leave the generated artifact local. This is ok because if we're testing
# it's likely we want the artifact anyway, and in production this is ran on
# single shot bots in the context of an overlayfs and will get cleaned up
# anyway.
with chroot_util.TempDirInChroot(delete=False) as temp_dir:
self.paygen = paygen_payload_lib.PaygenPayload(
self.payload,
temp_dir,
sign=should_sign,
verify=self.verify,
upload=self.upload,
cache_dir=self.cache_dir)
# We run and expect failures to raise, so if we get passed
# self.paygen.Run() then it's safe to assume the bin is in place.
local_path = os.path.join(temp_dir, 'delta.bin')
remote_uri = self.paygen.Run()
return (local_path, remote_uri)
class GeneratePayloadResult(object):
"""Value object to report GeneratePayload results."""
def __init__(self, return_code):
"""Initialize a GeneratePayloadResult.
Args:
return_code (bool): The return code of the GeneratePayload operation.
"""
self.success = return_code == 0
def _ImageTypeToStr(image_type_n):
"""The numeral image type enum in proto to lowercase string."""
return common_pb2.ImageType.Name(image_type_n).lower()
def _GenSignedGSPath(image, image_type):
"""Take a SignedImage_pb2 and return a gspaths.Image.
Args:
image (SignedImage_pb2): The build to create the gspath from.
image_type (string): The image type, either "recovery" or "base".
Returns:
A gspaths.Image instance.
"""
build = gspaths.Build(board=image.build.build_target.name,
version=image.build.version,
channel=image.build.channel,
bucket=image.build.bucket)
build_uri = gspaths.ChromeosReleases.ImageUri(
build, image.key, image_type)
build.uri = build_uri
return gspaths.Image(build=build,
image_type=image_type,
uri=build_uri)
def _GenUnsignedGSPath(image, image_type):
"""Take an UnsignedImage_pb2 and return a gspaths.UnsignedImageArchive.
Args:
image (UnsignedImage_pb2): The build to create the gspath from.
image_type (string): The image type, either "recovery" or "test".
Returns:
A gspaths.UnsignedImageArchive instance.
"""
build = gspaths.Build(board=image.build.build_target.name,
version=image.build.version,
channel=image.build.channel,
bucket=image.build.bucket)
build_uri = gspaths.ChromeosReleases.UnsignedImageUri(
build, image.milestone, image_type)
build.uri = build_uri
return gspaths.UnsignedImageArchive(build=build,
milestone=image.milestone,
image_type=image_type,
uri=build_uri)
def _GenDLCImageGSPath(image):
"""Take a DLCImage_pb2 and return a gspaths.DLCImage.
Args:
image (DLCImage_pb2): The dlc image to create the gspath from.
Returns:
A gspaths.DLCImage instance.
"""
build = gspaths.Build(
board=image.build.build_target.name,
version=image.build.version,
channel=image.build.channel,
bucket=image.build.bucket)
dlc_image_uri = gspaths.ChromeosReleases.DLCImageUri(build, image.dlc_id,
image.dlc_package,
image.dlc_image)
return gspaths.DLCImage(
build=build,
image_type=image.image_type,
key='',
uri=dlc_image_uri,
dlc_id=image.dlc_id,
dlc_package=image.dlc_package,
dlc_image=image.dlc_image)