blob: b459f62127431443643d9475b1bd84dc3ceb3025 [file] [log] [blame]
Tri Voe6638de2016-10-03 19:39:03 -07001#!/usr/bin/env python
2#
3# Copyright 2016 - The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""A client that talks to Android Build APIs."""
18
19import io
20import logging
21import os
22
23import apiclient
24
25from acloud.internal.lib import base_cloud_client
26from acloud.public import errors
27
28logger = logging.getLogger(__name__)
29
30
31class AndroidBuildClient(base_cloud_client.BaseCloudApiClient):
32 """Client that manages Android Build."""
33
34 # API settings, used by BaseCloudApiClient.
35 API_NAME = "androidbuildinternal"
36 API_VERSION = "v2beta1"
37 SCOPE = "https://www.googleapis.com/auth/androidbuild.internal"
38
39 # other variables.
40 DEFAULT_RESOURCE_ID = "0"
41 # TODO(fdeng): We should use "latest".
42 DEFAULT_ATTEMPT_ID = "0"
43 DEFAULT_CHUNK_SIZE = 20 * 1024 * 1024
44 NO_ACCESS_ERROR_PATTERN = "does not have storage.objects.create access"
45
46 # Message constant
47 COPY_TO_MSG = ("build artifact (target: %s, build_id: %s, "
48 "artifact: %s, attempt_id: %s) to "
49 "google storage (bucket: %s, path: %s)")
50
51 def DownloadArtifact(self,
52 build_target,
53 build_id,
54 resource_id,
55 local_dest,
56 attempt_id=None):
57 """Get Android build attempt information.
58
59 Args:
60 build_target: Target name, e.g. "gce_x86-userdebug"
61 build_id: Build id, a string, e.g. "2263051", "P2804227"
62 resource_id: Id of the resource, e.g "avd-system.tar.gz".
63 local_dest: A local path where the artifact should be stored.
64 e.g. "/tmp/avd-system.tar.gz"
65 attempt_id: String, attempt id, will default to DEFAULT_ATTEMPT_ID.
66 """
67 attempt_id = attempt_id or self.DEFAULT_ATTEMPT_ID
68 api = self.service.buildartifact().get_media(
69 buildId=build_id,
70 target=build_target,
71 attemptId=attempt_id,
72 resourceId=resource_id)
73 logger.info("Downloading artifact: target: %s, build_id: %s, "
74 "resource_id: %s, dest: %s", build_target, build_id,
75 resource_id, local_dest)
76 try:
77 with io.FileIO(local_dest, mode="wb") as fh:
78 downloader = apiclient.http.MediaIoBaseDownload(
79 fh, api, chunksize=self.DEFAULT_CHUNK_SIZE)
80 done = False
81 while not done:
82 _, done = downloader.next_chunk()
83 logger.info("Downloaded artifact: %s", local_dest)
84 except OSError as e:
85 logger.error("Downloading artifact failed: %s", str(e))
86 raise errors.DriverError(str(e))
87
88 def CopyTo(self,
89 build_target,
90 build_id,
91 artifact_name,
92 destination_bucket,
93 destination_path,
94 attempt_id=None):
95 """Copy an Android Build artifact to a storage bucket.
96
97 Args:
98 build_target: Target name, e.g. "gce_x86-userdebug"
99 build_id: Build id, a string, e.g. "2263051", "P2804227"
100 artifact_name: Name of the artifact, e.g "avd-system.tar.gz".
101 destination_bucket: String, a google storage bucket name.
102 destination_path: String, "path/inside/bucket"
103 attempt_id: String, attempt id, will default to DEFAULT_ATTEMPT_ID.
104 """
105 attempt_id = attempt_id or self.DEFAULT_ATTEMPT_ID
106 copy_msg = "Copying %s" % self.COPY_TO_MSG
107 logger.info(copy_msg, build_target, build_id, artifact_name,
108 attempt_id, destination_bucket, destination_path)
109 api = self.service.buildartifact().copyTo(
110 buildId=build_id,
111 target=build_target,
112 attemptId=attempt_id,
113 artifactName=artifact_name,
114 destinationBucket=destination_bucket,
115 destinationPath=destination_path)
116 try:
117 self.Execute(api)
118 finish_msg = "Finished copying %s" % self.COPY_TO_MSG
119 logger.info(finish_msg, build_target, build_id, artifact_name,
120 attempt_id, destination_bucket, destination_path)
121 except errors.HttpError as e:
122 if e.code == 503:
123 if self.NO_ACCESS_ERROR_PATTERN in str(e):
124 error_msg = "Please grant android build team's service account "
125 error_msg += "write access to bucket %s. Original error: %s"
126 error_msg %= (destination_bucket, str(e))
127 raise errors.HttpError(e.code, message=error_msg)
128 raise