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

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

Issue 1939343002: swarming: change meaning of inputs_ref (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-py@master
Patch Set: sort dict keys Created 4 years, 7 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 by the Apache v2.0 license that can be 3 # Use of this source code is governed by the Apache v2.0 license that can be
4 # found in the LICENSE file. 4 # 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 205 matching lines...) Expand 10 before | Expand all | Expand 10 after
216 if not cipd.is_valid_version(value): 216 if not cipd.is_valid_version(value):
217 raise datastore_errors.BadValueError( 217 raise datastore_errors.BadValueError(
218 '%s must be a valid package version "%s"' % (prop._name, value)) 218 '%s must be a valid package version "%s"' % (prop._name, value))
219 219
220 220
221 ### Models. 221 ### Models.
222 222
223 223
224 class FilesRef(ndb.Model): 224 class FilesRef(ndb.Model):
225 """Defines a data tree reference, normally a reference to a .isolated file.""" 225 """Defines a data tree reference, normally a reference to a .isolated file."""
226 # The hash of an isolated archive. 226
227 # Specifies FilesRef mode.
M-A Ruel 2016/05/11 14:41:41 This is not needed?
nodir 2016/05/11 16:04:29 Done.
228 # if True, this instance is a reference to a tree, as class docstring says.
229 # if False, this instance is isolate input/output settings in TaskProperties.
230 # TODO(maruel): refactor this class, get rid of this.
231 is_ref = True
232
233 # if is_ref, the hash of an isolated archive.
234 # otherwise, the hash of the input isolated archive.
227 isolated = ndb.StringProperty(validator=_validate_isolated, indexed=False) 235 isolated = ndb.StringProperty(validator=_validate_isolated, indexed=False)
228 # The hostname of the isolated server to use. 236 # The hostname of the isolated server to use.
229 isolatedserver = ndb.StringProperty( 237 isolatedserver = ndb.StringProperty(
230 validator=_validate_hostname, indexed=False) 238 validator=_validate_hostname, indexed=False)
231 # Namespace on the isolate server. 239 # Namespace on the isolate server.
232 namespace = ndb.StringProperty(validator=_validate_namespace, indexed=False) 240 namespace = ndb.StringProperty(validator=_validate_namespace, indexed=False)
233 241
234 def _pre_put_hook(self): 242 def _pre_put_hook(self):
235 super(FilesRef, self)._pre_put_hook() 243 super(FilesRef, self)._pre_put_hook()
236 if not self.isolated or not self.isolatedserver or not self.namespace: 244 if self.is_ref:
M-A Ruel 2016/05/11 14:41:41 Remove this part, self.isolated can be None.
nodir 2016/05/11 16:04:29 Done. Now is_ref is not used, so I've removed it.
237 raise datastore_errors.BadValueError( 245 if not self.isolated or not self.isolatedserver or not self.namespace:
238 'isolated requires server and namespace') 246 raise datastore_errors.BadValueError(
247 'isolated requires server and namespace')
248 else:
249 if not self.isolatedserver or not self.namespace:
250 raise datastore_errors.BadValueError(
251 'isolate server and namespace are required')
239 252
240 253
241 class CipdPackage(ndb.Model): 254 class CipdPackage(ndb.Model):
242 """A CIPD package to install in $CIPD_PATH and $PATH before task execution. 255 """A CIPD package to install in $CIPD_PATH and $PATH before task execution.
243 256
244 A part of TaskProperties. 257 A part of TaskProperties.
245 """ 258 """
246 package_name = ndb.StringProperty( 259 package_name = ndb.StringProperty(
247 indexed=False, validator=_validate_package_name) 260 indexed=False, validator=_validate_package_name)
248 version = ndb.StringProperty( 261 version = ndb.StringProperty(
(...skipping 10 matching lines...) Expand all
259 class TaskProperties(ndb.Model): 272 class TaskProperties(ndb.Model):
260 """Defines all the properties of a task to be run on the Swarming 273 """Defines all the properties of a task to be run on the Swarming
261 infrastructure. 274 infrastructure.
262 275
263 This entity is not saved in the DB as a standalone entity, instead it is 276 This entity is not saved in the DB as a standalone entity, instead it is
264 embedded in a TaskRequest. 277 embedded in a TaskRequest.
265 278
266 This model is immutable. 279 This model is immutable.
267 280
268 New-style TaskProperties supports invocation of run_isolated. When this 281 New-style TaskProperties supports invocation of run_isolated. When this
269 behavior is desired, the member .inputs_ref must be suppled. .extra_args can 282 behavior is desired, the member .inputs_ref.isolated must be supplied.
M-A Ruel 2016/05/11 14:41:41 Strictly speaking, it could still be missing when
nodir 2016/05/11 16:04:29 Done.
270 be supplied to pass extraneous arguments. 283 .extra_args can be supplied to pass extraneous arguments.
271 """ 284 """
285
286 # TODO(maruel): convert inputs_ref and _TaskResultCommon.outputs_ref as:
287 # - input = String which is the isolated input, if any
288 # - isolated_server = <server, metadata e.g. namespace> which is a
289 # simplified version of FilesRef
290 # - _TaskResultCommon.output = String which is isolated output, if any.
291
272 # Hashing algorithm used to hash TaskProperties to create its key. 292 # Hashing algorithm used to hash TaskProperties to create its key.
273 HASHING_ALGO = hashlib.sha1 293 HASHING_ALGO = hashlib.sha1
274 294
275 # Commands to run. It is a list of 1 item, the command to run. 295 # Commands to run. It is a list of 1 item, the command to run.
276 # TODO(maruel): Remove after 2016-06-01. 296 # TODO(maruel): Remove after 2016-06-01.
277 commands = datastore_utils.DeterministicJsonProperty( 297 commands = datastore_utils.DeterministicJsonProperty(
278 json_type=list, indexed=False) 298 json_type=list, indexed=False)
279 # Command to run. This is only relevant when self._inputs_ref is None. This is 299 # Command to run. This is only relevant when self.inputs_ref.isolated is None.
280 # what is called 'raw commands', in the sense that no inputs files are 300 # This is what is called 'raw commands', in the sense that no inputs files are
281 # declared. 301 # declared.
282 command = ndb.StringProperty(repeated=True, indexed=False) 302 command = ndb.StringProperty(repeated=True, indexed=False)
283 303
284 # File inputs of the task. Only inputs_ref or command&data can be specified. 304 # Isolate server, namespace and input isolate hash.
305 #
306 # Despite its name, contains isolate server URL and namespace for isolated
307 # output too. See TODO at the top of this class.
308 # May be non-None even if task input is not isolated.
309 #
310 # Only inputs_ref.isolated or command&data can be specified.
M-A Ruel 2016/05/11 14:41:41 data doesn't exist anymore, it's a left over so re
nodir 2016/05/11 16:04:29 Done.
285 inputs_ref = ndb.LocalStructuredProperty(FilesRef) 311 inputs_ref = ndb.LocalStructuredProperty(FilesRef)
286 312
287 # A list of CIPD packages to install $CIPD_PATH and $PATH before task 313 # A list of CIPD packages to install $CIPD_PATH and $PATH before task
288 # execution. 314 # execution.
289 packages = ndb.LocalStructuredProperty(CipdPackage, repeated=True) 315 packages = ndb.LocalStructuredProperty(CipdPackage, repeated=True)
290 316
291 # Filter to use to determine the required properties on the bot to run on. For 317 # Filter to use to determine the required properties on the bot to run on. For
292 # example, Windows or hostname. Encoded as json. Optional but highly 318 # example, Windows or hostname. Encoded as json. Optional but highly
293 # recommended. 319 # recommended.
294 dimensions = datastore_utils.DeterministicJsonProperty( 320 dimensions = datastore_utils.DeterministicJsonProperty(
295 validator=_validate_dimensions, json_type=dict, indexed=False) 321 validator=_validate_dimensions, json_type=dict, indexed=False)
296 322
297 # Environment variables. Encoded as json. Optional. 323 # Environment variables. Encoded as json. Optional.
298 env = datastore_utils.DeterministicJsonProperty( 324 env = datastore_utils.DeterministicJsonProperty(
299 validator=_validate_dict_of_strings, json_type=dict, indexed=False) 325 validator=_validate_dict_of_strings, json_type=dict, indexed=False)
300 326
301 # Maximum duration the bot can take to run this task. It's named hard_timeout 327 # Maximum duration the bot can take to run this task. It's named hard_timeout
302 # in the bot. 328 # in the bot.
303 execution_timeout_secs = ndb.IntegerProperty( 329 execution_timeout_secs = ndb.IntegerProperty(
304 validator=_validate_timeout, required=True, indexed=False) 330 validator=_validate_timeout, required=True, indexed=False)
305 331
306 # Extra arguments to supply to the command `python run_isolated ...`. Can only 332 # Extra arguments to supply to the command `python run_isolated ...`. Can only
307 # be set if inputs_ref is set. 333 # be set if inputs_ref.isolated is set.
308 extra_args = ndb.StringProperty(repeated=True, indexed=False) 334 extra_args = ndb.StringProperty(repeated=True, indexed=False)
309 335
310 # Grace period is the time between signaling the task it timed out and killing 336 # Grace period is the time between signaling the task it timed out and killing
311 # the process. During this time the process should clean up itself as quickly 337 # the process. During this time the process should clean up itself as quickly
312 # as possible, potentially uploading partial results back. 338 # as possible, potentially uploading partial results back.
313 grace_period_secs = ndb.IntegerProperty( 339 grace_period_secs = ndb.IntegerProperty(
314 validator=_validate_grace, default=30, indexed=False) 340 validator=_validate_grace, default=30, indexed=False)
315 341
316 # Bot controlled timeout for new bytes from the subprocess. If a subprocess 342 # Bot controlled timeout for new bytes from the subprocess. If a subprocess
317 # doesn't output new data to stdout for .io_timeout_secs, consider the command 343 # doesn't output new data to stdout for .io_timeout_secs, consider the command
318 # timed out. Optional. 344 # timed out. Optional.
319 io_timeout_secs = ndb.IntegerProperty( 345 io_timeout_secs = ndb.IntegerProperty(
320 validator=_validate_timeout, indexed=False) 346 validator=_validate_timeout, indexed=False)
321 347
322 # If True, the task can safely be served results from a previously succeeded 348 # If True, the task can safely be served results from a previously succeeded
323 # task. 349 # task.
324 idempotent = ndb.BooleanProperty(default=False, indexed=False) 350 idempotent = ndb.BooleanProperty(default=False, indexed=False)
325 351
326 @property 352 @property
327 def is_terminate(self): 353 def is_terminate(self):
328 """If True, it is a terminate request.""" 354 """If True, it is a terminate request."""
329 return ( 355 return (
330 not self.commands and 356 not self.commands and
331 not self.command and 357 not self.command and
332 self.dimensions.keys() == [u'id'] and 358 self.dimensions.keys() == [u'id'] and
333 not self.inputs_ref and 359 not (self.inputs_ref and self.inputs_ref.isolated) and
334 not self.env and 360 not self.env and
335 not self.execution_timeout_secs and 361 not self.execution_timeout_secs and
336 not self.extra_args and 362 not self.extra_args and
337 not self.grace_period_secs and 363 not self.grace_period_secs and
338 not self.io_timeout_secs and 364 not self.io_timeout_secs and
339 not self.idempotent) 365 not self.idempotent)
340 366
341 @property 367 @property
342 def properties_hash(self): 368 def properties_hash(self):
343 """Calculates the hash for this entity IFF the task is idempotent. 369 """Calculates the hash for this entity IFF the task is idempotent.
(...skipping 12 matching lines...) Expand all
356 out = super(TaskProperties, self).to_dict(exclude=['commands']) 382 out = super(TaskProperties, self).to_dict(exclude=['commands'])
357 out['command'] = self.commands[0] if self.commands else self.command 383 out['command'] = self.commands[0] if self.commands else self.command
358 return out 384 return out
359 385
360 def _pre_put_hook(self): 386 def _pre_put_hook(self):
361 super(TaskProperties, self)._pre_put_hook() 387 super(TaskProperties, self)._pre_put_hook()
362 if self.commands: 388 if self.commands:
363 raise datastore_errors.BadValueError( 389 raise datastore_errors.BadValueError(
364 'commands is not supported anymore') 390 'commands is not supported anymore')
365 if not self.is_terminate: 391 if not self.is_terminate:
366 if bool(self.command) == bool(self.inputs_ref): 392 isolated_input = self.inputs_ref and self.inputs_ref.isolated
367 raise datastore_errors.BadValueError('use one of command or inputs_ref') 393 if bool(self.command) == bool(isolated_input):
368 if self.extra_args and not self.inputs_ref: 394 raise datastore_errors.BadValueError(
369 raise datastore_errors.BadValueError('extra_args require inputs_ref') 395 'use one of command or inputs_ref.isolated')
396 if self.extra_args and not isolated_input:
397 raise datastore_errors.BadValueError(
398 'extra_args require inputs_ref.isolated')
370 if self.inputs_ref: 399 if self.inputs_ref:
400 self.inputs_ref.is_ref = False
371 self.inputs_ref._pre_put_hook() 401 self.inputs_ref._pre_put_hook()
372 402
373 package_names = set() 403 package_names = set()
374 for p in self.packages: 404 for p in self.packages:
375 p._pre_put_hook() 405 p._pre_put_hook()
376 if p.package_name in package_names: 406 if p.package_name in package_names:
377 raise datastore_errors.BadValueError( 407 raise datastore_errors.BadValueError(
378 'package %s is specified more than once' % p.package_name) 408 'package %s is specified more than once' % p.package_name)
379 package_names.add(p.package_name) 409 package_names.add(p.package_name)
380 self.packages.sort(key=lambda p: p.package_name) 410 self.packages.sort(key=lambda p: p.package_name)
381 411
382 if self.idempotent: 412 if self.idempotent:
383 pinned = lambda p: cipd.is_pinned_version(p.version) 413 pinned = lambda p: cipd.is_pinned_version(p.version)
384 if self.packages and any(not pinned(p) for p in self.packages): 414 if self.packages and any(not pinned(p) for p in self.packages):
385 raise datastore_errors.BadValueError( 415 raise datastore_errors.BadValueError(
386 'an idempotent task cannot have unpinned packages; ' 416 'an idempotent task cannot have unpinned packages; '
387 'use instance IDs or tags as package versions') 417 'use instance IDs or tags as package versions')
388 418
389 419
390
391 class TaskRequest(ndb.Model): 420 class TaskRequest(ndb.Model):
392 """Contains a user request. 421 """Contains a user request.
393 422
394 Key id is a decreasing integer based on time since utils.EPOCH plus some 423 Key id is a decreasing integer based on time since utils.EPOCH plus some
395 randomness on lower order bits. See _new_request_key() for the complete gory 424 randomness on lower order bits. See _new_request_key() for the complete gory
396 details. 425 details.
397 426
398 There is also "old style keys" which inherit from a fake root entity 427 There is also "old style keys" which inherit from a fake root entity
399 TaskRequestShard. 428 TaskRequestShard.
400 TODO(maruel): Remove support 2015-10-01 once entities are deleted. 429 TODO(maruel): Remove support 2015-10-01 once entities are deleted.
(...skipping 301 matching lines...) Expand 10 before | Expand all | Expand 10 after
702 _put_request(request) 731 _put_request(request)
703 return request 732 return request
704 733
705 734
706 def validate_priority(priority): 735 def validate_priority(priority):
707 """Throws ValueError if priority is not a valid value.""" 736 """Throws ValueError if priority is not a valid value."""
708 if 0 > priority or MAXIMUM_PRIORITY < priority: 737 if 0 > priority or MAXIMUM_PRIORITY < priority:
709 raise datastore_errors.BadValueError( 738 raise datastore_errors.BadValueError(
710 'priority (%d) must be between 0 and %d (inclusive)' % 739 'priority (%d) must be between 0 and %d (inclusive)' %
711 (priority, MAXIMUM_PRIORITY)) 740 (priority, MAXIMUM_PRIORITY))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698