Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(736)

Side by Side Diff: appengine/chromium_rietveld/codereview/buildbucket.py

Issue 1344253002: Rietveld: schedule builds on buildbucket (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright 2008 Google Inc. 1 # Copyright 2008 Google Inc.
2 # 2 #
3 # Licensed under the Apache License, Version 2.0 (the "License"); 3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with 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 5 # You may obtain a copy of the License at
6 # 6 #
7 # http://www.apache.org/licenses/LICENSE-2.0 7 # http://www.apache.org/licenses/LICENSE-2.0
8 # 8 #
9 # Unless required by applicable law or agreed to in writing, software 9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, 10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and 12 # See the License for the specific language governing permissions and
13 # limitations under the License. 13 # limitations under the License.
14 14
15 """Rietveld-BuildBucket integration module.""" 15 """Rietveld-BuildBucket integration module."""
16 16
17 import datetime 17 import datetime
18 import json 18 import json
19 import logging 19 import logging
20 import os 20 import os
21 import urllib 21 import urllib
22 import uuid
22 23
23 from google.appengine.api import app_identity 24 from google.appengine.api import app_identity
24 from google.appengine.api import memcache 25 from google.appengine.api import memcache
25 from google.appengine.api import users 26 from google.appengine.api import users
26 from google.appengine.ext import ndb 27 from google.appengine.ext import ndb
27 28
28 from django.conf import settings 29 from django.conf import settings
29 30
30 from codereview import common 31 from codereview import common
31 from codereview import models 32 from codereview import models
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
153 def get_builds_for_patchset_async(project, issue_id, patchset_id): 154 def get_builds_for_patchset_async(project, issue_id, patchset_id):
154 """Queries BuildBucket for builds associated with the patchset. 155 """Queries BuildBucket for builds associated with the patchset.
155 156
156 Requests for max 500 builds and does not check "next_cursor". Currently if 157 Requests for max 500 builds and does not check "next_cursor". Currently if
157 more than 100 builds are requested, only 100 are returned. Presumably there 158 more than 100 builds are requested, only 100 are returned. Presumably there
158 will be no patchsets with >100 builds. 159 will be no patchsets with >100 builds.
159 160
160 Returns: 161 Returns:
161 A list of buildbucket build dicts. 162 A list of buildbucket build dicts.
162 """ 163 """
163 # See tag conventions http://cr-buildbucket.appspot.com/#docs/conventions . 164 buildset_tag = get_buildset_for(project, issue_id, patchset_id)
164 hostname = common.get_preferred_domain(project, default_to_appid=False)
165 if not hostname:
166 logging.error(
167 'Preferred domain name for this app is not set. '
168 'See PREFERRED_DOMAIN_NAMES in settings.py: %r', hostname)
169 raise ndb.Return([])
170
171 buildset_tag = BUILDSET_TAG_FORMAT.format(
172 hostname=hostname,
173 issue=issue_id,
174 patch=patchset_id,
175 )
176 params = { 165 params = {
177 'max_builds': 500, 166 'max_builds': 500,
178 'tag': 'buildset:%s' % buildset_tag, 167 'tag': 'buildset:%s' % buildset_tag,
179 } 168 }
180 169
181 logging.info( 170 logging.info(
182 'Fetching builds for patchset %s/%s. Buildset: %s', 171 'Fetching builds for patchset %s/%s. Buildset: %s',
183 issue_id, patchset_id, buildset_tag) 172 issue_id, patchset_id, buildset_tag)
184 try: 173 try:
185 resp = yield rpc_async('GET', 'search', params=params) 174 resp = yield rpc_async('GET', 'search', params=params)
(...skipping 19 matching lines...) Expand all
205 try_job_result = BuildbucketTryJobResult.from_build(build) 194 try_job_result = BuildbucketTryJobResult.from_build(build)
206 if not try_job_result.builder: 195 if not try_job_result.builder:
207 logging.info( 196 logging.info(
208 'Build %s does not have a builder' % try_job_result.build_id) 197 'Build %s does not have a builder' % try_job_result.build_id)
209 continue 198 continue
210 results.append(try_job_result) 199 results.append(try_job_result)
211 raise ndb.Return(results) 200 raise ndb.Return(results)
212 201
213 202
214 ################################################################################ 203 ################################################################################
204 ## Scheduling builds.
205
206
207 def schedule(issue, patchset_id, builds):
208 """Schedules builds on buildbucket.
209
210 |builds| is a list of (master, builder) tuples, where master must not have
211 '.master' prefix.
212 """
213 account = models.Account.current_user_account
214 assert account, 'User is not logged in; cannot schedule builds.'
215
216 if not builds:
217 return
218
219 self_hostname = common.get_preferred_domain(issue.project)
220
221 req = {'builds':[]}
222 opid = uuid.uuid4()
223
224 for i, (master, builder) in enumerate(builds):
225 # Build definitions are similar to what CQ produces:
226 # https://chrome-internal.googlesource.com/infra/infra_internal/+/c3092da989 75c7a3e083093f21f0f4130c66a51c/commit_queue/buildbucket_util.py#171
227 req['builds'].append({
228 'bucket': 'master.%s' % master,
229 'parameters_json': json.dumps({
230 'builder_name': builder,
231 'changes': [{
232 'author': {'email': issue.owner.email()},
233 'url': 'https://%s/%s/%s/' % (
234 self_hostname, issue.key.id(), patchset_id)
235 }],
236 'properties': {
237 'issue': issue.key.id(),
238 'master': master,
239 'patch_project': issue.project,
240 'patch_storage': 'rietveld',
241 'patchset': patchset_id,
242 'project': issue.project,
243 'rietveld': self_hostname,
244 },
245 }),
246 'tags': [
247 'builder:%s' % builder,
248 'buildset:%s' % get_buildset_for(
249 issue.project, issue.key.id(), patchset_id),
250 'master:%s' % master,
251 'user_agent:rietveld',
252 ],
253 'client_operation_id': '%s:%s' % (opid, i),
254 })
255
256 logging.debug(
257 'Scheduling %d builds on behalf of %s', len(req['builds']), account.email)
258 res = rpc_async('PUT', 'builds/batch', payload=req).get_result()
259 for r in res['results']:
260 error = r.get('error')
261 if error:
262 logging.error('Build scheduling failed. Response: %r', res)
263 raise BuildBucketError('Could not schedule build(s): %r' % error)
Vadim Sh. 2015/09/16 21:57:38 how is this displayed to the user?
nodir 2015/09/17 17:44:17 very rough: on any 500 the old UI shows an alert t
264
265 actual_builds = [r['build'] for r in res['results']]
266 logging.info(
267 'Scheduled buildbucket builds: %r',
268 ', '.join([str(b['id']) for b in actual_builds]))
269 return actual_builds
270
271
272 ################################################################################
215 ## Buildbucket RPC. 273 ## Buildbucket RPC.
216 274
217 275
218 @ndb.tasklet 276 @ndb.tasklet
219 def _mint_delegation_token_async(): 277 def _mint_delegation_token_async():
220 """Generates an access token to impersonate the current user, if any. 278 """Generates an access token to impersonate the current user, if any.
221 279
222 Memcaches the token. 280 Memcaches the token.
223 """ 281 """
224 account = models.Account.current_user_account 282 account = models.Account.current_user_account
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
319 return None 377 return None
320 return value 378 return value
321 379
322 380
323 def timestamp_to_datetime(timestamp): 381 def timestamp_to_datetime(timestamp):
324 if timestamp is None: 382 if timestamp is None:
325 return None 383 return None
326 if isinstance(timestamp, basestring): 384 if isinstance(timestamp, basestring):
327 timestamp = int(timestamp) 385 timestamp = int(timestamp)
328 return EPOCH + datetime.timedelta(microseconds=timestamp) 386 return EPOCH + datetime.timedelta(microseconds=timestamp)
387
388
389 def get_buildset_for(project, issue_id, patchset_id):
390 # See tag conventions http://cr-buildbucket.appspot.com/#docs/conventions
Vadim Sh. 2015/09/16 21:57:38 https://
nodir 2015/09/17 17:44:17 Updated all doc links to point to the newer MD doc
391 hostname = common.get_preferred_domain(project, default_to_appid=False)
392 if not hostname:
393 logging.error(
394 'Preferred domain name for this app is not set. '
395 'See PREFERRED_DOMAIN_NAMES in settings.py: %r', hostname)
396 raise ndb.Return([])
397
398 return BUILDSET_TAG_FORMAT.format(
399 hostname=hostname,
400 issue=issue_id,
401 patch=patchset_id,
402 )
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698