mirrored from https://chromium.googlesource.com/infra/luci/luci-py
-
Notifications
You must be signed in to change notification settings - Fork 36
/
swarming_rpcs.py
910 lines (741 loc) · 34.9 KB
/
swarming_rpcs.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
# Copyright 2015 The LUCI Authors. All rights reserved.
# Use of this source code is governed under the Apache License, Version 2.0
# that can be found in the LICENSE file.
"""This module defines ProtoRPC types for the Swarming Server handlers."""
from protorpc import message_types
from protorpc import messages
### Enums
class TaskStateQuery(messages.Enum):
"""Use one of the values in this enum to query for tasks in one of the
specified state.
Use 'ALL' to not use any filtering based on task state.
As an example, this enum enables querying for all tasks with state COMPLETED
but non-zero exit code via COMPLETED_FAILURE.
Do not confuse TaskStateQuery and TaskState. TaskStateQuery is to query tasks
via the API. TaskState is the current task state.
"""
# Query for all tasks currently TaskState.PENDING.
PENDING = 0
# Query for all tasks currently TaskState.RUNNING. This includes tasks
# currently in the overhead phase; mapping input files or archiving outputs
# back to the server.
RUNNING = 1
# Query for all tasks currently TaskState.PENDING or TaskState.RUNNING. This
# is the query for the 'active' tasks.
PENDING_RUNNING = 2
# Query for all tasks that completed normally as TaskState.COMPLETED,
# independent of the process exit code.
COMPLETED = 3
# Query for all tasks that completed normally as TaskState.COMPLETED and that
# had exit code 0.
COMPLETED_SUCCESS = 4
# Query for all tasks that completed normally as TaskState.COMPLETED and that
# had exit code not 0.
COMPLETED_FAILURE = 5
# Query for all tasks that are TaskState.EXPIRED.
EXPIRED = 6
# Query for all tasks that are TaskState.TIMED_OUT.
TIMED_OUT = 7
# Query for all tasks that are TaskState.BOT_DIED.
BOT_DIED = 8
# Query for all tasks that are TaskState.CANCELED.
CANCELED = 9
# Query for all tasks, independent of the task state.
#
# In hindsight, this constant should have been the value 0. Sorry, the
# original author was young and foolish.
ALL = 10
# Query for all tasks that are TaskState.COMPLETED but that actually didn't
# run due to TaskProperties.idempotent being True *and* that a previous task
# with the exact same TaskProperties had successfully run before, aka
# COMPLETED_SUCCESS.
DEDUPED = 11
# Query for all tasks that are TaskState.KILLED.
KILLED = 12
# Query for all tasks that are TaskState.NO_RESOURCE.
NO_RESOURCE = 13
# Query for all tasks that are TaskState.CLIENT_ERROR.
CLIENT_ERROR = 14
class TaskState(messages.Enum):
"""Represents the current task state.
Some states are still mutable: PENDING and RUNNING. The others are final and
will not change afterward.
A task is guaranteed to be in exactly one state at any point of time.
Do not confuse TaskStateQuery and TaskState. TaskStateQuery is to query tasks
via the API. TaskState is the current task state.
As you read the following constants, astute readers may wonder why these
constants look like a bitmask. This is because of historical reasons and this
is effectively an enum, not a bitmask.
"""
# Invalid state, do not use.
INVALID = 0x00
# The task is currently running. This is in fact 3 phases: the initial
# overhead to fetch input files, the actual task running, and the tear down
# overhead to archive output files to the server.
RUNNING = 0x10
# The task is currently pending. This means that no bot reaped the task. It
# will stay in this state until either a task reaps it or the expiration
# elapsed. The task pending expiration is specified as
# TaskSlice.expiration_secs, one per task slice.
PENDING = 0x20
# The task is not pending anymore, and never ran due to lack of capacity. This
# means that other higher priority tasks ran instead and that not enough bots
# were available to run this task for TaskSlice.expiration_secs seconds.
EXPIRED = 0x30
# The task ran for longer than the allowed time in
# TaskProperties.execution_timeout_secs or TaskProperties.io_timeout_secs.
# This means the bot forcefully killed the task process as described in the
# graceful termination dance in the documentation.
TIMED_OUT = 0x40
# The task ran but the bot had an internal failure, unrelated to the task
# itself. It can be due to the server being unavailable to get task update,
# the host on which the bot is running crashing or rebooting, etc.
BOT_DIED = 0x50
# The task never ran, and was manually cancelled via the 'cancel' API before
# it was reaped.
CANCELED = 0x60
# The task ran and completed normally. The task process exit code may be 0 or
# another value.
COMPLETED = 0x70
# The task ran but was manually killed via the 'cancel' API. This means the
# bot forcefully killed the task process as described in the graceful
# termination dance in the documentation.
KILLED = 0x80
# The task was never set to PENDING and was immediately refused, as the server
# determined that there is no bot capacity to run this task. This happens
# because no bot exposes a superset of the requested task dimensions.
#
# Set TaskSlice.wait_for_capacity to True to force the server to keep the task
# slice pending even in this case. Generally speaking, the task will
# eventually switch to EXPIRED, as there's no bot to run it. That said, there
# are situations where it is known that in some not-too-distant future a wild
# bot will appear that will be able to run this task.
NO_RESOURCE = 0x100
# The task run into an issue that was caused by the client. It can be due to
# a bad CIPD or CAS package. Retrying the task with the same parameters will
# not change the result.
CLIENT_ERROR = 0x200
class TaskSort(messages.Enum):
"""Flag to sort returned tasks. The natural sort is CREATED_TS."""
CREATED_TS = 0
COMPLETED_TS = 2
ABANDONED_TS = 3
STARTED_TS = 4
class PoolTaskTemplateField(messages.Enum):
"""Flag to control the application of the pool's TaskTemplate in a new
TaskRequest.
The non-endpoints counterpart is in task_request.
"""
AUTO = 0
CANARY_PREFER = 1
CANARY_NEVER = 2
SKIP = 3
### Pretend Associative Array
class StringPair(messages.Message):
"""Represents a mapping of string to string."""
key = messages.StringField(1)
value = messages.StringField(2)
class StringListPair(messages.Message):
"""Represents a mapping of string to list of strings."""
key = messages.StringField(1)
value = messages.StringField(2, repeated=True)
class ThreeStateBool(messages.Enum):
FALSE = 1
TRUE = 2
NONE = 3
def to_bool(three_state):
if three_state in (None, True, False):
return three_state
if three_state == ThreeStateBool.FALSE:
return False
if three_state == ThreeStateBool.TRUE:
return True
### Server related.
class ServerDetails(messages.Message):
"""Reports details about the server."""
server_version = messages.StringField(1)
bot_version = messages.StringField(2)
machine_provider_template = messages.StringField(3)
display_server_url_template = messages.StringField(4)
luci_config = messages.StringField(5)
cas_viewer_server = messages.StringField(8)
class BootstrapToken(messages.Message):
"""Returns a token to bootstrap a new bot."""
bootstrap_token = messages.StringField(1)
class ClientPermissions(messages.Message):
"""Reports the client's permissions."""
delete_bot = messages.BooleanField(1)
delete_bots = messages.BooleanField(10)
terminate_bot = messages.BooleanField(2)
get_configs = messages.BooleanField(3)
put_configs = messages.BooleanField(4)
# Cancel one single task
cancel_task = messages.BooleanField(5)
get_bootstrap_token = messages.BooleanField(6)
# Cancel multiple tasks at once, usually in emergencies.
cancel_tasks = messages.BooleanField(7)
list_bots = messages.StringField(8, repeated=True)
list_tasks = messages.StringField(9, repeated=True)
class FileContent(messages.Message):
"""Content of a file."""
content = messages.StringField(1)
version = messages.StringField(2)
who = messages.StringField(3)
when = message_types.DateTimeField(4)
### Task-Related Requests
class Digest(messages.Message):
# This is a [Digest][build.bazel.remote.execution.v2.Digest] of a blob on
# RBE-CAS. See the explanations at the original definition.
# pylint: disable=line-too-long
# https://github.com/bazelbuild/remote-apis/blob/77cfb44a88577a7ade5dd2400425f6d50469ec6d/build/bazel/remote/execution/v2/remote_execution.proto#L753-L791
hash = messages.StringField(1)
size_bytes = messages.IntegerField(2)
class CASReference(messages.Message):
# Full name of RBE-CAS instance. `projects/{project_id}/instances/{instance}`.
# e.g. projects/chromium-swarm/instances/default_instance
cas_instance = messages.StringField(1)
# CAS Digest consists of hash and size bytes.
digest = messages.MessageField(Digest, 2)
class CipdPackage(messages.Message):
"""A CIPD package to install in the run dir before task execution."""
# A template of a full CIPD package name, e.g.
# "infra/tools/authutil/${platform}"
# See also cipd.ALL_PARAMS.
package_name = messages.StringField(1)
# Valid package version for all packages matched by package name.
version = messages.StringField(2)
# Path to dir, relative to the root dir, where to install the package.
# If empty, the package will be installed a the root of the mapped directory.
# If file names in the package and in the isolate clash, it will cause a
# failure.
path = messages.StringField(3)
class CipdInput(messages.Message):
"""Defines CIPD packages to install in task run directory."""
# URL of the CIPD server. Must start with "https://" or "http://".
# This field or its subfields are optional if default cipd client is defined
# in the server config.
server = messages.StringField(1)
# CIPD package of CIPD client to use.
# client_package.version is required.
# This field is optional is default value is defined in the server config.
# client_package.path must be empty.
client_package = messages.MessageField(CipdPackage, 2)
# List of CIPD packages to install.
packages = messages.MessageField(CipdPackage, 3, repeated=True)
class CipdPins(messages.Message):
"""Defines pinned CIPD packages that were installed during the task."""
# The pinned package + version of the CIPD client that was actually used.
client_package = messages.MessageField(CipdPackage, 1)
# List of CIPD packages that were installed in the task with fully resolved
# package names and versions.
packages = messages.MessageField(CipdPackage, 2, repeated=True)
class CacheEntry(messages.Message):
"""Describes a named cache that should be present on the bot.
A CacheEntry in a task specified that the task prefers the cache to be present
on the bot. A symlink to the cache directory is created at <run_dir>/|path|.
If cache is not present on the machine, the directory is empty.
If the tasks makes any changes to the contents of the cache directory, they
are persisted on the machine. If another task runs on the same machine and
requests the same named cache, even if mapped to a different path, it will see
the changes.
"""
# Unique name of the cache. Required. Length is limited to 4096.
name = messages.StringField(1)
# Relative path to the directory that will be linked to the named cache.
# Required.
# A path cannot be shared among multiple caches or CIPD installations.
# A task will fail if a file/dir with the same name already exists.
path = messages.StringField(2)
class ContainmentType(messages.Enum):
"""See proto/api/swarming.proto for description."""
NOT_SPECIFIED = 0
NONE = 1
AUTO = 2
JOB_OBJECT = 3
class Containment(messages.Message):
"""See proto/api/swarming.proto for description."""
containment_type = messages.EnumField(ContainmentType, 2)
# Deprecated: lower_priority, limit_processes, limit_total_committed_memory
class TaskProperties(messages.Message):
"""Important metadata about a particular task."""
# Specifies named caches to map into the working directory. These caches
# outlives the task, which can then be reused by tasks later used on this bot
# that request the same named cache.
caches = messages.MessageField(CacheEntry, 11, repeated=True)
# CIPD packages to install. These packages are meant to be software that is
# needed (a dependency) to the task being run. Unlike isolated files, the CIPD
# packages do not expire from the server.
cipd_input = messages.MessageField(CipdInput, 10)
# Command to run. This has priority over a command specified in the isolated
# files.
command = messages.StringField(1, repeated=True)
# Relative working directory to start the 'command' in, defaults to the root
# mapped directory or what is provided in the isolated file, if any.
relative_cwd = messages.StringField(15)
# Dimensions are what is used to determine which bot can run the task. The
# bot must have all the matching dimensions, even for repeated keys with
# multiple different values. It is a logical AND, all values must match.
#
# It should have been a StringListPair but this would be a breaking change.
dimensions = messages.MessageField(StringPair, 2, repeated=True)
# Environment variables to set when running the task.
env = messages.MessageField(StringPair, 3, repeated=True)
# Swarming-root relative paths to prepend to a given environment variable.
#
# These allow you to put certain subdirectories of the task into PATH,
# PYTHONPATH, or other PATH-like environment variables. The order of
# operations is:
# * Turn slashes into native-platform slashes.
# * Make the path absolute
# * Prepend it to the current value of the envvar using the os-native list
# separator (i.e. `;` on windows, `:` on POSIX).
#
# Each envvar can have multiple paths to prepend. They will be prepended in
# the order seen here.
#
# For example, if env_prefixes was:
# [("PATH", ["foo", "bar"]),
# ("CUSTOMPATH", ["custom"])]
#
# The task would see:
# PATH=/path/to/swarming/rundir/foo:/path/to/swarming/rundir/bar:$PATH
# CUSTOMPATH=/path/to/swarming/rundir/custom
#
# The path should always be specified here with forward-slashes, and it must
# not attempt to escape the swarming root (i.e. must not contain `..`).
#
# These are applied AFTER evaluating `env` entries.
env_prefixes = messages.MessageField(StringListPair, 14, repeated=True)
# Maximum number of seconds the task can run before its process is forcibly
# terminated and the task results in TIMED_OUT.
execution_timeout_secs = messages.IntegerField(4)
# Number of second to give the child process after a SIGTERM before sending a
# SIGKILL. See doc/Bot.md#timeout-handling
grace_period_secs = messages.IntegerField(6)
# True if the task does not access any service through the network and is
# believed to be 100% reproducible with the same outcome. In the case of a
# successful task, previous results will be reused if possible.
idempotent = messages.BooleanField(7)
# Digest of the input root uploaded to RBE-CAS.
# This MUST be digest of [build.bazel.remote.execution.v2.Directory].
cas_input_root = messages.MessageField(CASReference, 17)
# Maximum number of seconds the task may be silent (no output to stdout nor
# stderr) before it is considered hung and it forcibly terminated early and
# the task results in TIMED_OUT.
io_timeout_secs = messages.IntegerField(9)
# Paths in the working directory to archive back.
outputs = messages.StringField(12, repeated=True)
# Secret bytes to provide to the task. Cannot be retrieved back.
secret_bytes = messages.BytesField(13)
# Containment of the task processes.
containment = messages.MessageField(Containment, 16)
class TaskSlice(messages.Message):
"""Defines a possible task execution for a task request to be run on the
Swarming infrastructure.
This is one of the possible fallback on a task request.
"""
# The property of the task to try to run.
#
# If there is no bot that can serve this properties.dimensions when this task
# slice is enqueued, it is immediately denied. This can trigger if:
# - There is no bot with these dimensions currently known.
# - Bots that could run this task are either all dead or quarantined.
# Swarming considers a bot dead if it hasn't pinged in the last N minutes
# (currently 10 minutes).
properties = messages.MessageField(TaskProperties, 1)
# Maximum of seconds the task slice may stay PENDING.
#
# If this task request slice is not scheduled after waiting this long, the
# next one will be processed. If this slice is the last one, the task state
# will be set to EXPIRED.
expiration_secs = messages.IntegerField(2)
# When a task is scheduled and there are currently no bots available to run
# the task, the TaskSlice can either be PENDING, or be denied immediately.
# When denied, the next TaskSlice is enqueued, and if there's no following
# TaskSlice, the task state is set to NO_RESOURCE. This should normally be
# set to False to avoid unnecessary waiting.
wait_for_capacity = messages.BooleanField(3)
class ResultDBCfg(messages.Message):
"""Swarming:ResultDB integration configuration for a task.
See NewTaskRequest.resultdb for more details.
"""
# If True and this task is not deduplicated, create
# "task-{swarming_hostname}-{run_id}" invocation for this task,
# provide its update token to the task subprocess via LUCI_CONTEXT
# and finalize the invocation when the task is done.
# If the task is deduplicated, then TaskResult.invocation_name will be the
# invocation name of the original task.
# Swarming:ResultDB integration is off by default, but it may change in the
# future.
enable = messages.BooleanField(1)
class NewTaskRequest(messages.Message):
"""Description of a new task request as described by the client.
This message is used to create a new task.
"""
# DEPRECATED. Use task_slices[0].expiration_secs.
expiration_secs = messages.IntegerField(1)
# Task name for display purpose.
name = messages.StringField(2)
# Parent Swarming run ID of the process requesting this task. This is to tell
# the server about reentrancy: when a task creates children Swarming tasks, so
# that the tree of tasks can be presented in the UI; the parent task will list
# all the children tasks that were triggered.
parent_task_id = messages.StringField(3)
# Task priority, the lower the more important.
priority = messages.IntegerField(4)
# DEPRECATED. Use task_slices[0].properties.
properties = messages.MessageField(TaskProperties, 5)
# Slice of TaskSlice, along their scheduling parameters. Cannot be used at the
# same time as properties and expiration_secs.
#
# This defines all the various possible task execution for a task request to
# be run on the Swarming infrastructure. They are processed in order, and it
# is guaranteed that at most one of these will be processed.
task_slices = messages.MessageField(TaskSlice, 12, repeated=True)
# Tags are 'key:value' strings that describes what the task is about. This can
# later be leveraged to search for kinds of tasks per tag.
tags = messages.StringField(6, repeated=True)
# User on which behalf this task is run, if relevant. Not validated.
user = messages.StringField(7)
# Defines what OAuth2 credentials the task uses when calling other services.
#
# Possible values are:
# - 'none': do not use a task service account at all, this is the default.
# - 'bot': use bot's own account, works only if bots authenticate with
# OAuth2.
# - <some email>: use this specific service account if it is allowed in the
# pool (via 'allowed_service_account' pools.cfg setting) and configured
# in the token server's service_accounts.cfg.
#
# Note that the service account name is specified outside of task properties,
# and thus it is possible to have two tasks with different service accounts,
# but identical properties hash (so one can be deduped). If this is unsuitable
# use 'idempotent=False' or include a service account name in properties
# separately.
service_account = messages.StringField(8)
# Full topic name to post task state updates to, e.g.
# "projects/<id>/topics/<id>".
pubsub_topic = messages.StringField(9)
# Secret string to put into "auth_token" attribute of PubSub message.
pubsub_auth_token = messages.StringField(10)
# Will be but into "userdata" fields of PubSub message.
pubsub_userdata = messages.StringField(11)
# Only evaluate the task, as if we were going to schedule it, but don't
# actually schedule it. This will return the TaskRequest, but without
# a task_id.
evaluate_only = messages.BooleanField(13)
# Controls the application of the pool's TaskTemplate to the creation of this
# task. By default this will automatically select the pool's preference for
# template, but you can also instruct swarming to prefer/prevent the
# application of canary templates, as well as skipping the template
# altogether.
pool_task_template = messages.EnumField(
PoolTaskTemplateField, 14, default='AUTO')
# Maximum delay between bot pings before the bot is considered dead
# while running a task.
bot_ping_tolerance_secs = messages.IntegerField(15)
# This is used to make new task request idempotent in best effort.
# If new request has request_uuid field, it checks memcache before scheduling
# actual task to check there is already the task triggered by same request
# previously.
request_uuid = messages.StringField(16)
# Configuration of Swarming:ResultDB integration.
resultdb = messages.MessageField(ResultDBCfg, 17)
# Task realm.
# See api/swarming.proto for more details.
realm = messages.StringField(18)
class TaskRequest(messages.Message):
"""Description of a task request as registered by the server.
This message is used when retrieving information about an existing task.
See NewTaskRequest for more details.
"""
expiration_secs = messages.IntegerField(1)
name = messages.StringField(2)
task_id = messages.StringField(15)
parent_task_id = messages.StringField(3)
priority = messages.IntegerField(4)
# For some amount of time, the properties will be copied into the
# task_slices and vice-versa, to give time to the clients to update.
# Eventually, only task_slices will be supported.
properties = messages.MessageField(TaskProperties, 5)
tags = messages.StringField(6, repeated=True)
created_ts = message_types.DateTimeField(7)
user = messages.StringField(8)
# User name of whoever posted this task, extracted from the credentials.
authenticated = messages.StringField(9)
task_slices = messages.MessageField(TaskSlice, 13, repeated=True)
# Indicates what OAuth2 credentials the task uses when calling other services.
service_account = messages.StringField(10)
realm = messages.StringField(16)
# Configuration of Swarming:ResultDB integration.
resultdb = messages.MessageField(ResultDBCfg, 17)
pubsub_topic = messages.StringField(11)
pubsub_userdata = messages.StringField(12)
bot_ping_tolerance_secs = messages.IntegerField(14)
# RBE migration fields.
rbe_instance = messages.StringField(18)
class TaskCancelRequest(messages.Message):
"""Request to cancel one task."""
kill_running = messages.BooleanField(1)
class TasksCancelRequest(messages.Message):
"""Request to cancel some subset of pending/running tasks."""
tags = messages.StringField(1, repeated=True)
cursor = messages.StringField(2)
limit = messages.IntegerField(3, default=100)
kill_running = messages.BooleanField(4)
end = messages.FloatField(5)
start = messages.FloatField(6)
### Task-Related Responses
class OperationStats(messages.Message):
duration = messages.FloatField(1)
class CASOperationStats(messages.Message):
duration = messages.FloatField(1)
initial_number_items = messages.IntegerField(2)
initial_size = messages.IntegerField(3)
# These buffers are compressed as deflate'd delta-encoded varints. They are
# all the items for an isolated operation, which can scale in the 100k range.
# So can be large! See //client/utils/large.py for the code to handle these.
items_cold = messages.BytesField(4)
items_hot = messages.BytesField(5)
# Corresponding summaries; for each list above, sum of the number of files
# and the sum bytes of the files.
num_items_cold = messages.IntegerField(6)
total_bytes_items_cold = messages.IntegerField(7)
num_items_hot = messages.IntegerField(8)
total_bytes_items_hot = messages.IntegerField(9)
class PerformanceStats(messages.Message):
"""Performance stats of task execution.
See task_result.PerformanceStats for details.
"""
bot_overhead = messages.FloatField(1)
isolated_download = messages.MessageField(CASOperationStats, 2)
isolated_upload = messages.MessageField(CASOperationStats, 3)
package_installation = messages.MessageField(OperationStats, 4)
cache_trim = messages.MessageField(OperationStats, 5)
named_caches_install = messages.MessageField(OperationStats, 6)
named_caches_uninstall = messages.MessageField(OperationStats, 7)
cleanup = messages.MessageField(OperationStats, 8)
class CancelResponse(messages.Message):
"""Result of a request to cancel a task."""
ok = messages.BooleanField(1)
was_running = messages.BooleanField(2)
class TasksCancelResponse(messages.Message):
"""Result of canceling some subset of pending tasks.
"""
cursor = messages.StringField(1)
now = message_types.DateTimeField(2)
matched = messages.IntegerField(3)
class TaskOutput(messages.Message):
"""A task's output as a string."""
output = messages.StringField(1)
# Current state of the task (e.g. PENDING, RUNNING, COMPLETED, EXPIRED, etc).
state = messages.EnumField(TaskState, 2)
class ResultDBInfo(messages.Message):
"""ResultDB related properties."""
# ResultDB hostname, e.g. "results.api.cr.dev"
hostname = messages.StringField(1)
# e.g. "invocations/task-chromium-swarm.appspot.com-deadbeef1"
#
# If the task was deduplicated, this equals invocation name of the original
# task.
invocation = messages.StringField(2)
class TaskResult(messages.Message):
"""Representation of the TaskResultSummary or TaskRunResult ndb model."""
# Time when the task was abandoned instead of normal completion (e.g.
# EXPIRED, BOT_DIED, KILLED).
#
# In the case of KILLED, this records the time the user requested the task to
# stop.
abandoned_ts = message_types.DateTimeField(1)
# The same key cannot be repeated.
bot_dimensions = messages.MessageField(StringListPair, 2, repeated=True)
# Unique ID of the bot.
bot_id = messages.StringField(3)
# Time the bot became ready for a next task.
bot_idle_since_ts = message_types.DateTimeField(32)
# Hash of the bot code which ran the task.
bot_version = messages.StringField(4)
# The cloud project id where the bot saves its logs.
bot_logs_cloud_project = messages.StringField(35)
# Time the task completed normally. Only one of abandoned_ts or completed_ts
# can be set except for state == KILLED.
#
# In case of KILLED, completed_ts is the time the task completed.
completed_ts = message_types.DateTimeField(6)
# $ saved for task with state DEDUPED.
cost_saved_usd = messages.FloatField(7)
# Time the task was requested.
created_ts = message_types.DateTimeField(8)
# Task ID which results was reused for state DEDUPED.
deduped_from = messages.StringField(9)
# Duration of the task in seconds. This excludes overheads.
duration = messages.FloatField(10)
# Process exit code if relevant. May be forcibly set to -1 in exceptional
# cases.
exit_code = messages.IntegerField(11)
# True if exit_code != 0.
failure = messages.BooleanField(12)
# True if state is BOT_DIED.
internal_failure = messages.BooleanField(13)
# Time the results was last updated in the DB.
modified_ts = message_types.DateTimeField(14)
# CAS Digest of the output root uploaded to RBE-CAS.
# This MUST be digest of [build.bazel.remote.execution.v2.Directory].
cas_output_root = messages.MessageField(CASReference, 31)
# Server versions that touched this task.
server_versions = messages.StringField(17, repeated=True)
# Time the task started being run by a bot.
started_ts = message_types.DateTimeField(18)
# Current state of the task (e.g. PENDING, RUNNING, COMPLETED, EXPIRED, etc).
state = messages.EnumField(TaskState, 19)
# Summary task ID (ending with '0') when creating a new task.
task_id = messages.StringField(20)
# Can be multiple values only in TaskResultSummary.
costs_usd = messages.FloatField(22, repeated=True)
# Name of the task. Only set when requesting task ID summary, ending with '0'.
name = messages.StringField(23)
# Tags associated with the task when it was requested. Only set when
# requesting task ID summary, ending with '0'.
tags = messages.StringField(24, repeated=True)
# User on behalf this task was requested. Only set when requesting task ID
# summary, ending with '0'.
user = messages.StringField(25)
# Statistics about overhead for an isolated task. Only sent when requested.
performance_stats = messages.MessageField(PerformanceStats, 26)
# Listing of the ACTUAL pinned CipdPackages that the task used. These can vary
# from the input packages if the inputs included non-identity versions (e.g. a
# ref like "latest").
cipd_pins = messages.MessageField(CipdPins, 27)
# Actual executed task id that this task represents. For deduped tasks, it is
# the same value as deduped_from. This value can be empty if there is no
# execution, for example the task was cancelled.
run_id = messages.StringField(28)
# Index in the TaskRequest.task_slices (TaskSlice instance) that this result
# represents. This is updated when a TaskSlice is enqueued to run.
#
# The TaskSlice contains a TaskProperties, which defines what is run.
current_task_slice = messages.IntegerField(29)
# ResultDB related information.
# None if the integration was not enabled for this task.
resultdb_info = messages.MessageField(ResultDBInfo, 30)
# Reported missing CAS packages on CLIENT_ERROR state
missing_cas = messages.MessageField(CASReference, 33, repeated=True)
# Reported missing CIPD packages on CLIENT_ERROR state
missing_cipd = messages.MessageField(CipdPackage, 34, repeated=True)
class TaskStates(messages.Message):
"""Only holds states. Used in the 'get_states' RPC."""
states = messages.EnumField(TaskState, 1, repeated=True)
class TaskList(messages.Message):
"""Wraps a list of TaskResult."""
# TODO(maruel): Rename to TaskResults.
cursor = messages.StringField(1)
items = messages.MessageField(TaskResult, 2, repeated=True)
now = message_types.DateTimeField(3)
class TaskRequests(messages.Message):
"""Wraps a list of TaskRequest."""
cursor = messages.StringField(1)
items = messages.MessageField(TaskRequest, 2, repeated=True)
now = message_types.DateTimeField(3)
class TasksCount(messages.Message):
"""Returns the count, as requested."""
count = messages.IntegerField(1)
now = message_types.DateTimeField(2)
class TaskRequestMetadata(messages.Message):
"""Provides the ID of the requested TaskRequest."""
request = messages.MessageField(TaskRequest, 1)
task_id = messages.StringField(2)
# Set to finished task result in case task was deduplicated.
task_result = messages.MessageField(TaskResult, 3)
### Task queues
class TaskQueue(messages.Message):
# Must be a list of 'key:value' strings to filter the returned list of bots
# on.
dimensions = messages.StringField(1, repeated=True)
valid_until_ts = message_types.DateTimeField(2)
class TaskQueueList(messages.Message):
cursor = messages.StringField(1)
# Note that it's possible that the RPC returns a tad more or less items than
# requested limit.
items = messages.MessageField(TaskQueue, 2, repeated=True)
now = message_types.DateTimeField(3)
### Bots
### Bot-Related Responses
class BotInfo(messages.Message):
"""Representation of the BotInfo ndb model."""
bot_id = messages.StringField(1)
dimensions = messages.MessageField(StringListPair, 2, repeated=True)
external_ip = messages.StringField(3)
authenticated_as = messages.StringField(4)
first_seen_ts = message_types.DateTimeField(5)
is_dead = messages.BooleanField(6)
last_seen_ts = message_types.DateTimeField(7)
quarantined = messages.BooleanField(8)
maintenance_msg = messages.StringField(18)
task_id = messages.StringField(9)
task_name = messages.StringField(10)
version = messages.StringField(11)
# Encoded as json since it's an arbitrary dict.
state = messages.StringField(12)
deleted = messages.BooleanField(15)
# Deprecated. TODO(crbug/897355): Remove.
lease_id = messages.StringField(13)
lease_expiration_ts = message_types.DateTimeField(14)
machine_type = messages.StringField(16)
machine_lease = messages.StringField(17)
leased_indefinitely = messages.BooleanField(19)
class BotList(messages.Message):
"""Wraps a list of BotInfo."""
cursor = messages.StringField(1)
items = messages.MessageField(BotInfo, 2, repeated=True)
now = message_types.DateTimeField(3)
death_timeout = messages.IntegerField(4)
class BotsCount(messages.Message):
"""Returns the count, as requested."""
now = message_types.DateTimeField(1)
count = messages.IntegerField(2)
quarantined = messages.IntegerField(3)
maintenance = messages.IntegerField(6)
dead = messages.IntegerField(4)
busy = messages.IntegerField(5)
class BotsDimensions(messages.Message):
"""Returns all the dimensions and dimension possibilities in the fleet."""
bots_dimensions = messages.MessageField(StringListPair, 1, repeated=True)
# Time at which this summary was calculated.
ts = message_types.DateTimeField(2)
class BotEvent(messages.Message):
# Timestamp of this event.
ts = message_types.DateTimeField(1)
# Type of event.
event_type = messages.StringField(2)
# Message included in the event.
message = messages.StringField(3)
# Bot dimensions at that moment.
dimensions = messages.MessageField(StringListPair, 4, repeated=True)
# Bot state at that moment, encoded as json.
state = messages.StringField(5)
# IP address as seen by the HTTP handler.
external_ip = messages.StringField(6)
# Bot identity as seen by the HTTP handler.
authenticated_as = messages.StringField(7)
# Version of swarming_bot.zip the bot is currently running.
version = messages.StringField(8)
# If True, the bot is not accepting task due to being quarantined.
quarantined = messages.BooleanField(9)
# If set, the bot is rejecting tasks due to maintenance.
maintenance_msg = messages.StringField(11)
# Affected by event_type == 'request_task', 'task_completed', 'task_error'.
task_id = messages.StringField(10)
class BotEvents(messages.Message):
cursor = messages.StringField(1)
items = messages.MessageField(BotEvent, 2, repeated=True)
now = message_types.DateTimeField(3)
class BotTasks(messages.Message):
cursor = messages.StringField(1)
items = messages.MessageField(TaskResult, 2, repeated=True)
now = message_types.DateTimeField(3)
class DeletedResponse(messages.Message):
"""Indicates whether a bot was deleted."""
deleted = messages.BooleanField(1)
class TerminateResponse(messages.Message):
"""Returns the pseudo taskid to wait for the bot to shut down."""
task_id = messages.StringField(1)