| # Copyright 2012 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Centralize knowledge about how to create standardized Google Storage paths. |
| |
| This includes definitions for various build flags: |
| |
| LOCK - means that payload processing is in progress on the host which |
| owns the locks. Locks have a timeout associated with them in |
| case of error, but are not 100% atomic when a lock is timing out. |
| |
| Example file paths: |
| gs://chromeos-releases/blah-channel/board-name/1.2.3/payloads/LOCK_flag |
| """ |
| |
| from enum import Enum |
| import logging |
| import os |
| import re |
| from typing import Optional |
| |
| from chromite.lib import cros_build_lib |
| from chromite.lib.paygen import utils |
| |
| |
| class OSType(Enum): |
| """Define different OS types""" |
| |
| CROS = 1 |
| MINIOS = 2 |
| |
| |
| class Build(utils.RestrictedAttrDict): |
| """Define a ChromeOS Build. |
| |
| The order of attributes in self._slots dictates the order attributes |
| are printed in by __str__ method of super class. Keep the attributes |
| that are more helpful in identifying this build earlier in the list, |
| because this string ends up cut off in email subjects. |
| |
| Fields: |
| board: The board of the image "x86-mario", etc. |
| bucket: The bucket of the image. "chromeos-releases" as default. |
| channel: The channel of the image "stable-channel", "nplusone", etc. |
| uri: The URI of the build directory. |
| version: The version of the image. "0.14.23.2", "3401.0.0", etc. |
| """ |
| |
| _slots = ("board", "version", "channel", "bucket", "uri") |
| _name = "Build definition" |
| |
| @staticmethod |
| def BuildValuesFromUri(uri_re, uri): |
| """Builds a dictionary from a URI using a regular expression. |
| |
| In addition, it removes the 'board', 'version', 'channel', and 'bucket' |
| keys and replaces them with on Build object. |
| |
| Args: |
| uri_re: A regular expression to match with the given URI. |
| uri: The URI to match against the regular expression. |
| |
| Returns: |
| A dictionary containing all the necessary files or None if it could |
| not match the URI against the regular expression. |
| """ |
| m = re.match(uri_re, uri) |
| if not m: |
| return None |
| values = m.groupdict() |
| |
| # Replace Build values with Build object. |
| build_keys = ("bucket", "channel", "board", "version") |
| values["build"] = Build({key: values[key] for key in build_keys}) |
| for key in build_keys: |
| del values[key] |
| |
| return values |
| |
| |
| class Image(utils.RestrictedAttrDict): |
| """Define a ChromeOS Image. |
| |
| Fields: |
| build: An instance of gspaths.Build that defines the build. |
| image_channel: Sometimes an image has a different channel than the build |
| directory it's in. (ie: nplusone). None otherwise. |
| image_version: Sometimes an image has a different version than the build |
| directory it's in. (ie: nplusone). None otherwise. |
| image_type: The type of the image. Currently, "recovery" or "base" types |
| are supported. |
| key: The key the image was signed with. "premp", "mp", "mp-v2" |
| This is not the board specific key name, but the general value used |
| in image/payload names. |
| uri: The URI of the image. This URI can be any format understood by |
| urilib. |
| """ |
| |
| _name = "Image definition" |
| _slots = ( |
| "build", |
| "image_type", |
| "key", |
| "image_channel", |
| "image_version", |
| "uri", |
| ) |
| DEFAULT_IMAGE_TYPE = "recovery" |
| |
| def __init__(self, *args, **kwargs) -> None: |
| super().__init__(*args, **kwargs) |
| |
| # Pylint isn't able to follow utils.RestrictedAttrDict & _slots |
| # trickery. |
| # pylint: disable=access-member-before-definition |
| |
| # If these match defaults, set to None. |
| if self.build: |
| self._clear_if_default("image_channel", self.build.channel) |
| self._clear_if_default("image_version", self.build.version) |
| |
| # Force a default image_type if unspecified. |
| if not self.image_type: |
| self.image_type = Image.DEFAULT_IMAGE_TYPE |
| |
| def __str__(self) -> str: |
| if self.uri: |
| return self.uri.split("/")[-1] |
| else: |
| return "Image: %s:%s/%s%s/%s%s/%s/%s (no uri)" % ( |
| self.build.bucket, |
| self.build.board, |
| self.build.channel, |
| "(%s)" % self.image_channel if self.image_channel else "", |
| self.build.version, |
| "(%s)" % self.image_version if self.image_version else "", |
| self.image_type, |
| self.key, |
| ) |
| |
| |
| class DLCImage(Image): |
| """Define a ChromeOS DLC Image. |
| |
| Fields: |
| dlc_id: ID of a DLC module image. |
| dlc_package: Package name of the DLC module. |
| dlc_image: File name of a DLC module image. |
| """ |
| |
| _name = "DLC Image definition" |
| _slots = Image._slots + ("dlc_id", "dlc_package", "dlc_image") |
| |
| def __str__(self) -> str: |
| if self.uri: |
| delim = "/" |
| # All DLC images have the same image name, so differentiate by |
| # printing a bit more information than the image itself. |
| return delim.join(self.uri.split(delim)[-3:]) |
| else: |
| return "%s %s/%s/%s" % ( |
| super().__str__(), |
| self.dlc_id, |
| self.dlc_package, |
| self.dlc_image, |
| ) |
| |
| |
| class MiniOSImage(Image): |
| """Define a ChromeOS MiniOS Image.""" |
| |
| _name = "MiniOS Image definition" |
| _slots = Image._slots + ("minios",) |
| |
| def __init__(self, *args, **kwargs) -> None: |
| super().__init__(*args, **kwargs) |
| self.minios = True |
| |
| def __str__(self) -> str: |
| if self.uri: |
| return "%s (minios)" % self.uri.split("/")[-1] |
| else: |
| return "%s %r (minios)" % (super().__str__(), self.minios) |
| |
| |
| class UnsignedImageArchive(utils.RestrictedAttrDict): |
| """Define a unsigned ChromeOS image archive. |
| |
| Fields: |
| bucket: The bucket of the image. "chromeos-releases" as default. |
| channel: The channel of the image "stable-channel", "nplusone", etc. |
| board: The board of the image "x86-mario", etc. |
| version: The version of the image. "0.14.23.2", "3401.0.0", etc. |
| milestone: the most recent branch corresponding to the version. |
| e.g. "R19". |
| image_type: "test", "recovery" or "base". |
| uri: The URI of the image. This URI can be any format understood by |
| urilib. |
| """ |
| |
| _name = "Unsigned image archive definition" |
| _slots = ("build", "milestone", "image_type", "uri") |
| |
| def __str__(self) -> str: |
| if self.uri: |
| return "%s" % self.uri.split("/")[-1] |
| else: |
| return "Unsigned image archive: %s:%s/%s/%s-%s/%s (no uri)" % ( |
| self.build.bucket, |
| self.build.board, |
| self.build.channel, |
| self.milestone, |
| self.build.version, |
| self.image_type, |
| ) |
| |
| |
| class UnsignedMiniOSImageArchive(UnsignedImageArchive): |
| """Define a unsigned MiniOS image archive.""" |
| |
| _name = "Unsigned MiniOS image archive definition" |
| _slots = UnsignedImageArchive._slots + ("minios",) |
| |
| def __init__(self, *args, **kwargs) -> None: |
| super().__init__(*args, **kwargs) |
| self.minios = True |
| |
| def __str__(self) -> str: |
| if self.uri: |
| return "%s (minios)" % self.uri.split("/")[-1] |
| else: |
| return "%s %r (minios)" % (super().__str__(), self.minios) |
| |
| |
| class Payload(utils.RestrictedAttrDict): |
| """Define a ChromeOS Payload. |
| |
| Fields: |
| tgt_image: A representation of image the payload updates to, either |
| Image or UnsignedImageArchive. |
| src_image: A representation of image it updates from. None for |
| Full updates, or the same type as tgt_image otherwise. |
| build: A build if it is supposed to be different from the tgt_image's |
| build. |
| uri: The URI of the payload. This can be any format understood by |
| urilib. |
| exists: A boolean. If true, artifacts for this build already exist. |
| minios: If true, extracts the minios partition instead of root and |
| kernel. |
| """ |
| |
| _name = "Payload definition" |
| _slots = ("tgt_image", "src_image", "build", "uri", "exists", "minios") |
| |
| def __init__(self, exists=False, *args, **kwargs) -> None: |
| kwargs.update(exists=exists) |
| super().__init__(*args, **kwargs) |
| |
| # Pylint isn't able to follow utils.RestrictedAttrDict & _slots |
| # trickery. |
| # pylint: disable=access-member-before-definition |
| |
| # If there was no build passed, set the target image's build as the |
| # default. |
| if not self.build and self.tgt_image.build: |
| self.build = Build(self.tgt_image.build) |
| |
| def __str__(self) -> str: |
| if self.uri: |
| return self.uri.split("/")[-1] |
| else: |
| return "%s -> %s (no uri)" % ( |
| self.src_image or "any", |
| self.tgt_image, |
| ) |
| |
| |
| class ChromeosReleases: |
| """Name space class for static methods for URIs in chromeos-releases.""" |
| |
| BUCKET = "chromeos-releases" |
| |
| TEST_BUCKET = "chromeos-releases-test" |
| |
| # Build flags |
| LOCK = "LOCK" |
| |
| FLAGS = (LOCK,) |
| |
| UNSIGNED_IMAGE_TYPES = ("test", "recovery", "base") |
| |
| @staticmethod |
| def BuildUri(build): |
| """Creates the gspath for a given build. |
| |
| Args: |
| build: An instance of gspaths.Build that defines the build. |
| |
| Returns: |
| The url for the specified build artifacts. Should be of the form: |
| gs://chromeos-releases/blah-channel/board-name/1.2.3 |
| """ |
| return "gs://%s/%s/%s/%s" % ( |
| build.bucket, |
| build.channel, |
| build.board, |
| build.version, |
| ) |
| |
| @staticmethod |
| def BuildPayloadsUri(build): |
| """Creates the gspath for the payloads of a given build. |
| |
| Args: |
| build: An instance of gspaths.Build that defines the build. |
| |
| Returns: |
| The url for the specified build's payloads. Should be of the form: |
| gs://chromeos-releases/blah-channel/board-name/1.2.3/payloads |
| """ |
| return os.path.join(ChromeosReleases.BuildUri(build), "payloads") |
| |
| @staticmethod |
| def BuildPayloadsSigningUri(build): |
| """Creates the base gspath for payload signing files. |
| |
| We create a number of files during signer interaction. This method |
| creates the base path for all such files associated with a given build. |
| There should still be subdirectories per-payload to avoid collisions, |
| but by using this uniform base pass clean up can be more reliable. |
| |
| Args: |
| build: An instance of gspaths.Build that defines the build. |
| |
| Returns: |
| The url for the specified build's payloads. Should be of the form: |
| gs://chromeos-releases/some-channel/board-name/1.2.3/ |
| payloads/signing |
| """ |
| return os.path.join(ChromeosReleases.BuildPayloadsUri(build), "signing") |
| |
| @staticmethod |
| def BuildPayloadsFlagUri(build, flag): |
| """Creates the gspath for a given build flag. |
| |
| LOCK - means that payload processing is in progress on the host which |
| owns the locks. Locks have a timeout associated with them in |
| case of error, but are not 100% atomic when a lock is timing out. |
| |
| Args: |
| build: An instance of gspaths.Build that defines the build. |
| flag: gs_paths.LOCK |
| |
| Returns: |
| The url for the specified build's payloads. Should be of the form: |
| gs://chromeos-releases/some-channel/board-name/1.2.3/ |
| payloads/LOCK_FLAG |
| """ |
| assert flag in ChromeosReleases.FLAGS |
| return os.path.join( |
| ChromeosReleases.BuildPayloadsUri(build), "%s_flag" % flag |
| ) |
| |
| @staticmethod |
| def ImageName(channel, board, version, key, image_type): |
| """Creates the base file name for a given build image. |
| |
| Args: |
| channel: What channel does the build belong too. Usually |
| xxx-channel. |
| board: What board is the build for? "x86-alex", "lumpy", etc. |
| version: "What is the build version. "3015.0.0", "1945.76.3", etc. |
| key: "What is the signing key. "premp", "mp", "mp-v2", etc. |
| image_type: The type of image. It can be either "recovery" or |
| "base". |
| |
| Returns: |
| The name of the specified image. Should be of the form: |
| chromeos_1.2.3_board-name_recovery_blah-channel_key.bin |
| """ |
| |
| template = ( |
| "chromeos_%(version)s_%(board)s_%(image_type)s" |
| + "_%(channel)s_%(key)s.bin" |
| ) |
| |
| return template % { |
| "channel": channel, |
| "board": board, |
| "version": version, |
| "key": key, |
| "image_type": image_type, |
| } |
| |
| @staticmethod |
| def DLCImageName(): |
| """Creates file name for a DLC image. |
| |
| Returns: |
| The name of the DLC image. |
| """ |
| return "dlc.img" |
| |
| @staticmethod |
| def UnsignedImageArchiveName(board, version, milestone, image_type): |
| """The base name for the tarball containing an unsigned build image. |
| |
| Args: |
| board: What board is the build for? "x86-alex", "lumpy", etc. |
| version: What is the build version? "3015.0.0", "1945.76.3", etc |
| milestone: the most recent branch corresponding to the version; |
| e.g. "R19". |
| image_type: either "recovery" or "test", currently. |
| |
| Returns: |
| The name of the specified image archive. Should be of the form: |
| ChromeOS-type-R19-1.2.3-board-name.tar.xz |
| """ |
| |
| template = ( |
| "ChromeOS-%(image_type)s-%(milestone)s-%(version)s-%(board)s.tar.xz" |
| ) |
| |
| return template % { |
| "board": board, |
| "version": version, |
| "milestone": milestone, |
| "image_type": image_type, |
| } |
| |
| @staticmethod |
| def ImageUri( |
| build, key, image_type, image_channel=None, image_version=None |
| ): |
| """Creates the gspath for a given build image. |
| |
| Args: |
| build: An instance of gspaths.Build that defines the build. |
| key: What is the signing key? "premp", "mp", "mp-v2", etc |
| image_type: The type of image. It can be either "recovery" or |
| "base". |
| image_channel: Sometimes an image has a different channel than the |
| build directory it's in. (ie: nplusone). |
| image_version: Sometimes an image has a different version than the |
| build directory it's in. (ie: nplusone). |
| |
| Returns: |
| The url for the specified build's image. Should be of the form: |
| gs://chromeos-releases/blah-channel/board-name/1.2.3/ |
| chromeos_1.2.3_board-name_recovery_blah-channel_key.bin |
| """ |
| if not image_channel: |
| image_channel = build.channel |
| |
| if not image_version: |
| image_version = build.version |
| |
| return os.path.join( |
| ChromeosReleases.BuildUri(build), |
| ChromeosReleases.ImageName( |
| image_channel, build.board, image_version, key, image_type |
| ), |
| ) |
| |
| @staticmethod |
| def UnsignedImageUri(build, milestone, image_type): |
| """Creates the gspath for a given unsigned build image archive. |
| |
| Args: |
| build: An instance of gspaths.Build that defines the build. |
| milestone: the most recent branch corresponding to the version; |
| e.g. "R19". |
| image_type: either "recovery" or "test", currently. |
| |
| Returns: |
| The url for the specified build's image. Should be of the form: |
| gs://chromeos-releases/blah-channel/board-name/1.2.3/ |
| ChromeOS-type-R19-1.2.3-board-name.tar.xz |
| """ |
| return os.path.join( |
| ChromeosReleases.BuildUri(build), |
| ChromeosReleases.UnsignedImageArchiveName( |
| build.board, build.version, milestone, image_type |
| ), |
| ) |
| |
| @staticmethod |
| def DLCImageUri(build, dlc_id, dlc_package, dlc_image): |
| """Creates the gspath for a given dlc image archive. |
| |
| Args: |
| build: An instance of gspaths.Build that defines the build. |
| dlc_id: The dlc's id (e.g. 'termina-dlc'). |
| dlc_package: The dlc's package (e.g. 'package'). |
| dlc_image: The dlc's image name (currently always 'dlc.img'). |
| |
| Returns: |
| The url for the specified dlc's image. Should be of the form: |
| gs://chromeos-releases/beta-channel/fizz/13505.33.0/ |
| dlc/termina-dlc/package/dlc.img |
| """ |
| return os.path.join( |
| ChromeosReleases.BuildUri(build), |
| "dlc", |
| dlc_id, |
| dlc_package, |
| dlc_image, |
| ) |
| |
| @staticmethod |
| def DLCImagesUri(build): |
| """Creates the gspath for DLC images for a given build image archive.""" |
| |
| # DLC images are located at: |
| # gs://{path_to_build}/dlc/{DLC_ID}/{DLC_PACKAGE} |
| return os.path.join( |
| ChromeosReleases.BuildUri(build), |
| "dlc", |
| "*", |
| "*", |
| ChromeosReleases.DLCImageName(), |
| ) |
| |
| @classmethod |
| def _ParseImageUriValues(cls, image_uri): |
| # The named values in this regex must match the arguments to |
| # gspaths.Image. |
| exp = ( |
| r"^gs://(?P<bucket>.*)/(?P<channel>.*)/(?P<board>.*)/" |
| r"(?P<version>.*)/chromeos_(?P<image_version>[^_]+)_" |
| r"(?P=board)_(?P<image_type>[^_]+)_(?P<image_channel>[^_]+)_" |
| "(?P<key>[^_]+).bin$" |
| ) |
| |
| return Build.BuildValuesFromUri(exp, image_uri) |
| |
| @classmethod |
| def ParseImageUri(cls, image_uri, os_type): |
| """Parse the URI of an image into an Image object.""" |
| |
| values = cls._ParseImageUriValues(image_uri) |
| if not values: |
| return None |
| |
| # Insert the URI. |
| values["uri"] = image_uri |
| |
| # Create an Image object using the values we parsed out. |
| return { |
| OSType.CROS: Image, |
| OSType.MINIOS: MiniOSImage, |
| }[ |
| os_type |
| ](values) |
| |
| @classmethod |
| def ParseMiniOSImageUri(cls, image_uri): |
| """Parse the URI of an MiniOS image into an Image object.""" |
| |
| values = cls._ParseImageUriValues(image_uri) |
| if not values: |
| return None |
| |
| # Insert the URI. |
| values["uri"] = image_uri |
| |
| # Create an Image object using the values we parsed out. |
| return MiniOSImage(values) |
| |
| @classmethod |
| def ParseUnsignedImageUri(cls, image_uri, os_type): |
| """Parse the URI of an image into an UnsignedImageArchive object.""" |
| |
| # The named values in this regex must match the arguments to |
| # gspaths.Image. |
| exp = ( |
| r"gs://(?P<bucket>[^/]+)/(?P<channel>[^/]+)/" |
| r"(?P<board>[^/]+)/(?P<version>[^/]+)/" |
| r"ChromeOS-(?P<image_type>%s)-(?P<milestone>R[0-9]+)-" |
| r"(?P=version)-(?P=board).tar.xz" |
| % "|".join(cls.UNSIGNED_IMAGE_TYPES) |
| ) |
| |
| values = Build.BuildValuesFromUri(exp, image_uri) |
| if not values: |
| return None |
| |
| # Insert the URI. |
| values["uri"] = image_uri |
| |
| # Create an Image object using the values we parsed out. |
| return { |
| OSType.CROS: UnsignedImageArchive, |
| OSType.MINIOS: UnsignedMiniOSImageArchive, |
| }[os_type](values) |
| |
| @classmethod |
| def ParseDLCImageUri(cls, image_uri): |
| """Parse the URI of a DLC image into an Image object.""" |
| |
| # The named values in this regex must match the arguments to |
| # gspaths.DLCImage. |
| exp = ( |
| r"^gs://(?P<bucket>.*)/(?P<channel>.*)/(?P<board>.*)/" |
| r"(?P<version>.*)/dlc/(?P<dlc_id>.*)/(?P<dlc_package>.*)/" |
| r"(?P<dlc_image>.*)$" |
| ) |
| |
| values = Build.BuildValuesFromUri(exp, image_uri) |
| if not values: |
| logging.warning("Unparsable DLC URI: %s", image_uri) |
| return None |
| |
| # Insert the URI |
| values["uri"] = image_uri |
| |
| # Create an Image object using the values we parsed out. |
| return DLCImage(values) |
| |
| @staticmethod |
| def DLCPayloadName( |
| channel, |
| board, |
| version, |
| dlc_id, |
| dlc_package, |
| random_str=None, |
| src_version=None, |
| sign=True, |
| ): |
| """Creates the payload file name of a DLC image. |
| |
| Args: |
| channel: What channel does the build belong to? Usually |
| "xxx-channel". |
| board: What board is the build for? "x86-alex", "lumpy", etc. |
| version: What is the build version? "3015.0.0", "1945.76.3", etc. |
| dlc_id: This is the ID of the DLC module. |
| dlc_package: Package name of the DLC module. |
| random_str: Force a given random string. None means generate one. |
| src_version: If this payload is a delta, this is the version of the |
| image it updates from. |
| sign: Whether to sign the payload. |
| |
| Returns: |
| The name for the specified build's payloads. Should be in the form: |
| dlc_sample-dlc_sample-package_11869.0.0_kevin-arcnext_ |
| canary-channel_full .bin- |
| 250bc111ea4955aebc2af08db1f1773c.signed |
| """ |
| if random_str is None: |
| random_str = cros_build_lib.GetRandomString() |
| |
| if sign is True: |
| signed_ext = ".signed" |
| else: |
| signed_ext = "" |
| |
| if src_version: |
| template = ( |
| "dlc_%(dlc_id)s_%(dlc_package)s_%(src_version)s-%(version)s_" |
| "%(board)s_%(channel)s_delta.bin-%(random_str)s" |
| "%(signed_ext)s" |
| ) |
| |
| return template % { |
| "dlc_id": dlc_id, |
| "dlc_package": dlc_package, |
| "channel": channel, |
| "board": board, |
| "version": version, |
| "random_str": random_str, |
| "src_version": src_version, |
| "signed_ext": signed_ext, |
| } |
| else: |
| template = ( |
| "dlc_%(dlc_id)s_%(dlc_package)s_%(version)s_%(board)s_" |
| "%(channel)s_full.bin-%(random_str)s%(signed_ext)s" |
| ) |
| |
| return template % { |
| "dlc_id": dlc_id, |
| "dlc_package": dlc_package, |
| "channel": channel, |
| "board": board, |
| "version": version, |
| "random_str": random_str, |
| "signed_ext": signed_ext, |
| } |
| |
| @staticmethod |
| def MiniOSPayloadName( |
| channel, |
| board, |
| version, |
| key=None, |
| random_str=None, |
| src_version=None, |
| unsigned_image_type: Optional[str] = "test", |
| ): |
| """Creates the payload file name of a DLC image. |
| |
| Args: |
| channel: What channel does the build belong to? Usually |
| "xxx-channel". |
| board: What board is the build for? "x86-alex", "lumpy", etc. |
| version: What is the build version? "3015.0.0", "1945.76.3", etc. |
| key: What is the signing key? "premp", "mp", "mp-v2", etc; None |
| indicates that the image is not signed, e.g. a test image. |
| random_str: Force a given random string. None means generate one. |
| src_version: If this payload is a delta, this is the version of the |
| image it updates from. |
| unsigned_image_type: the type descriptor of an unsigned image; |
| significant iff key is None. |
| |
| Returns: |
| The name for the specified build's payloads. |
| """ |
| if random_str is None: |
| random_str = cros_build_lib.GetRandomString() |
| |
| if not key: |
| signed_ext = "" |
| key = unsigned_image_type |
| else: |
| signed_ext = ".signed" |
| |
| if src_version: |
| return ( |
| f"minios_{src_version}-{version}_{board}_{channel}_" |
| f"delta_{key}.bin-{random_str}{signed_ext}" |
| ) |
| else: |
| return ( |
| f"minios_{version}_{board}_{channel}_" |
| f"full_{key}.bin-{random_str}{signed_ext}" |
| ) |
| |
| @staticmethod |
| def PayloadName( |
| channel, |
| board, |
| version, |
| key=None, |
| random_str=None, |
| src_version=None, |
| unsigned_image_type="test", |
| ): |
| """Creates the gspath for a payload associated with a given build. |
| |
| Args: |
| channel: What channel does the build belong to? Usually |
| "xxx-channel". |
| board: What board is the build for? "x86-alex", "lumpy", etc. |
| version: What is the build version? "3015.0.0", "1945.76.3", etc. |
| key: What is the signing key? "premp", "mp", "mp-v2", etc; None |
| (default) indicates that the image is not signed, e.g. a test |
| image. |
| random_str: Force a given random string. None means generate one. |
| src_version: If this payload is a delta, this is the version of the |
| image it updates from. |
| unsigned_image_type: the type descriptor (string) of an unsigned |
| image; significant iff key is None (default: "test"). |
| |
| Returns: |
| The name for the specified build's payloads. Should be of the form: |
| |
| chromeos_0.12.433.257-2913.377.0_x86-alex_stable-channel_ |
| delta_mp-v3.bin-b334762d0f6b80f471069153bbe8b97a.signed |
| |
| chromeos_2913.377.0_x86-alex_stable-channel_full_mp-v3. |
| bin-610c97c30fae8561bde01a6116d65cb9.signed |
| """ |
| if random_str is None: |
| random_str = cros_build_lib.GetRandomString() |
| |
| if not key: |
| signed_ext = "" |
| key = unsigned_image_type |
| else: |
| signed_ext = ".signed" |
| |
| if src_version: |
| template = ( |
| "chromeos_%(src_version)s-%(version)s_%(board)s_%(channel)s_" |
| "delta_%(key)s.bin-%(random_str)s%(signed_ext)s" |
| ) |
| |
| return template % { |
| "channel": channel, |
| "board": board, |
| "version": version, |
| "key": key, |
| "random_str": random_str, |
| "src_version": src_version, |
| "signed_ext": signed_ext, |
| } |
| else: |
| template = ( |
| "chromeos_%(version)s_%(board)s_%(channel)s_" |
| "full_%(key)s.bin-%(random_str)s%(signed_ext)s" |
| ) |
| |
| return template % { |
| "channel": channel, |
| "board": board, |
| "version": version, |
| "key": key, |
| "random_str": random_str, |
| "signed_ext": signed_ext, |
| } |
| |
| @staticmethod |
| def DLCPayloadUri( |
| build, |
| random_str, |
| dlc_id, |
| dlc_package, |
| image_channel=None, |
| image_version=None, |
| src_version=None, |
| ): |
| """Creates the gspath for a payload associated with a given build. |
| |
| Args: |
| build: An instance of gspaths.Build that defines the build. |
| random_str: Force a given random string. None means generate one. |
| dlc_id: This is the ID of the DLC module. |
| dlc_package: This is the package name of the DLC module. |
| image_channel: Sometimes an image has a different channel than the |
| build directory it's in. (ie: nplusone). |
| image_version: Sometimes an image has a different version than the |
| build directory it's in. (ie: nplusone). |
| src_version: If this payload is a delta, this is the version of the |
| image it updates from. |
| """ |
| if image_channel is None: |
| image_channel = build.channel |
| |
| if image_version is None: |
| image_version = build.version |
| |
| # DLC payloads are pushed to dlc/|dlc_id|/|dlc_package| subfolder. |
| return os.path.join( |
| ChromeosReleases.BuildPayloadsUri(build), |
| "dlc", |
| dlc_id, |
| dlc_package, |
| ChromeosReleases.DLCPayloadName( |
| image_channel, |
| build.board, |
| image_version, |
| dlc_id, |
| dlc_package, |
| random_str, |
| src_version, |
| ), |
| ) |
| |
| @staticmethod |
| def MiniOSPayloadUri( |
| build, |
| random_str, |
| key=None, |
| image_channel=None, |
| image_version=None, |
| src_version=None, |
| ): |
| """Creates the gspath for a payload associated with a given build. |
| |
| Args: |
| build: An instance of gspaths.Build that defines the build. |
| random_str: Force a given random string. None means generate one. |
| key: What is the signing key? "premp", "mp", "mp-v2", etc; None |
| means that the image is unsigned (e.g. a test image). |
| image_channel: Sometimes an image has a different channel than the |
| build directory it's in. (ie: nplusone). |
| image_version: Sometimes an image has a different version than the |
| build directory it's in. (ie: nplusone). |
| src_version: If this payload is a delta, this is the version of the |
| image it updates from. |
| |
| Returns: |
| The url for the specified build's payloads. Should be of the form: |
| |
| gs://chromeos-releases/stable-channel/x86-alex/2913.377.0/payloads/ |
| minios/chromeos_0.12.433.257-2913.377.0_x86-alex_stable-channel_ |
| delta_mp-v3.bin-b334762d0f6b80f471069153bbe8b97a.signed |
| |
| gs://chromeos-releases/stable-channel/x86-alex/2913.377.0/payloads/ |
| minios/chromeos_2913.377.0_x86-alex_stable-channel_full_mp-v3. |
| bin-610c97c30fae8561bde01a6116d65cb9.signed |
| """ |
| if image_channel is None: |
| image_channel = build.channel |
| |
| if image_version is None: |
| image_version = build.version |
| |
| # MiniOS payloads are pushed to minios subfolder. |
| return os.path.join( |
| ChromeosReleases.BuildPayloadsUri(build), |
| "minios", |
| ChromeosReleases.MiniOSPayloadName( |
| image_channel, |
| build.board, |
| image_version, |
| key, |
| random_str, |
| src_version, |
| ), |
| ) |
| |
| @staticmethod |
| def PayloadUri( |
| build, |
| random_str, |
| key=None, |
| image_channel=None, |
| image_version=None, |
| src_version=None, |
| ): |
| """Creates the gspath for a payload associated with a given build. |
| |
| Args: |
| build: An instance of gspaths.Build that defines the build. |
| key: What is the signing key? "premp", "mp", "mp-v2", etc; None |
| means that the image is unsigned (e.g. a test image). |
| image_channel: Sometimes an image has a different channel than the |
| build directory it's in. (ie: nplusone). |
| image_version: Sometimes an image has a different version than the |
| build directory it's in. (ie: nplusone). |
| random_str: Force a given random string. None means generate one. |
| src_version: If this payload is a delta, this is the version of the |
| image it updates from. |
| |
| Returns: |
| The url for the specified build's payloads. Should be of the form: |
| |
| gs://chromeos-releases/stable-channel/x86-alex/2913.377.0/payloads/ |
| chromeos_0.12.433.257-2913.377.0_x86-alex_stable-channel_ |
| delta_mp-v3.bin-b334762d0f6b80f471069153bbe8b97a.signed |
| |
| gs://chromeos-releases/stable-channel/x86-alex/2913.377.0/payloads/ |
| chromeos_2913.377.0_x86-alex_stable-channel_full_mp-v3. |
| bin-610c97c30fae8561bde01a6116d65cb9.signed |
| """ |
| if image_channel is None: |
| image_channel = build.channel |
| |
| if image_version is None: |
| image_version = build.version |
| |
| return os.path.join( |
| ChromeosReleases.BuildPayloadsUri(build), |
| ChromeosReleases.PayloadName( |
| image_channel, |
| build.board, |
| image_version, |
| key, |
| random_str, |
| src_version, |
| ), |
| ) |
| |
| @classmethod |
| def ParsePayloadUri(cls, payload_uri): |
| """Parse the URI of an image into an Image object.""" |
| |
| # Sample Delta URI: |
| # gs://chromeos-releases/stable-channel/x86-mario/4731.72.0/payloads/ |
| # chromeos_4537.147.0-4731.72.0_x86-mario_stable-channel_delta_mp-v3 |
| # .bin-3a90d8666d1d42b7a7367660b897e8c9.signed |
| |
| # Sample Full URI: |
| # gs://chromeos-releases/stable-channel/x86-mario/4731.72.0/payloads/ |
| # chromeos_4731.72.0_x86-mario_stable-channel_full_mp-v3.bin- |
| # 969f24ba8cbf2096ebe3c57d5f0253b7.signed |
| |
| # Handle FULL payload URIs. |
| full_exp = ( |
| r"^gs://(?P<bucket>.*)/(?P<channel>.*)/(?P<board>.*)/" |
| r"(?P<version>.*)/payloads/chromeos_(?P<image_version>[^_]+)_" |
| r"(?P=board)_(?P<image_channel>[^_]+)_full_(?P<key>[^_]+)\.bin" |
| r"-[0-9A-Fa-f]+\.signed$" |
| ) |
| |
| image_values = Build.BuildValuesFromUri(full_exp, payload_uri) |
| if image_values: |
| # The image URIs can't be discovered from the payload URI. |
| image_values["uri"] = None |
| |
| # Create the Payload. |
| tgt_image = Image(image_values) |
| |
| return Payload(tgt_image=tgt_image, uri=payload_uri) |
| |
| # Handle DELTA payload URIs. |
| delta_exp = ( |
| r"^gs://(?P<bucket>.*)/(?P<channel>.*)/(?P<board>.*)/" |
| r"(?P<version>.*)/payloads/chromeos_(?P<src_version>[^_]+)-" |
| r"(?P<image_version>[^_]+)_(?P=board)_" |
| r"(?P<image_channel>[^_]+)_delta_(?P<key>[^_]+)\.bin" |
| r"-[0-9A-Fa-f]+\.signed$" |
| ) |
| |
| image_values = Build.BuildValuesFromUri(delta_exp, payload_uri) |
| if image_values: |
| # The image URIs can't be discovered from the payload URI. |
| image_values["uri"] = None |
| |
| # Remember the src_version for the src_image. |
| src_version = image_values["src_version"] |
| del image_values["src_version"] |
| |
| # Create the payload. |
| tgt_image = Image(image_values) |
| |
| # Create a new Build, so we don't override the one in the target |
| # image. |
| image_values["build"] = Build(image_values["build"]) |
| # Set the values which are different for src versions. |
| image_values["build"]["version"] = src_version |
| |
| # The payload URI doesn't tell us any of these values. However, it's |
| # a mostly safe bet that the src version has no |
| # image_version/image_channel. |
| # Not knowing the source key is problematic. |
| image_values["image_version"] = None |
| image_values["image_channel"] = None |
| image_values["key"] = None |
| |
| src_image = Image(image_values) |
| |
| return Payload( |
| src_image=src_image, tgt_image=tgt_image, uri=payload_uri |
| ) |
| |
| # The URI didn't match. |
| return None |
| |
| |
| class ChromeosImageArchive: |
| """Namespace class for static methods for URIs in chromeos-image-archive.""" |
| |
| BUCKET = "chromeos-image-archive" |
| |
| @classmethod |
| def BuildUri(cls, board, milestone, version, bucket=None): |
| """Creates the gspath for a given build. |
| |
| Args: |
| board: What board is the build for? "x86-alex", "lumpy", etc. |
| milestone: Numeric milestone, e.g. 19 for R19. |
| version: "What is the build version. "3015.0.0", "1945.76.3", etc. |
| bucket: the bucket the build in (None means cls.BUCKET). |
| |
| Returns: |
| The url for the specified build artifacts. Should be of the form: |
| gs://chromeos-image-archive/board-release/R23-4.5.6 |
| """ |
| |
| bucket = bucket or cls.BUCKET |
| |
| return "gs://%s/%s-release/R%s-%s" % (bucket, board, milestone, version) |
| |
| |
| def VersionKey(version): |
| """Convert a version string to a comparable value. |
| |
| All old style values are considered older than all new style values. |
| The actual values returned should only be used for comparison against |
| other VersionKey results. |
| |
| Args: |
| version: String with a build version "1.2.3" or "0.12.3.4" |
| |
| Returns: |
| A value comparable against other version strings. |
| """ |
| |
| key = [int(n) for n in version.split(".")] |
| |
| # 3 number versions are new style. |
| # 4 number versions are old style. |
| assert len(key) in (3, 4) |
| |
| if len(key) == 3: |
| # 1.2.3 -> (1, 0, 1, 2, 3) |
| return [1, 0] + key |
| else: |
| # 0.12.3.4 -> (0, 0, 12, 3, 4) |
| return [0] + key |
| |
| |
| def VersionGreater(left, right): |
| """Compare two version strings. |
| |
| left > right |
| |
| Args: |
| left: String with lefthand version string "1.2.3" or "0.12.3.4" |
| right: String with righthand version string "1.2.3" or "0.12.3.4" |
| |
| Returns: |
| left > right taking into account new style versions versus old style. |
| """ |
| return VersionKey(left) > VersionKey(right) |
| |
| |
| def IsImage(a): |
| """Return if the object is of Image type. |
| |
| Args: |
| a: object whose type needs to be checked |
| |
| Returns: |
| True if |a| is of Image type, False otherwise |
| """ |
| return isinstance(a, Image) |
| |
| |
| def IsUnsignedImageArchive(a): |
| """Return if the object is of UnsignedImageArchive type. |
| |
| Args: |
| a: object whose type needs to be checked |
| |
| Returns: |
| True if |a| is of UnsignedImageArchive type, False otherwise |
| """ |
| return isinstance(a, UnsignedImageArchive) |
| |
| |
| def IsUnsignedMiniOSImageArchive(a): |
| """Return if the object is of UnsignedMiniOSImageArchive type. |
| |
| Args: |
| a: object whose type needs to be checked |
| |
| Returns: |
| True if |a| is of UnsignedMiniOSImageArchive type, False otherwise |
| """ |
| return isinstance(a, UnsignedMiniOSImageArchive) |
| |
| |
| def IsDLCImage(a): |
| """Return if the object is of DLCImage type. |
| |
| Args: |
| a: object whose type needs to be checked |
| |
| Returns: |
| True if |a| is of DLCImage type, False otherwise |
| """ |
| return isinstance(a, DLCImage) |
| |
| |
| def IsMiniOSImage(obj): |
| """Return if the |obj| is of MiniOSImage type. |
| |
| Args: |
| obj: Object whose type needs to be checked. |
| |
| Returns: |
| True if |obj| is of MiniOSImage type, False otherwise |
| """ |
| return isinstance(obj, MiniOSImage) |