xref: /aosp_15_r20/tools/acloud/internal/lib/android_build_client.py (revision 800a58d989c669b8eb8a71d8df53b1ba3d411444)
1*800a58d9SAndroid Build Coastguard Worker#!/usr/bin/env python
2*800a58d9SAndroid Build Coastguard Worker#
3*800a58d9SAndroid Build Coastguard Worker# Copyright 2016 - The Android Open Source Project
4*800a58d9SAndroid Build Coastguard Worker#
5*800a58d9SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*800a58d9SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*800a58d9SAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*800a58d9SAndroid Build Coastguard Worker#
9*800a58d9SAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
10*800a58d9SAndroid Build Coastguard Worker#
11*800a58d9SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*800a58d9SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*800a58d9SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*800a58d9SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*800a58d9SAndroid Build Coastguard Worker# limitations under the License.
16*800a58d9SAndroid Build Coastguard Worker
17*800a58d9SAndroid Build Coastguard Worker"""A client that talks to Android Build APIs."""
18*800a58d9SAndroid Build Coastguard Worker
19*800a58d9SAndroid Build Coastguard Workerimport collections
20*800a58d9SAndroid Build Coastguard Workerimport io
21*800a58d9SAndroid Build Coastguard Workerimport json
22*800a58d9SAndroid Build Coastguard Workerimport logging
23*800a58d9SAndroid Build Coastguard Workerimport os
24*800a58d9SAndroid Build Coastguard Workerimport ssl
25*800a58d9SAndroid Build Coastguard Workerimport stat
26*800a58d9SAndroid Build Coastguard Worker
27*800a58d9SAndroid Build Coastguard Workerimport apiclient
28*800a58d9SAndroid Build Coastguard Worker
29*800a58d9SAndroid Build Coastguard Workerfrom acloud import errors
30*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal import constants
31*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import base_cloud_client
32*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import utils
33*800a58d9SAndroid Build Coastguard Worker
34*800a58d9SAndroid Build Coastguard Worker
35*800a58d9SAndroid Build Coastguard Workerlogger = logging.getLogger(__name__)
36*800a58d9SAndroid Build Coastguard Worker
37*800a58d9SAndroid Build Coastguard Worker# The BuildInfo namedtuple data structure.
38*800a58d9SAndroid Build Coastguard Worker# It will be the data structure returned by GetBuildInfo method.
39*800a58d9SAndroid Build Coastguard WorkerBuildInfo = collections.namedtuple("BuildInfo", [
40*800a58d9SAndroid Build Coastguard Worker    "branch",  # The branch name string
41*800a58d9SAndroid Build Coastguard Worker    "build_id",  # The build id string
42*800a58d9SAndroid Build Coastguard Worker    "build_target",  # The build target string
43*800a58d9SAndroid Build Coastguard Worker    "release_build_id"])  # The release build id string
44*800a58d9SAndroid Build Coastguard Worker_DEFAULT_BRANCH = "aosp-master"
45*800a58d9SAndroid Build Coastguard Worker
46*800a58d9SAndroid Build Coastguard Worker
47*800a58d9SAndroid Build Coastguard Workerclass AndroidBuildClient(base_cloud_client.BaseCloudApiClient):
48*800a58d9SAndroid Build Coastguard Worker    """Client that manages Android Build."""
49*800a58d9SAndroid Build Coastguard Worker
50*800a58d9SAndroid Build Coastguard Worker    # API settings, used by BaseCloudApiClient.
51*800a58d9SAndroid Build Coastguard Worker    API_NAME = "androidbuildinternal"
52*800a58d9SAndroid Build Coastguard Worker    API_VERSION = "v2beta1"
53*800a58d9SAndroid Build Coastguard Worker    SCOPE = "https://www.googleapis.com/auth/androidbuild.internal"
54*800a58d9SAndroid Build Coastguard Worker
55*800a58d9SAndroid Build Coastguard Worker    # other variables.
56*800a58d9SAndroid Build Coastguard Worker    DEFAULT_RESOURCE_ID = "0"
57*800a58d9SAndroid Build Coastguard Worker    # TODO(b/27269552): We should use "latest".
58*800a58d9SAndroid Build Coastguard Worker    DEFAULT_ATTEMPT_ID = "0"
59*800a58d9SAndroid Build Coastguard Worker    DEFAULT_CHUNK_SIZE = 20 * 1024 * 1024
60*800a58d9SAndroid Build Coastguard Worker    NO_ACCESS_ERROR_PATTERN = "does not have storage.objects.create access"
61*800a58d9SAndroid Build Coastguard Worker    # LKGB variables.
62*800a58d9SAndroid Build Coastguard Worker    BUILD_STATUS_COMPLETE = "complete"
63*800a58d9SAndroid Build Coastguard Worker    BUILD_TYPE_SUBMITTED = "submitted"
64*800a58d9SAndroid Build Coastguard Worker    ONE_RESULT = 1
65*800a58d9SAndroid Build Coastguard Worker    BUILD_SUCCESSFUL = True
66*800a58d9SAndroid Build Coastguard Worker    LATEST = "latest"
67*800a58d9SAndroid Build Coastguard Worker    # FETCH_CVD variables.
68*800a58d9SAndroid Build Coastguard Worker    FETCHER_NAME = "fetch_cvd"
69*800a58d9SAndroid Build Coastguard Worker    FETCHER_BUILD_TARGET = "aosp_cf_x86_64_phone-trunk_staging-userdebug"
70*800a58d9SAndroid Build Coastguard Worker    FETCHER_BUILD_TARGET_ARM = "aosp_cf_arm64_only_phone-trunk_staging-userdebug"
71*800a58d9SAndroid Build Coastguard Worker    # TODO(b/297085994): cvd fetch is migrating from AOSP to github artifacts, so
72*800a58d9SAndroid Build Coastguard Worker    # temporary returning hardcoded values instead of LKGB
73*800a58d9SAndroid Build Coastguard Worker    FETCHER_BUILD_ID = 11559438
74*800a58d9SAndroid Build Coastguard Worker    FETCHER_BUILD_ID_ARM = 11559085
75*800a58d9SAndroid Build Coastguard Worker    MAX_RETRY = 3
76*800a58d9SAndroid Build Coastguard Worker    RETRY_SLEEP_SECS = 3
77*800a58d9SAndroid Build Coastguard Worker
78*800a58d9SAndroid Build Coastguard Worker    # Message constant
79*800a58d9SAndroid Build Coastguard Worker    COPY_TO_MSG = ("build artifact (target: %s, build_id: %s, "
80*800a58d9SAndroid Build Coastguard Worker                   "artifact: %s, attempt_id: %s) to "
81*800a58d9SAndroid Build Coastguard Worker                   "google storage (bucket: %s, path: %s)")
82*800a58d9SAndroid Build Coastguard Worker    # pylint: disable=invalid-name
83*800a58d9SAndroid Build Coastguard Worker    def DownloadArtifact(self,
84*800a58d9SAndroid Build Coastguard Worker                         build_target,
85*800a58d9SAndroid Build Coastguard Worker                         build_id,
86*800a58d9SAndroid Build Coastguard Worker                         resource_id,
87*800a58d9SAndroid Build Coastguard Worker                         local_dest,
88*800a58d9SAndroid Build Coastguard Worker                         attempt_id=None):
89*800a58d9SAndroid Build Coastguard Worker        """Get Android build attempt information.
90*800a58d9SAndroid Build Coastguard Worker
91*800a58d9SAndroid Build Coastguard Worker        Args:
92*800a58d9SAndroid Build Coastguard Worker            build_target: Target name, e.g. "aosp_cf_x86_64_phone-userdebug"
93*800a58d9SAndroid Build Coastguard Worker            build_id: Build id, a string, e.g. "2263051", "P2804227"
94*800a58d9SAndroid Build Coastguard Worker            resource_id: Id of the resource, e.g "avd-system.tar.gz".
95*800a58d9SAndroid Build Coastguard Worker            local_dest: A local path where the artifact should be stored.
96*800a58d9SAndroid Build Coastguard Worker                        e.g. "/tmp/avd-system.tar.gz"
97*800a58d9SAndroid Build Coastguard Worker            attempt_id: String, attempt id, will default to DEFAULT_ATTEMPT_ID.
98*800a58d9SAndroid Build Coastguard Worker        """
99*800a58d9SAndroid Build Coastguard Worker        attempt_id = attempt_id or self.DEFAULT_ATTEMPT_ID
100*800a58d9SAndroid Build Coastguard Worker        api = self.service.buildartifact().get_media(
101*800a58d9SAndroid Build Coastguard Worker            buildId=build_id,
102*800a58d9SAndroid Build Coastguard Worker            target=build_target,
103*800a58d9SAndroid Build Coastguard Worker            attemptId=attempt_id,
104*800a58d9SAndroid Build Coastguard Worker            resourceId=resource_id)
105*800a58d9SAndroid Build Coastguard Worker        logger.info("Downloading artifact: target: %s, build_id: %s, "
106*800a58d9SAndroid Build Coastguard Worker                    "resource_id: %s, dest: %s", build_target, build_id,
107*800a58d9SAndroid Build Coastguard Worker                    resource_id, local_dest)
108*800a58d9SAndroid Build Coastguard Worker        try:
109*800a58d9SAndroid Build Coastguard Worker            with io.FileIO(local_dest, mode="wb") as fh:
110*800a58d9SAndroid Build Coastguard Worker                downloader = apiclient.http.MediaIoBaseDownload(
111*800a58d9SAndroid Build Coastguard Worker                    fh, api, chunksize=self.DEFAULT_CHUNK_SIZE)
112*800a58d9SAndroid Build Coastguard Worker                done = False
113*800a58d9SAndroid Build Coastguard Worker                while not done:
114*800a58d9SAndroid Build Coastguard Worker                    _, done = downloader.next_chunk()
115*800a58d9SAndroid Build Coastguard Worker            logger.info("Downloaded artifact: %s", local_dest)
116*800a58d9SAndroid Build Coastguard Worker        except (OSError, apiclient.errors.HttpError) as e:
117*800a58d9SAndroid Build Coastguard Worker            logger.error("Downloading artifact failed: %s", str(e))
118*800a58d9SAndroid Build Coastguard Worker            raise errors.DriverError(str(e))
119*800a58d9SAndroid Build Coastguard Worker
120*800a58d9SAndroid Build Coastguard Worker    def DownloadFetchcvd(
121*800a58d9SAndroid Build Coastguard Worker            self,
122*800a58d9SAndroid Build Coastguard Worker            local_dest,
123*800a58d9SAndroid Build Coastguard Worker            fetch_cvd_version,
124*800a58d9SAndroid Build Coastguard Worker            is_arm_version=False):
125*800a58d9SAndroid Build Coastguard Worker        """Get fetch_cvd from Android Build.
126*800a58d9SAndroid Build Coastguard Worker
127*800a58d9SAndroid Build Coastguard Worker        Args:
128*800a58d9SAndroid Build Coastguard Worker            local_dest: A local path where the artifact should be stored.
129*800a58d9SAndroid Build Coastguard Worker                        e.g. "/tmp/fetch_cvd"
130*800a58d9SAndroid Build Coastguard Worker            fetch_cvd_version: String of fetch_cvd version.
131*800a58d9SAndroid Build Coastguard Worker            is_arm_version: is ARM version fetch_cvd.
132*800a58d9SAndroid Build Coastguard Worker        """
133*800a58d9SAndroid Build Coastguard Worker        if fetch_cvd_version == constants.LKGB:
134*800a58d9SAndroid Build Coastguard Worker            fetch_cvd_version = self.GetFetcherVersion(is_arm_version)
135*800a58d9SAndroid Build Coastguard Worker        fetch_cvd_build_target = (
136*800a58d9SAndroid Build Coastguard Worker            self.FETCHER_BUILD_TARGET_ARM if is_arm_version
137*800a58d9SAndroid Build Coastguard Worker            else self.FETCHER_BUILD_TARGET)
138*800a58d9SAndroid Build Coastguard Worker        try:
139*800a58d9SAndroid Build Coastguard Worker            utils.RetryExceptionType(
140*800a58d9SAndroid Build Coastguard Worker                exception_types=(ssl.SSLError, errors.DriverError),
141*800a58d9SAndroid Build Coastguard Worker                max_retries=self.MAX_RETRY,
142*800a58d9SAndroid Build Coastguard Worker                functor=self.DownloadArtifact,
143*800a58d9SAndroid Build Coastguard Worker                sleep_multiplier=self.RETRY_SLEEP_SECS,
144*800a58d9SAndroid Build Coastguard Worker                retry_backoff_factor=utils.DEFAULT_RETRY_BACKOFF_FACTOR,
145*800a58d9SAndroid Build Coastguard Worker                build_target=fetch_cvd_build_target,
146*800a58d9SAndroid Build Coastguard Worker                build_id=fetch_cvd_version,
147*800a58d9SAndroid Build Coastguard Worker                resource_id=self.FETCHER_NAME,
148*800a58d9SAndroid Build Coastguard Worker                local_dest=local_dest,
149*800a58d9SAndroid Build Coastguard Worker                attempt_id=self.LATEST)
150*800a58d9SAndroid Build Coastguard Worker        except Exception:
151*800a58d9SAndroid Build Coastguard Worker            logger.debug("Download fetch_cvd with build id: %s",
152*800a58d9SAndroid Build Coastguard Worker                         constants.FETCH_CVD_SECOND_VERSION)
153*800a58d9SAndroid Build Coastguard Worker            utils.RetryExceptionType(
154*800a58d9SAndroid Build Coastguard Worker                exception_types=(ssl.SSLError, errors.DriverError),
155*800a58d9SAndroid Build Coastguard Worker                max_retries=self.MAX_RETRY,
156*800a58d9SAndroid Build Coastguard Worker                functor=self.DownloadArtifact,
157*800a58d9SAndroid Build Coastguard Worker                sleep_multiplier=self.RETRY_SLEEP_SECS,
158*800a58d9SAndroid Build Coastguard Worker                retry_backoff_factor=utils.DEFAULT_RETRY_BACKOFF_FACTOR,
159*800a58d9SAndroid Build Coastguard Worker                build_target=fetch_cvd_build_target,
160*800a58d9SAndroid Build Coastguard Worker                build_id=constants.FETCH_CVD_SECOND_VERSION,
161*800a58d9SAndroid Build Coastguard Worker                resource_id=self.FETCHER_NAME,
162*800a58d9SAndroid Build Coastguard Worker                local_dest=local_dest,
163*800a58d9SAndroid Build Coastguard Worker                attempt_id=self.LATEST)
164*800a58d9SAndroid Build Coastguard Worker        fetch_cvd_stat = os.stat(local_dest)
165*800a58d9SAndroid Build Coastguard Worker        os.chmod(local_dest, fetch_cvd_stat.st_mode | stat.S_IEXEC)
166*800a58d9SAndroid Build Coastguard Worker
167*800a58d9SAndroid Build Coastguard Worker    @staticmethod
168*800a58d9SAndroid Build Coastguard Worker    def ProcessBuild(build_info, ignore_artifact=False):
169*800a58d9SAndroid Build Coastguard Worker        """Create a Cuttlefish fetch_cvd build string.
170*800a58d9SAndroid Build Coastguard Worker
171*800a58d9SAndroid Build Coastguard Worker        Args:
172*800a58d9SAndroid Build Coastguard Worker            build_info: The dictionary that contains build information.
173*800a58d9SAndroid Build Coastguard Worker            ignore_artifact: Avoid adding artifact part to fetch_cvd build string
174*800a58d9SAndroid Build Coastguard Worker
175*800a58d9SAndroid Build Coastguard Worker        Returns:
176*800a58d9SAndroid Build Coastguard Worker            A string, used in the fetch_cvd cmd or None if all args are None.
177*800a58d9SAndroid Build Coastguard Worker        """
178*800a58d9SAndroid Build Coastguard Worker        build_id = build_info.get(constants.BUILD_ID)
179*800a58d9SAndroid Build Coastguard Worker        build_target = build_info.get(constants.BUILD_TARGET)
180*800a58d9SAndroid Build Coastguard Worker        branch = build_info.get(constants.BUILD_BRANCH)
181*800a58d9SAndroid Build Coastguard Worker        artifact = build_info.get(constants.BUILD_ARTIFACT)
182*800a58d9SAndroid Build Coastguard Worker
183*800a58d9SAndroid Build Coastguard Worker        result = build_id or branch
184*800a58d9SAndroid Build Coastguard Worker        if build_target is not None:
185*800a58d9SAndroid Build Coastguard Worker            result = result or _DEFAULT_BRANCH
186*800a58d9SAndroid Build Coastguard Worker            result += "/" + build_target
187*800a58d9SAndroid Build Coastguard Worker
188*800a58d9SAndroid Build Coastguard Worker        if not ignore_artifact and artifact:
189*800a58d9SAndroid Build Coastguard Worker            result += "{" + artifact + "}"
190*800a58d9SAndroid Build Coastguard Worker
191*800a58d9SAndroid Build Coastguard Worker        return result
192*800a58d9SAndroid Build Coastguard Worker
193*800a58d9SAndroid Build Coastguard Worker    def GetFetchBuildArgs(self, default_build_info, system_build_info,
194*800a58d9SAndroid Build Coastguard Worker                          kernel_build_info, boot_build_info,
195*800a58d9SAndroid Build Coastguard Worker                          bootloader_build_info, android_efi_loader_build_info,
196*800a58d9SAndroid Build Coastguard Worker                          ota_build_info, host_package_build_info):
197*800a58d9SAndroid Build Coastguard Worker        """Get args from build information for fetch_cvd.
198*800a58d9SAndroid Build Coastguard Worker
199*800a58d9SAndroid Build Coastguard Worker        Each build_info is a dictionary that contains 3 items, for example,
200*800a58d9SAndroid Build Coastguard Worker        {
201*800a58d9SAndroid Build Coastguard Worker            constants.BUILD_ID: "2263051",
202*800a58d9SAndroid Build Coastguard Worker            constants.BUILD_TARGET: "aosp_cf_x86_64_phone-userdebug",
203*800a58d9SAndroid Build Coastguard Worker            constants.BUILD_BRANCH: "aosp-master",
204*800a58d9SAndroid Build Coastguard Worker        }
205*800a58d9SAndroid Build Coastguard Worker
206*800a58d9SAndroid Build Coastguard Worker        Args:
207*800a58d9SAndroid Build Coastguard Worker            default_build_info: The build that provides full cuttlefish images.
208*800a58d9SAndroid Build Coastguard Worker            system_build_info: The build that provides the system image.
209*800a58d9SAndroid Build Coastguard Worker            kernel_build_info: The build that provides the kernel.
210*800a58d9SAndroid Build Coastguard Worker            boot_build_info: The build that provides the boot image. This
211*800a58d9SAndroid Build Coastguard Worker                             dictionary may contain an additional key
212*800a58d9SAndroid Build Coastguard Worker                             constants.BUILD_ARTIFACT which is mapped to the
213*800a58d9SAndroid Build Coastguard Worker                             boot image name.
214*800a58d9SAndroid Build Coastguard Worker            bootloader_build_info: The build that provides the bootloader.
215*800a58d9SAndroid Build Coastguard Worker            android_efi_loader_build_info: The build that provides the Android EFI loader.
216*800a58d9SAndroid Build Coastguard Worker            ota_build_info: The build that provides the OTA tools.
217*800a58d9SAndroid Build Coastguard Worker            host_package_build_info: The build that provides the host package.
218*800a58d9SAndroid Build Coastguard Worker
219*800a58d9SAndroid Build Coastguard Worker        Returns:
220*800a58d9SAndroid Build Coastguard Worker            List of string args for fetch_cvd.
221*800a58d9SAndroid Build Coastguard Worker        """
222*800a58d9SAndroid Build Coastguard Worker        fetch_cvd_args = []
223*800a58d9SAndroid Build Coastguard Worker
224*800a58d9SAndroid Build Coastguard Worker        default_build = self.ProcessBuild(default_build_info)
225*800a58d9SAndroid Build Coastguard Worker        if default_build:
226*800a58d9SAndroid Build Coastguard Worker            fetch_cvd_args.append(f"-default_build={default_build}")
227*800a58d9SAndroid Build Coastguard Worker        system_build = self.ProcessBuild(system_build_info)
228*800a58d9SAndroid Build Coastguard Worker        if system_build:
229*800a58d9SAndroid Build Coastguard Worker            fetch_cvd_args.append(f"-system_build={system_build}")
230*800a58d9SAndroid Build Coastguard Worker        bootloader_build = self.ProcessBuild(bootloader_build_info)
231*800a58d9SAndroid Build Coastguard Worker        if bootloader_build:
232*800a58d9SAndroid Build Coastguard Worker            fetch_cvd_args.append(f"-bootloader_build={bootloader_build}")
233*800a58d9SAndroid Build Coastguard Worker        android_efi_loader_build = self.ProcessBuild(android_efi_loader_build_info)
234*800a58d9SAndroid Build Coastguard Worker        if android_efi_loader_build:
235*800a58d9SAndroid Build Coastguard Worker            fetch_cvd_args.append(f"-android_efi_loader_build {android_efi_loader_build}")
236*800a58d9SAndroid Build Coastguard Worker        kernel_build = self.GetKernelBuild(kernel_build_info)
237*800a58d9SAndroid Build Coastguard Worker        if kernel_build:
238*800a58d9SAndroid Build Coastguard Worker            fetch_cvd_args.append(f"-kernel_build={kernel_build}")
239*800a58d9SAndroid Build Coastguard Worker        boot_build = self.ProcessBuild(boot_build_info, ignore_artifact=True)
240*800a58d9SAndroid Build Coastguard Worker        if boot_build:
241*800a58d9SAndroid Build Coastguard Worker            fetch_cvd_args.append(f"-boot_build={boot_build}")
242*800a58d9SAndroid Build Coastguard Worker            boot_artifact = boot_build_info.get(constants.BUILD_ARTIFACT)
243*800a58d9SAndroid Build Coastguard Worker            if boot_artifact:
244*800a58d9SAndroid Build Coastguard Worker                fetch_cvd_args.append(f"-boot_artifact={boot_artifact}")
245*800a58d9SAndroid Build Coastguard Worker        ota_build = self.ProcessBuild(ota_build_info)
246*800a58d9SAndroid Build Coastguard Worker        if ota_build:
247*800a58d9SAndroid Build Coastguard Worker            fetch_cvd_args.append(f"-otatools_build={ota_build}")
248*800a58d9SAndroid Build Coastguard Worker        host_package_build = self.ProcessBuild(host_package_build_info)
249*800a58d9SAndroid Build Coastguard Worker        if host_package_build:
250*800a58d9SAndroid Build Coastguard Worker            fetch_cvd_args.append(f"-host_package_build={host_package_build}")
251*800a58d9SAndroid Build Coastguard Worker
252*800a58d9SAndroid Build Coastguard Worker        return fetch_cvd_args
253*800a58d9SAndroid Build Coastguard Worker
254*800a58d9SAndroid Build Coastguard Worker    def GetFetcherVersion(self, is_arm_version=False):
255*800a58d9SAndroid Build Coastguard Worker        """Get fetch_cvd build id from LKGB.
256*800a58d9SAndroid Build Coastguard Worker
257*800a58d9SAndroid Build Coastguard Worker        Returns:
258*800a58d9SAndroid Build Coastguard Worker            The build id of fetch_cvd.
259*800a58d9SAndroid Build Coastguard Worker        """
260*800a58d9SAndroid Build Coastguard Worker        # TODO(b/297085994): currently returning hardcoded values
261*800a58d9SAndroid Build Coastguard Worker        # For more information, please check the BUILD_ID constant definition
262*800a58d9SAndroid Build Coastguard Worker        # comment section
263*800a58d9SAndroid Build Coastguard Worker        return self.FETCHER_BUILD_ID_ARM if is_arm_version else self.FETCHER_BUILD_ID
264*800a58d9SAndroid Build Coastguard Worker
265*800a58d9SAndroid Build Coastguard Worker    @staticmethod
266*800a58d9SAndroid Build Coastguard Worker    # pylint: disable=broad-except
267*800a58d9SAndroid Build Coastguard Worker    def GetFetchCertArg(certification_file):
268*800a58d9SAndroid Build Coastguard Worker        """Get cert arg from certification file for fetch_cvd.
269*800a58d9SAndroid Build Coastguard Worker
270*800a58d9SAndroid Build Coastguard Worker        Parse the certification file to get access token of the latest
271*800a58d9SAndroid Build Coastguard Worker        credential data and pass it to fetch_cvd command.
272*800a58d9SAndroid Build Coastguard Worker        Example of certification file:
273*800a58d9SAndroid Build Coastguard Worker        {
274*800a58d9SAndroid Build Coastguard Worker          "data": [
275*800a58d9SAndroid Build Coastguard Worker          {
276*800a58d9SAndroid Build Coastguard Worker            "credential": {
277*800a58d9SAndroid Build Coastguard Worker              "_class": "OAuth2Credentials",
278*800a58d9SAndroid Build Coastguard Worker              "_module": "oauth2client.client",
279*800a58d9SAndroid Build Coastguard Worker              "access_token": "token_strings",
280*800a58d9SAndroid Build Coastguard Worker              "client_id": "179485041932",
281*800a58d9SAndroid Build Coastguard Worker            }
282*800a58d9SAndroid Build Coastguard Worker          }]
283*800a58d9SAndroid Build Coastguard Worker        }
284*800a58d9SAndroid Build Coastguard Worker
285*800a58d9SAndroid Build Coastguard Worker
286*800a58d9SAndroid Build Coastguard Worker        Args:
287*800a58d9SAndroid Build Coastguard Worker            certification_file: String of certification file path.
288*800a58d9SAndroid Build Coastguard Worker
289*800a58d9SAndroid Build Coastguard Worker        Returns:
290*800a58d9SAndroid Build Coastguard Worker            String of certificate arg for fetch_cvd. If there is no
291*800a58d9SAndroid Build Coastguard Worker            certification file, return empty string for aosp branch.
292*800a58d9SAndroid Build Coastguard Worker        """
293*800a58d9SAndroid Build Coastguard Worker        cert_arg = ""
294*800a58d9SAndroid Build Coastguard Worker        try:
295*800a58d9SAndroid Build Coastguard Worker            with open(certification_file) as cert_file:
296*800a58d9SAndroid Build Coastguard Worker                auth_token = json.load(cert_file).get("data")[-1].get(
297*800a58d9SAndroid Build Coastguard Worker                    "credential").get("access_token")
298*800a58d9SAndroid Build Coastguard Worker                if auth_token:
299*800a58d9SAndroid Build Coastguard Worker                    cert_arg = f"-credential_source={auth_token}"
300*800a58d9SAndroid Build Coastguard Worker        except Exception as e:
301*800a58d9SAndroid Build Coastguard Worker            utils.PrintColorString(
302*800a58d9SAndroid Build Coastguard Worker                f"Fail to open the certification file "
303*800a58d9SAndroid Build Coastguard Worker                f"({certification_file}): {e}",
304*800a58d9SAndroid Build Coastguard Worker                utils.TextColors.WARNING)
305*800a58d9SAndroid Build Coastguard Worker        return cert_arg
306*800a58d9SAndroid Build Coastguard Worker
307*800a58d9SAndroid Build Coastguard Worker    def GetKernelBuild(self, kernel_build_info):
308*800a58d9SAndroid Build Coastguard Worker        """Get kernel build args for fetch_cvd.
309*800a58d9SAndroid Build Coastguard Worker
310*800a58d9SAndroid Build Coastguard Worker        Args:
311*800a58d9SAndroid Build Coastguard Worker            kernel_build_info: The dictionary that contains build information.
312*800a58d9SAndroid Build Coastguard Worker
313*800a58d9SAndroid Build Coastguard Worker        Returns:
314*800a58d9SAndroid Build Coastguard Worker            String of kernel build args for fetch_cvd.
315*800a58d9SAndroid Build Coastguard Worker            If no kernel build then return None.
316*800a58d9SAndroid Build Coastguard Worker        """
317*800a58d9SAndroid Build Coastguard Worker        # kernel_target have default value "kernel". If user provide kernel_build_id
318*800a58d9SAndroid Build Coastguard Worker        # or kernel_branch, then start to process kernel image.
319*800a58d9SAndroid Build Coastguard Worker        if (kernel_build_info.get(constants.BUILD_ID) or
320*800a58d9SAndroid Build Coastguard Worker                kernel_build_info.get(constants.BUILD_BRANCH)):
321*800a58d9SAndroid Build Coastguard Worker            return self.ProcessBuild(kernel_build_info)
322*800a58d9SAndroid Build Coastguard Worker        return None
323*800a58d9SAndroid Build Coastguard Worker
324*800a58d9SAndroid Build Coastguard Worker    def CopyTo(self,
325*800a58d9SAndroid Build Coastguard Worker               build_target,
326*800a58d9SAndroid Build Coastguard Worker               build_id,
327*800a58d9SAndroid Build Coastguard Worker               artifact_name,
328*800a58d9SAndroid Build Coastguard Worker               destination_bucket,
329*800a58d9SAndroid Build Coastguard Worker               destination_path,
330*800a58d9SAndroid Build Coastguard Worker               attempt_id=None):
331*800a58d9SAndroid Build Coastguard Worker        """Copy an Android Build artifact to a storage bucket.
332*800a58d9SAndroid Build Coastguard Worker
333*800a58d9SAndroid Build Coastguard Worker        Args:
334*800a58d9SAndroid Build Coastguard Worker            build_target: Target name, e.g. "aosp_cf_x86_64_phone-userdebug"
335*800a58d9SAndroid Build Coastguard Worker            build_id: Build id, a string, e.g. "2263051", "P2804227"
336*800a58d9SAndroid Build Coastguard Worker            artifact_name: Name of the artifact, e.g "avd-system.tar.gz".
337*800a58d9SAndroid Build Coastguard Worker            destination_bucket: String, a google storage bucket name.
338*800a58d9SAndroid Build Coastguard Worker            destination_path: String, "path/inside/bucket"
339*800a58d9SAndroid Build Coastguard Worker            attempt_id: String, attempt id, will default to DEFAULT_ATTEMPT_ID.
340*800a58d9SAndroid Build Coastguard Worker        """
341*800a58d9SAndroid Build Coastguard Worker        attempt_id = attempt_id or self.DEFAULT_ATTEMPT_ID
342*800a58d9SAndroid Build Coastguard Worker        copy_msg = "Copying %s" % self.COPY_TO_MSG
343*800a58d9SAndroid Build Coastguard Worker        logger.info(copy_msg, build_target, build_id, artifact_name,
344*800a58d9SAndroid Build Coastguard Worker                    attempt_id, destination_bucket, destination_path)
345*800a58d9SAndroid Build Coastguard Worker        api = self.service.buildartifact().copyTo(
346*800a58d9SAndroid Build Coastguard Worker            buildId=build_id,
347*800a58d9SAndroid Build Coastguard Worker            target=build_target,
348*800a58d9SAndroid Build Coastguard Worker            attemptId=attempt_id,
349*800a58d9SAndroid Build Coastguard Worker            artifactName=artifact_name,
350*800a58d9SAndroid Build Coastguard Worker            destinationBucket=destination_bucket,
351*800a58d9SAndroid Build Coastguard Worker            destinationPath=destination_path)
352*800a58d9SAndroid Build Coastguard Worker        try:
353*800a58d9SAndroid Build Coastguard Worker            self.Execute(api)
354*800a58d9SAndroid Build Coastguard Worker            finish_msg = "Finished copying %s" % self.COPY_TO_MSG
355*800a58d9SAndroid Build Coastguard Worker            logger.info(finish_msg, build_target, build_id, artifact_name,
356*800a58d9SAndroid Build Coastguard Worker                        attempt_id, destination_bucket, destination_path)
357*800a58d9SAndroid Build Coastguard Worker        except errors.HttpError as e:
358*800a58d9SAndroid Build Coastguard Worker            if e.code == 503:
359*800a58d9SAndroid Build Coastguard Worker                if self.NO_ACCESS_ERROR_PATTERN in str(e):
360*800a58d9SAndroid Build Coastguard Worker                    error_msg = "Please grant android build team's service account "
361*800a58d9SAndroid Build Coastguard Worker                    error_msg += "write access to bucket %s. Original error: %s"
362*800a58d9SAndroid Build Coastguard Worker                    error_msg %= (destination_bucket, str(e))
363*800a58d9SAndroid Build Coastguard Worker                    raise errors.HttpError(e.code, message=error_msg)
364*800a58d9SAndroid Build Coastguard Worker            raise
365*800a58d9SAndroid Build Coastguard Worker
366*800a58d9SAndroid Build Coastguard Worker    def GetBranch(self, build_target, build_id):
367*800a58d9SAndroid Build Coastguard Worker        """Derives branch name.
368*800a58d9SAndroid Build Coastguard Worker
369*800a58d9SAndroid Build Coastguard Worker        Args:
370*800a58d9SAndroid Build Coastguard Worker            build_target: Target name, e.g. "aosp_cf_x86_64_phone-userdebug"
371*800a58d9SAndroid Build Coastguard Worker            build_id: Build ID, a string, e.g. "2263051", "P2804227"
372*800a58d9SAndroid Build Coastguard Worker
373*800a58d9SAndroid Build Coastguard Worker        Returns:
374*800a58d9SAndroid Build Coastguard Worker            A string, the name of the branch
375*800a58d9SAndroid Build Coastguard Worker        """
376*800a58d9SAndroid Build Coastguard Worker        api = self.service.build().get(buildId=build_id, target=build_target)
377*800a58d9SAndroid Build Coastguard Worker        build = self.Execute(api)
378*800a58d9SAndroid Build Coastguard Worker        return build.get("branch", "")
379*800a58d9SAndroid Build Coastguard Worker
380*800a58d9SAndroid Build Coastguard Worker    def GetLKGB(self, build_target, build_branch):
381*800a58d9SAndroid Build Coastguard Worker        """Get latest successful build id.
382*800a58d9SAndroid Build Coastguard Worker
383*800a58d9SAndroid Build Coastguard Worker        From branch and target, we can use api to query latest successful build id.
384*800a58d9SAndroid Build Coastguard Worker        e.g. {u'nextPageToken':..., u'builds': [{u'completionTimestamp':u'1534157869286',
385*800a58d9SAndroid Build Coastguard Worker        ... u'buildId': u'4949805', u'machineName'...}]}
386*800a58d9SAndroid Build Coastguard Worker
387*800a58d9SAndroid Build Coastguard Worker        Args:
388*800a58d9SAndroid Build Coastguard Worker            build_target: String, target name, e.g. "aosp_cf_x86_64_phone-userdebug"
389*800a58d9SAndroid Build Coastguard Worker            build_branch: String, git branch name, e.g. "aosp-master"
390*800a58d9SAndroid Build Coastguard Worker
391*800a58d9SAndroid Build Coastguard Worker        Returns:
392*800a58d9SAndroid Build Coastguard Worker            A string, string of build id number.
393*800a58d9SAndroid Build Coastguard Worker
394*800a58d9SAndroid Build Coastguard Worker        Raises:
395*800a58d9SAndroid Build Coastguard Worker            errors.CreateError: Can't get build id.
396*800a58d9SAndroid Build Coastguard Worker        """
397*800a58d9SAndroid Build Coastguard Worker        api = self.service.build().list(
398*800a58d9SAndroid Build Coastguard Worker            branch=build_branch,
399*800a58d9SAndroid Build Coastguard Worker            target=build_target,
400*800a58d9SAndroid Build Coastguard Worker            buildAttemptStatus=self.BUILD_STATUS_COMPLETE,
401*800a58d9SAndroid Build Coastguard Worker            buildType=self.BUILD_TYPE_SUBMITTED,
402*800a58d9SAndroid Build Coastguard Worker            maxResults=self.ONE_RESULT,
403*800a58d9SAndroid Build Coastguard Worker            successful=self.BUILD_SUCCESSFUL)
404*800a58d9SAndroid Build Coastguard Worker        build = self.Execute(api)
405*800a58d9SAndroid Build Coastguard Worker        logger.info("GetLKGB build API response: %s", build)
406*800a58d9SAndroid Build Coastguard Worker        if build:
407*800a58d9SAndroid Build Coastguard Worker            return str(build.get("builds")[0].get("buildId"))
408*800a58d9SAndroid Build Coastguard Worker        raise errors.GetBuildIDError(
409*800a58d9SAndroid Build Coastguard Worker            "No available good builds for branch: %s target: %s"
410*800a58d9SAndroid Build Coastguard Worker            % (build_branch, build_target)
411*800a58d9SAndroid Build Coastguard Worker        )
412*800a58d9SAndroid Build Coastguard Worker
413*800a58d9SAndroid Build Coastguard Worker    def GetBuildInfo(self, build_target, build_id, branch):
414*800a58d9SAndroid Build Coastguard Worker        """Get build info namedtuple.
415*800a58d9SAndroid Build Coastguard Worker
416*800a58d9SAndroid Build Coastguard Worker        Args:
417*800a58d9SAndroid Build Coastguard Worker          build_target: Target name.
418*800a58d9SAndroid Build Coastguard Worker          build_id: Build id, a string or None, e.g. "2263051", "P2804227"
419*800a58d9SAndroid Build Coastguard Worker                    If None or latest, the last green build id will be
420*800a58d9SAndroid Build Coastguard Worker                    returned.
421*800a58d9SAndroid Build Coastguard Worker          branch: Branch name, a string or None, e.g. git_master. If None, the
422*800a58d9SAndroid Build Coastguard Worker                  returned branch will be searched by given build_id.
423*800a58d9SAndroid Build Coastguard Worker
424*800a58d9SAndroid Build Coastguard Worker        Returns:
425*800a58d9SAndroid Build Coastguard Worker          A build info namedtuple with keys build_target, build_id, branch and
426*800a58d9SAndroid Build Coastguard Worker          gcs_bucket_build_id
427*800a58d9SAndroid Build Coastguard Worker        """
428*800a58d9SAndroid Build Coastguard Worker        if build_id and build_id != self.LATEST:
429*800a58d9SAndroid Build Coastguard Worker            # Get build from build_id and build_target
430*800a58d9SAndroid Build Coastguard Worker            api = self.service.build().get(buildId=build_id,
431*800a58d9SAndroid Build Coastguard Worker                                           target=build_target)
432*800a58d9SAndroid Build Coastguard Worker            build = self.Execute(api) or {}
433*800a58d9SAndroid Build Coastguard Worker        elif branch:
434*800a58d9SAndroid Build Coastguard Worker            # Get last green build in the branch
435*800a58d9SAndroid Build Coastguard Worker            api = self.service.build().list(
436*800a58d9SAndroid Build Coastguard Worker                branch=branch,
437*800a58d9SAndroid Build Coastguard Worker                target=build_target,
438*800a58d9SAndroid Build Coastguard Worker                successful=True,
439*800a58d9SAndroid Build Coastguard Worker                maxResults=1,
440*800a58d9SAndroid Build Coastguard Worker                buildType="submitted")
441*800a58d9SAndroid Build Coastguard Worker            builds = self.Execute(api).get("builds", [])
442*800a58d9SAndroid Build Coastguard Worker            build = builds[0] if builds else {}
443*800a58d9SAndroid Build Coastguard Worker        else:
444*800a58d9SAndroid Build Coastguard Worker            build = {}
445*800a58d9SAndroid Build Coastguard Worker
446*800a58d9SAndroid Build Coastguard Worker        build_id = build.get("buildId")
447*800a58d9SAndroid Build Coastguard Worker        build_target = build_target if build_id else None
448*800a58d9SAndroid Build Coastguard Worker        return BuildInfo(build.get("branch"), build_id, build_target,
449*800a58d9SAndroid Build Coastguard Worker                         build.get("releaseCandidateName"))
450