forked from luci/luci-py
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbot_config.py
300 lines (227 loc) · 10.4 KB
/
bot_config.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
# coding: utf-8
# Copyright 2013 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 file is meant to be overriden by the server's specific copy.
You can upload a new version via /restricted/upload/bot_config.
There's 3 types of functions in this file:
- get_*() to return properties to describe this bot.
- on_*() as hooks based on events happening on the bot.
- setup_*() to setup global state on the host.
This file shouldn't import from other scripts in this directory except
os_utilities which is guaranteed to be usable as an API. It's fine to import
from stdlib.
There can be two copies of this file. The base file and a bot specific version:
- For get_*() functions, the bot specific version is called first,
and if missing, the general version is called.
- For on_*() functions, the hook in the general bot_config.py is called first,
then the hook in the bot specific bot_config.py is called.
- For setup_*(), the bot specific bot_config.py is never used.
This file contains unicode to confirm UTF-8 encoded file is well supported.
Here's a pile of poo: 💩
"""
import os
from api import os_utilities
from api import platforms
# Unused argument 'bot' - pylint: disable=W0613
def get_dimensions(bot):
# pylint: disable=line-too-long
"""Returns dict with the bot's dimensions.
The dimensions are what are used to select the bot that can run each task.
The bot id will be automatically selected based on the hostname with
os_utilities.get_dimensions(). If you want something more special, specify it
in your bot_config.py and override the item 'id'.
The dimensions returned here will be joined with server defined dimensions
(extracted from bots.cfg config file based on the bot id). Server defined
dimensions override the ones provided by the bot. See bot.Bot.dimensions for
more information.
See https://github.com/luci/luci-py/tree/master/appengine/swarming/doc/Magic-Values.md.
Arguments:
- bot: bot.Bot instance or None. See ../api/bot.py.
"""
return os_utilities.get_dimensions()
def get_settings(bot):
"""Returns settings for this bot.
This function should be fast and mostly (preferably) constant.
"""
# Here is the default values. Keep in sync with the default values in
# ../bot_code/bot_main.py.
return {
# Free partition (disk) space to keep and to self-quarantine on.
#
# The exact minimum free space can be calculated with:
# max(disk_size * 'min_percent', min('size', disk_size * 'max_percent'))
# where 'min_percent' and 'percent' are relative to the total partition
# size. Setting any value to 0 disables this setting. In practice, with the
# default values:
# - For disks <27GB this will be "disk_size * max_percent"
# - For disks 27GB-80GB this will be 4GB
# - For disks >80GB this will be "disk_size * min_percent"
#
# When trimming the cache, 'wiggle' is added to the value selected above.
#
# The partitions checked for self-quarantine are the root partition (/ on
# POSIX, C:\ on Windows) and the partition containing swarming_bot.zip.
'free_partition': {
# Minimum free space in bytes to use, if lower than 'max_percent'.
'size': 4 * 1024*1024*1024,
# Maximum free space in percent to ensure to keep free, if lower than
# 'size'.
'max_percent': 15.,
# Minimum of of free space percentage, even if higher than 'size'.
'min_percent': 7.,
# Number of bytes to add to the minimum value selected above when
# calculating the isolated cache trimming. This is to ensure that system
# level processes writing logs and such do not cause to criss the
# self-quarantine line while the bot is idle.
'wiggle': 250 * 1024*1024,
},
# Local caches settings.
'caches': {
# Local isolated cache settings, used for isolated tasks. The cache actual
# size is bounded by the lesser of all 3:
# - The cache total size in bytes
# - The number of items in the cache
# - The cache is further trimmed until 'free_partition' value is
# respected.
'isolated': {
# Maximum local isolated cache size in bytes.
'size': 50 * 1024*1024*1024,
# Maximum number of items in the local isolated cache.
'items': 50*1024,
},
},
}
def get_state(bot):
# pylint: disable=line-too-long
"""Returns dict with a state of the bot reported to the server with each poll.
It is only for dynamic state that changes while bot is running for information
for the sysadmins.
The server can not use this state for immediate scheduling purposes (use
'dimensions' for that), but it can use it for maintenance and bookkeeping
tasks.
See https://github.com/luci/luci-py/tree/master/appengine/swarming/doc/Magic-Values.md.
Arguments:
- bot: bot.Bot instance or None. See ../api/bot.py.
"""
return os_utilities.get_state()
def get_authentication_headers(bot):
"""Returns authentication headers and their expiration time.
The returned headers will be passed with each HTTP request to the Swarming
server (and only Swarming server). The bot will use the returned headers until
they are close to expiration (usually 6 min, see AUTH_HEADERS_EXPIRATION_SEC
in remote_client.py), and then it'll attempt to refresh them by calling
get_authentication_headers again.
Can be used to implement per-bot authentication. If no headers are returned,
the server will use only IP whitelist for bot authentication.
On GCE will use OAuth token of the default GCE service account. It should have
"User info" API scope enabled (this can be set when starting an instance). The
server should be configured (via bots.cfg) to trust this account (see
'require_service_account' in bots.proto).
May be called by different threads, but never concurrently.
Arguments:
- bot: bot.Bot instance. See ../api/bot.py.
Returns:
Tuple (dict with headers or None, unix timestamp of when they expire).
"""
if platforms.is_gce():
# By default, VMs do not have "User info" API enabled, as commented above.
# When this is the case, the oauth token is unusable. So do not use the
# oauth token in this case and fall back to IP based whitelisting.
if ('https://www.googleapis.com/auth/userinfo.email' in
platforms.gce.oauth2_available_scopes('default')):
tok, exp = platforms.gce.oauth2_access_token_with_expiration('default')
return {'Authorization': 'Bearer %s' % tok}, exp
return (None, None)
### Hooks
def on_bot_shutdown(bot):
"""Hook function called when the bot shuts down, usually rebooting.
It's a good time to do other kinds of cleanup.
Arguments:
- bot: bot.Bot instance. See ../api/bot.py.
"""
pass
def on_bot_startup(bot):
"""Hook function called when the bot starts, before handshake with the server.
Here the bot may initialize and examine its environment, pick initial state
and dimensions to send to the server during the handshake.
Arguments:
- bot: bot.Bot instance. See ../api/bot.py.
"""
pass
def on_handshake(bot):
"""Hook function called when the bot starts, after handshake with the server.
Here the bot already knows server enforced dimensions (defined in server side
bots.cfg file).
This is called right before starting to poll for tasks. It's a good time to
do some final initialization or cleanup that may depend on server provided
configuration.
Arguments:
- bot: bot.Bot instance. See ../api/bot.py.
"""
pass
def on_before_task(bot, bot_file):
"""Hook function called before running a task.
It shouldn't do much, since it can't cancel the task so it shouldn't do
anything too fancy.
Arguments:
- bot: bot.Bot instance. See ../api/bot.py.
- bot_file: Path to file to write information about the state of the bot.
This file can be used to pass certain info about the bot
to tasks, such as which connected android devices to run on. See
https://github.com/luci/luci-py/tree/master/appengine/swarming/doc/Magic-Values.md#run_isolated
"""
pass
def on_after_task(bot, failure, internal_failure, dimensions, summary):
"""Hook function called after running a task.
It is an excellent place to do post-task cleanup of temporary files.
Arguments:
- bot: bot.Bot instance. See ../api/bot.py.
- failure: bool, True if the task failed.
- internal_failure: bool, True if an internal failure happened.
- dimensions: dict, Dimensions requested as part of the task.
- summary: dict, Summary of the task execution.
"""
# Example code:
#if failure:
# bot.host_reboot('Task failure')
#elif internal_failure:
# bot.host_reboot('Internal failure')
def on_bot_idle(bot, since_last_action):
"""Hook function called once when the bot has been idle; when it has no
command to execute.
This is an excellent place to put device in 'cool down' mode or any
"pre-warming" kind of stuff that could take several seconds to do, that would
not be appropriate to do in on_after_task(). It could be worth waiting for
`since_last_action` to be several seconds before doing a more lengthy
operation.
This function is called repeatedly until an action is taken (a task, updating,
etc).
This is a good place to do "auto reboot" for hardware based bots that are
rebooted periodically.
Arguments:
- bot: bot.Bot instance. See ../api/bot.py.
- since_last_action: time in second since last action; e.g. amount of time the
bot has been idle.
"""
pass
### Setup
def setup_bot(bot):
"""Does one time initialization for this bot.
Returns True if it's fine to start the bot right away. Otherwise, the calling
script should exit.
TODO(maruel): Have the user call bot.host_reboot() or bot.bot_restart()
instead of returning a value.
This is an excellent place to drop a README file in the bot directory, to give
more information about the purpose of this bot.
Example: making this script starts automatically on user login via
os_utilities.set_auto_startup_win() or os_utilities.set_auto_startup_osx().
"""
with open(os.path.join(bot.base_dir, 'README'), 'wb') as f:
f.write(
"""This directory contains a Swarming bot.
Swarming source code is hosted at https://github.com/luci/luci-py.
The bot was generated from the server %s. To get the bot's attributes, run:
python swarming_bot.zip attributes
""" % bot.server)
return True