1# Copyright (C) 2020 The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14# 15# Licensed under the Apache License, Version 2.0 (the "License"); 16# you may not use this file except in compliance with the License. 17# You may obtain a copy of the License at 18# 19# http://www.apache.org/licenses/LICENSE-2.0 20# 21# Unless required by applicable law or agreed to in writing, software 22# distributed under the License is distributed on an "AS IS" BASIS, 23# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24# See the License for the specific language governing permissions and 25# limitations under the License. 26# 27# Licensed under the Apache License, Version 2.0 (the "License"); 28# you may not use this file except in compliance with the License. 29# You may obtain a copy of the License at 30# 31# http://www.apache.org/licenses/LICENSE-2.0 32# 33# Unless required by applicable law or agreed to in writing, software 34# distributed under the License is distributed on an "AS IS" BASIS, 35# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 36# See the License for the specific language governing permissions and 37# limitations under the License. 38 39 40""" Utility functions for logstorage. """ 41from __future__ import print_function 42 43import logging 44import uuid 45import time 46 47# pylint: disable=import-error 48try: 49 import httplib2 50 from googleapiclient.discovery import build 51except ImportError as e: 52 logging.debug('Import error due to: %s', e) 53 54from atest import constants 55from atest import metrics 56 57 58class BuildClient: 59 """Build api helper class.""" 60 61 def __init__(self, creds): 62 """Init BuildClient class. 63 Args: 64 creds: An oauth2client.OAuth2Credentials instance. 65 """ 66 http_auth = creds.authorize(httplib2.Http()) 67 self.client = build( 68 serviceName=constants.STORAGE_SERVICE_NAME, 69 version=constants.STORAGE_API_VERSION, 70 cache_discovery=False, 71 http=http_auth, 72 discoveryServiceUrl=constants.DISCOVERY_SERVICE) 73 74 def list_branch(self): 75 """List all branch.""" 76 return self.client.branch().list(maxResults=10000).execute() 77 78 def list_target(self, branch): 79 """List all target in the branch.""" 80 return self.client.target().list(branch=branch, 81 maxResults=10000).execute() 82 83 def get_branch(self, branch): 84 """Get BuildInfo for specific branch. 85 Args: 86 branch: A string of branch name to query. 87 """ 88 query_branch = '' 89 try: 90 query_branch = self.client.branch().get(resourceId=branch).execute() 91 # pylint: disable=broad-except 92 except Exception: 93 return '' 94 return query_branch 95 96 def insert_local_build(self, external_id, target, branch): 97 """Insert a build record. 98 Args: 99 external_id: unique id of build record. 100 target: build target. 101 branch: build branch. 102 103 Returns: 104 An build record object. 105 """ 106 body = { 107 "buildId": "", 108 "externalId": external_id, 109 "branch": branch, 110 "target": { 111 "name": target, 112 "target": target 113 }, 114 "buildAttemptStatus": "complete", 115 } 116 return self.client.build().insert(buildType="local", 117 body=body).execute() 118 119 def insert_build_attempts(self, build_record): 120 """Insert a build attempt record. 121 Args: 122 build_record: build record. 123 124 Returns: 125 An build attempt object. 126 """ 127 build_attempt = { 128 "id": 0, 129 "status": "complete", 130 "successful": True 131 } 132 return self.client.buildattempt().insert( 133 buildId=build_record['buildId'], 134 target=build_record['target']['name'], 135 body=build_attempt).execute() 136 137 def insert_invocation(self, build_record): 138 """Insert a build invocation record. 139 Args: 140 build_record: build record. 141 142 Returns: 143 A build invocation object. 144 """ 145 sponge_invocation_id = str(uuid.uuid4()) 146 user_email = metrics.metrics_base.get_user_email() 147 invocation = { 148 "primaryBuild": { 149 "buildId": build_record['buildId'], 150 "buildTarget": build_record['target']['name'], 151 "branch": build_record['branch'], 152 }, 153 "schedulerState": "running", 154 "runner": "atest", 155 "scheduler": "atest", 156 "users": [user_email], 157 "properties": [{ 158 'name': 'sponge_invocation_id', 159 'value': sponge_invocation_id, 160 }, { 161 'name': 'test_uri', 162 'value': f'{constants.STORAGE2_TEST_URI}{sponge_invocation_id}' 163 }] 164 } 165 return self.client.invocation().insert(body=invocation).execute() 166 167 def update_invocation(self, invocation): 168 """Insert a build invocation record. 169 Args: 170 invocation: invocation record. 171 172 Returns: 173 A invocation object. 174 """ 175 # Because invocation revision will be update by TF, we need to fetch 176 # latest invocation revision to update status correctly. 177 count = 0 178 invocations = None 179 while count < 5: 180 invocations = self.client.invocation().list( 181 invocationId=invocation['invocationId'], 182 maxResults=10).execute().get('invocations', []) 183 if invocations: 184 break 185 time.sleep(0.5) 186 count = count + 1 187 if invocations: 188 latest_revision = invocations[-1].get('revision', '') 189 if latest_revision: 190 logging.debug('Get latest_revision:%s from invocations:%s', 191 latest_revision, invocations) 192 invocation['revision'] = latest_revision 193 return self.client.invocation().update( 194 resourceId=invocation['invocationId'], 195 body=invocation).execute() 196 197 def insert_work_unit(self, invocation_record): 198 """Insert a workunit record. 199 Args: 200 invocation_record: invocation record. 201 202 Returns: 203 the workunit object. 204 """ 205 workunit = { 206 'invocationId': invocation_record['invocationId'] 207 } 208 return self.client.workunit().insert(body=workunit).execute() 209