blob: cae3b3b5f6d2af1edfd75e9f40f5ab43db07da8e [file] [log] [blame]
Dan Shi72b16132015-10-08 12:10:33 -07001# Copyright 2015 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Helper methods to make Google API call to query Android build server."""
6
7from __future__ import print_function
8
9import apiclient
10import httplib2
11import io
12
13from apiclient import discovery
14from oauth2client.client import SignedJwtAssertionCredentials
15
16CREDENTIAL_SCOPE = 'https://www.googleapis.com/auth/androidbuild.internal'
17DEFAULT_BUILDER = 'androidbuildinternal'
18DEFAULT_CHUNKSIZE = 20*1024*1024
19
20class AndroidBuildFetchError(Exception):
21 """Exception to raise when failed to make calls to Android build server."""
22
23class BuildAccessor(object):
24 """Wrapper class to make Google API call to query Android build server."""
25
26 # Credential information is required to access Android builds. The values will
27 # be set when the devserver starts.
28 credential_info = None
29
30 @classmethod
31 def _GetServiceObject(cls):
32 """Returns a service object with given credential information."""
33 if not cls.credential_info:
34 raise AndroidBuildFetchError('Android Build credential is missing.')
35
36 credentials = SignedJwtAssertionCredentials(
37 cls.credential_info['client_email'],
38 cls.credential_info['private_key'], CREDENTIAL_SCOPE)
39 http_auth = credentials.authorize(httplib2.Http())
40 service_obj = discovery.build(DEFAULT_BUILDER, 'v1', http=http_auth)
41 return service_obj
42
43 @classmethod
44 def _VerifyBranch(cls, service_obj, branch, build_id, target):
45 """Verify the build with given id and target is for the specified branch.
46
47 Args:
48 service_obj: A service object to be used to make API call to build server.
49 branch: branch of the desired build.
50 build_id: Build id of the Android build, e.g., 2155602.
51 target: Target of the Android build, e.g., shamu-userdebug.
52
53 Raises:
54 AndroidBuildFetchError: If the given build id and target are not for the
55 specified branch.
56 """
57 builds = service_obj.build().list(
58 buildType='submitted', branch=branch, buildId=build_id, target=target,
59 maxResults=0).execute()
60 if not builds:
61 raise AndroidBuildFetchError(
62 'Failed to locate build with branch %s, build id %s and target %s.' %
63 (branch, build_id, target))
64
65 @classmethod
66 def GetArtifacts(cls, branch, build_id, target):
67 """Get the list of artifacts for given build id and target.
68
69 The return value is a list of dictionaries, each containing information
70 about an artifact.
71 For example:
72 {u'contentType': u'application/octet-stream',
73 u'crc32': 4131231264,
74 u'lastModifiedTime': u'143518405786',
75 u'md5': u'c04c823a64293aa5bf508e2eb4683ec8',
76 u'name': u'fastboot',
77 u'revision': u'HsXLpGsgEaqj654THKvR/A==',
78 u'size': u'6999296'},
79
80 Args:
81 branch: branch of the desired build.
82 build_id: Build id of the Android build, e.g., 2155602.
83 target: Target of the Android build, e.g., shamu-userdebug.
84
85 Returns:
86 A list of artifacts for given build id and target.
87 """
88 service_obj = cls._GetServiceObject()
89 cls._VerifyBranch(service_obj, branch, build_id, target)
90
91 # Get all artifacts for the given build_id and target.
92 artifacts = service_obj.buildartifact().list(
93 buildType='submitted', buildId=build_id, target=target,
94 attemptId='latest', maxResults=0).execute()
95 return artifacts['artifacts']
96
97 @classmethod
98 def Download(cls, branch, build_id, target, resource_id, dest_file):
99 """Get the list of artifacts for given build id and target.
100
101 Args:
102 branch: branch of the desired build.
103 build_id: Build id of the Android build, e.g., 2155602.
104 target: Target of the Android build, e.g., shamu-userdebug.
105 resource_id: Name of the artifact to donwload.
106 dest_file: Path to the file to download to.
107 """
108 service_obj = cls._GetServiceObject()
109 cls._VerifyBranch(service_obj, branch, build_id, target)
110
111 # TODO(dshi): Add retry logic here to avoid API flakes.
112 download_req = service_obj.buildartifact().get_media(
113 buildType='submitted', buildId=build_id, target=target,
114 attemptId='latest', resourceId=resource_id)
115 with io.FileIO(dest_file, mode='wb') as fh:
116 downloader = apiclient.http.MediaIoBaseDownload(
117 fh, download_req, chunksize=DEFAULT_CHUNKSIZE)
118 done = None
119 while not done:
120 _, done = downloader.next_chunk()