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

Side by Side Diff: appengine/swarming/server/task_request.py

Issue 2069903003: swarming: custom cipd package paths (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-py@cipd-win
Patch Set: fix _validate_cipd_path Created 4 years, 6 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 # coding: utf-8 1 # coding: utf-8
2 # Copyright 2014 The LUCI Authors. All rights reserved. 2 # Copyright 2014 The LUCI Authors. All rights reserved.
3 # Use of this source code is governed under the Apache License, Version 2.0 3 # Use of this source code is governed under the Apache License, Version 2.0
4 # that can be found in the LICENSE file. 4 # that can be found in the LICENSE file.
5 5
6 """Tasks definition. 6 """Tasks definition.
7 7
8 Each user request creates a new TaskRequest. The TaskRequest instance saves the 8 Each user request creates a new TaskRequest. The TaskRequest instance saves the
9 metadata of the request, e.g. who requested it, when why, etc. It links to the 9 metadata of the request, e.g. who requested it, when why, etc. It links to the
10 actual data of the request in a TaskProperties. The TaskProperties represents 10 actual data of the request in a TaskProperties. The TaskProperties represents
(...skipping 28 matching lines...) Expand all
39 +---------------------+ 39 +---------------------+
40 ^ 40 ^
41 | 41 |
42 <See task_to_run.py and task_result.py> 42 <See task_to_run.py and task_result.py>
43 43
44 TaskProperties is embedded in TaskRequest. TaskProperties is still declared as a 44 TaskProperties is embedded in TaskRequest. TaskProperties is still declared as a
45 separate entity to clearly declare the boundary for task request deduplication. 45 separate entity to clearly declare the boundary for task request deduplication.
46 """ 46 """
47 47
48 48
49 import collections
49 import datetime 50 import datetime
50 import hashlib 51 import hashlib
51 import random 52 import random
52 import re 53 import re
53 import urlparse 54 import urlparse
54 55
55 from google.appengine.api import datastore_errors 56 from google.appengine.api import datastore_errors
56 from google.appengine.ext import ndb 57 from google.appengine.ext import ndb
57 58
58 from components import auth 59 from components import auth
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after
213 prop._name, value)) 214 prop._name, value))
214 215
215 216
216 def _validate_package_version(prop, value): 217 def _validate_package_version(prop, value):
217 """Validates a CIPD package version.""" 218 """Validates a CIPD package version."""
218 if not cipd.is_valid_version(value): 219 if not cipd.is_valid_version(value):
219 raise datastore_errors.BadValueError( 220 raise datastore_errors.BadValueError(
220 '%s must be a valid package version "%s"' % (prop._name, value)) 221 '%s must be a valid package version "%s"' % (prop._name, value))
221 222
222 223
224 def _validate_package_path(_prop, path):
225 """Validates a CIPD installation path."""
226 if not path:
227 raise datastore_errors.BadValueError(
228 'CIPD package path is required. Use "." to install to run dir.')
229 if '\\' in path:
230 raise datastore_errors.BadValueError(
231 'CIPD package path cannot contain \\. On Windows forward-slashes '
232 'will be replaced with back-slashes.')
233 if '..' in path.split('/'):
234 raise datastore_errors.BadValueError(
235 'CIPD package path cannot contain "..".')
236 if path.startswith('/'):
237 raise datastore_errors.BadValueError(
238 'CIPD package path cannot start with "/".')
239
240
223 ### Models. 241 ### Models.
224 242
225 243
226 class FilesRef(ndb.Model): 244 class FilesRef(ndb.Model):
227 """Defines a data tree reference, normally a reference to a .isolated file.""" 245 """Defines a data tree reference, normally a reference to a .isolated file."""
228 246
229 # TODO(maruel): make this class have one responsibility. Currently it is used 247 # TODO(maruel): make this class have one responsibility. Currently it is used
230 # in two modes: 248 # in two modes:
231 # - a reference to a tree, as class docstring says. 249 # - a reference to a tree, as class docstring says.
232 # - input/output settings in TaskProperties. 250 # - input/output settings in TaskProperties.
(...skipping 19 matching lines...) Expand all
252 A part of TaskProperties. 270 A part of TaskProperties.
253 """ 271 """
254 # Package name template. May use cipd.ALL_PARAMS. 272 # Package name template. May use cipd.ALL_PARAMS.
255 # Most users will specify ${platform} parameter. 273 # Most users will specify ${platform} parameter.
256 package_name = ndb.StringProperty( 274 package_name = ndb.StringProperty(
257 indexed=False, validator=_validate_package_name_template) 275 indexed=False, validator=_validate_package_name_template)
258 # Package version that is valid for all packages matched by package_name. 276 # Package version that is valid for all packages matched by package_name.
259 # Most users will specify tags. 277 # Most users will specify tags.
260 version = ndb.StringProperty( 278 version = ndb.StringProperty(
261 indexed=False, validator=_validate_package_version) 279 indexed=False, validator=_validate_package_version)
280 # Path to dir, relative to the run dir, where to install the package.
281 # If empty, the package will be installed in run dir.
282 path = ndb.StringProperty(indexed=False, validator=_validate_package_path)
262 283
263 def __str__(self): 284 def __str__(self):
264 return '%s:%s' % (self.package_name, self.version) 285 return '%s:%s' % (self.package_name, self.version)
265 286
266 def _pre_put_hook(self): 287 def _pre_put_hook(self):
267 super(CipdPackage, self)._pre_put_hook() 288 super(CipdPackage, self)._pre_put_hook()
268 if not self.package_name: 289 if not self.package_name:
269 raise datastore_errors.BadValueError('CIPD package name is required') 290 raise datastore_errors.BadValueError('CIPD package name is required')
270 if not self.version: 291 if not self.version:
271 raise datastore_errors.BadValueError('CIPD package version is required') 292 raise datastore_errors.BadValueError('CIPD package version is required')
272 293
273 294
274 class CipdInput(ndb.Model): 295 class CipdInput(ndb.Model):
275 """Specifies which CIPD client and packages to install, from which server. 296 """Specifies which CIPD client and packages to install, from which server.
276 297
277 A part of TaskProperties. 298 A part of TaskProperties.
278 """ 299 """
279 # URL of the CIPD server. Must start with "https://" or "http://". 300 # URL of the CIPD server. Must start with "https://" or "http://".
280 server = ndb.StringProperty(indexed=False, validator=_validate_url) 301 server = ndb.StringProperty(indexed=False, validator=_validate_url)
281 302
282 # CIPD package of CIPD client to use. 303 # CIPD package of CIPD client to use.
283 # client_package.version is required. 304 # client_package.version is required.
305 # client_package.path must be None.
284 client_package = ndb.LocalStructuredProperty(CipdPackage) 306 client_package = ndb.LocalStructuredProperty(CipdPackage)
285 307
286 # List of packages to install in $CIPD_PATH prior task execution. 308 # List of packages to install in $CIPD_PATH prior task execution.
287 packages = ndb.LocalStructuredProperty(CipdPackage, repeated=True) 309 packages = ndb.LocalStructuredProperty(CipdPackage, repeated=True)
288 310
289 def _pre_put_hook(self): 311 def _pre_put_hook(self):
290 if not self.server: 312 if not self.server:
291 raise datastore_errors.BadValueError('cipd server is required') 313 raise datastore_errors.BadValueError('cipd server is required')
292 if not self.client_package: 314 if not self.client_package:
293 raise datastore_errors.BadValueError('client_package is required') 315 raise datastore_errors.BadValueError('client_package is required')
316 if self.client_package.path:
317 raise datastore_errors.BadValueError('client_package.path must be unset')
294 self.client_package._pre_put_hook() 318 self.client_package._pre_put_hook()
295 319
296 if not self.packages: 320 if not self.packages:
297 raise datastore_errors.BadValueError( 321 raise datastore_errors.BadValueError(
298 'cipd_input cannot have an empty package list') 322 'cipd_input cannot have an empty package list')
299 323
300 package_names = set() 324 package_names = set()
301 for p in self.packages: 325 for p in self.packages:
302 p._pre_put_hook() 326 p._pre_put_hook()
303 if p.package_name in package_names: 327 if p.package_name in package_names:
304 raise datastore_errors.BadValueError( 328 raise datastore_errors.BadValueError(
305 'package %s is specified more than once' % p.package_name) 329 'package %s is specified more than once' % p.package_name)
306 package_names.add(p.package_name) 330 package_names.add(p.package_name)
307 self.packages.sort(key=lambda p: p.package_name) 331 self.packages.sort(key=lambda p: p.package_name)
308 332
333 def packages_grouped_by_path(self):
334 """Returns sorted [(path), [package]) list. Used by user_task.html."""
335 packages = collections.defaultdict(list)
336 for p in self.packages:
337 packages[p.path].append(p)
338 for pkgs in packages.itervalues():
339 pkgs.sort()
340 return sorted(packages.iteritems())
341
309 342
310 class TaskProperties(ndb.Model): 343 class TaskProperties(ndb.Model):
311 """Defines all the properties of a task to be run on the Swarming 344 """Defines all the properties of a task to be run on the Swarming
312 infrastructure. 345 infrastructure.
313 346
314 This entity is not saved in the DB as a standalone entity, instead it is 347 This entity is not saved in the DB as a standalone entity, instead it is
315 embedded in a TaskRequest. 348 embedded in a TaskRequest.
316 349
317 This model is immutable. 350 This model is immutable.
318 351
(...skipping 450 matching lines...) Expand 10 before | Expand all | Expand 10 after
769 _put_request(request) 802 _put_request(request)
770 return request 803 return request
771 804
772 805
773 def validate_priority(priority): 806 def validate_priority(priority):
774 """Throws ValueError if priority is not a valid value.""" 807 """Throws ValueError if priority is not a valid value."""
775 if 0 > priority or MAXIMUM_PRIORITY < priority: 808 if 0 > priority or MAXIMUM_PRIORITY < priority:
776 raise datastore_errors.BadValueError( 809 raise datastore_errors.BadValueError(
777 'priority (%d) must be between 0 and %d (inclusive)' % 810 'priority (%d) must be between 0 and %d (inclusive)' %
778 (priority, MAXIMUM_PRIORITY)) 811 (priority, MAXIMUM_PRIORITY))
OLDNEW
« no previous file with comments | « appengine/swarming/handlers_endpoints_test.py ('k') | appengine/swarming/server/task_request_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698