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

Side by Side Diff: appengine/swarming/handlers_api.py

Issue 1457613002: Add alerts on all old APIs on both Swarming and Isolate servers. (Closed) Base URL: git@github.com:luci/luci-py.git@0_test
Patch Set: Created 5 years, 1 month 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
« no previous file with comments | « appengine/isolate/handlers_api.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2014 The Swarming Authors. All rights reserved. 1 # Copyright 2014 The Swarming Authors. All rights reserved.
2 # Use of this source code is governed by the Apache v2.0 license that can be 2 # Use of this source code is governed by the Apache v2.0 license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Swarming client REST APIs handlers.""" 5 """Swarming client REST APIs handlers."""
6 6
7 import base64 7 import base64
8 import datetime 8 import datetime
9 import json 9 import json
10 import logging 10 import logging
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
81 81
82 82
83 ### New Client APIs. 83 ### New Client APIs.
84 84
85 85
86 class ClientApiListHandler(auth.ApiHandler): 86 class ClientApiListHandler(auth.ApiHandler):
87 """All query handlers""" 87 """All query handlers"""
88 88
89 @auth.public 89 @auth.public
90 def get(self): 90 def get(self):
91 logging.error('Unexpected old client')
91 # Hard to make it any simpler. 92 # Hard to make it any simpler.
92 prefix = '/swarming/api/v1/client/' 93 prefix = '/swarming/api/v1/client/'
93 data = { 94 data = {
94 r.template[len(prefix):]: process_doc(r.handler) for r in get_routes() 95 r.template[len(prefix):]: process_doc(r.handler) for r in get_routes()
95 if r.template.startswith(prefix) and hasattr(r.handler, 'get') 96 if r.template.startswith(prefix) and hasattr(r.handler, 'get')
96 } 97 }
97 self.send_response(data) 98 self.send_response(data)
98 99
99 100
100 class ClientHandshakeHandler(auth.ApiHandler): 101 class ClientHandshakeHandler(auth.ApiHandler):
(...skipping 12 matching lines...) Expand all
113 """ 114 """
114 115
115 # This handler is called to get XSRF token, there's nothing to enforce yet. 116 # This handler is called to get XSRF token, there's nothing to enforce yet.
116 xsrf_token_enforce_on = () 117 xsrf_token_enforce_on = ()
117 118
118 EXPECTED_KEYS = frozenset() 119 EXPECTED_KEYS = frozenset()
119 120
120 @auth.require_xsrf_token_request 121 @auth.require_xsrf_token_request
121 @auth.require(acl.is_bot_or_user) 122 @auth.require(acl.is_bot_or_user)
122 def post(self): 123 def post(self):
124 logging.error('Unexpected old client')
123 request = self.parse_body() 125 request = self.parse_body()
124 log_unexpected_keys( 126 log_unexpected_keys(
125 self.EXPECTED_KEYS, request, self.request, 'client', 'keys') 127 self.EXPECTED_KEYS, request, self.request, 'client', 'keys')
126 data = { 128 data = {
127 # This access token will be used to validate each subsequent request. 129 # This access token will be used to validate each subsequent request.
128 'server_version': utils.get_app_version(), 130 'server_version': utils.get_app_version(),
129 'xsrf_token': self.generate_xsrf_token(), 131 'xsrf_token': self.generate_xsrf_token(),
130 } 132 }
131 self.send_response(data) 133 self.send_response(data)
132 134
133 135
134 class ClientTaskResultBase(auth.ApiHandler): 136 class ClientTaskResultBase(auth.ApiHandler):
135 """Implements the common base code for task related query APIs.""" 137 """Implements the common base code for task related query APIs."""
136 138
137 def get_result_key(self, task_id): 139 def get_result_key(self, task_id):
140 logging.error('Unexpected old client')
138 # TODO(maruel): Users can only request their own task. Privileged users can 141 # TODO(maruel): Users can only request their own task. Privileged users can
139 # request any task. 142 # request any task.
140 key = None 143 key = None
141 summary_key = None 144 summary_key = None
142 try: 145 try:
143 key = task_pack.unpack_result_summary_key(task_id) 146 key = task_pack.unpack_result_summary_key(task_id)
144 summary_key = key 147 summary_key = key
145 except ValueError: 148 except ValueError:
146 try: 149 try:
147 key = task_pack.unpack_run_result_key(task_id) 150 key = task_pack.unpack_run_result_key(task_id)
148 summary_key = task_pack.run_result_key_to_result_summary_key(key) 151 summary_key = task_pack.run_result_key_to_result_summary_key(key)
149 except ValueError: 152 except ValueError:
150 self.abort_with_error(400, error='Invalid key') 153 self.abort_with_error(400, error='Invalid key')
151 return key, summary_key 154 return key, summary_key
152 155
153 def get_result_entity(self, task_id): 156 def get_result_entity(self, task_id):
154 key, _ = self.get_result_key(task_id) 157 key, _ = self.get_result_key(task_id)
155 result = key.get() 158 result = key.get()
156 if not result: 159 if not result:
157 self.abort_with_error(404, error='Task not found') 160 self.abort_with_error(404, error='Task not found')
158 return result 161 return result
159 162
160 163
161 class ClientTaskResultHandler(ClientTaskResultBase): 164 class ClientTaskResultHandler(ClientTaskResultBase):
162 """Task's result meta data""" 165 """Task's result meta data"""
163 166
164 @auth.require(acl.is_bot_or_user) 167 @auth.require(acl.is_bot_or_user)
165 def get(self, task_id): 168 def get(self, task_id):
169 logging.error('Unexpected old client')
166 result = self.get_result_entity(task_id) 170 result = self.get_result_entity(task_id)
167 self.send_response(utils.to_json_encodable(result)) 171 self.send_response(utils.to_json_encodable(result))
168 172
169 173
170 class ClientTaskResultRequestHandler(ClientTaskResultBase): 174 class ClientTaskResultRequestHandler(ClientTaskResultBase):
171 """Task's request details""" 175 """Task's request details"""
172 176
173 @auth.require(acl.is_bot_or_user) 177 @auth.require(acl.is_bot_or_user)
174 def get(self, task_id): 178 def get(self, task_id):
179 logging.error('Unexpected old client')
175 _, summary_key = self.get_result_key(task_id) 180 _, summary_key = self.get_result_key(task_id)
176 request_key = task_pack.result_summary_key_to_request_key(summary_key) 181 request_key = task_pack.result_summary_key_to_request_key(summary_key)
177 self.send_response(utils.to_json_encodable(request_key.get())) 182 self.send_response(utils.to_json_encodable(request_key.get()))
178 183
179 184
180 class ClientTaskResultOutputHandler(ClientTaskResultBase): 185 class ClientTaskResultOutputHandler(ClientTaskResultBase):
181 """Task's output for a single command""" 186 """Task's output for a single command"""
182 187
183 @auth.require(acl.is_bot_or_user) 188 @auth.require(acl.is_bot_or_user)
184 def get(self, task_id, command_index): 189 def get(self, task_id, command_index):
190 logging.error('Unexpected old client')
185 result = self.get_result_entity(task_id) 191 result = self.get_result_entity(task_id)
186 output = result.get_command_output_async(int(command_index)).get_result() 192 output = result.get_command_output_async(int(command_index)).get_result()
187 if output: 193 if output:
188 output = output.decode('utf-8', 'replace') 194 output = output.decode('utf-8', 'replace')
189 # JSON then reencodes to ascii compatible encoded strings, which explodes 195 # JSON then reencodes to ascii compatible encoded strings, which explodes
190 # the size. 196 # the size.
191 data = { 197 data = {
192 'output': output, 198 'output': output,
193 } 199 }
194 self.send_response(utils.to_json_encodable(data)) 200 self.send_response(utils.to_json_encodable(data))
195 201
196 202
197 class ClientTaskResultOutputAllHandler(ClientTaskResultBase): 203 class ClientTaskResultOutputAllHandler(ClientTaskResultBase):
198 """All output from all commands in a task""" 204 """All output from all commands in a task"""
199 205
200 @auth.require(acl.is_bot_or_user) 206 @auth.require(acl.is_bot_or_user)
201 def get(self, task_id): 207 def get(self, task_id):
208 logging.error('Unexpected old client')
202 result = self.get_result_entity(task_id) 209 result = self.get_result_entity(task_id)
203 # JSON then reencodes to ascii compatible encoded strings, which explodes 210 # JSON then reencodes to ascii compatible encoded strings, which explodes
204 # the size. 211 # the size.
205 data = { 212 data = {
206 'outputs': [ 213 'outputs': [
207 i.decode('utf-8', 'replace') if i else i 214 i.decode('utf-8', 'replace') if i else i
208 for i in result.get_outputs() 215 for i in result.get_outputs()
209 ], 216 ],
210 } 217 }
211 self.send_response(utils.to_json_encodable(data)) 218 self.send_response(utils.to_json_encodable(data))
(...skipping 16 matching lines...) Expand all
228 'completed', 'completed_success', 'completed_failure', 'bot_died', 235 'completed', 'completed_success', 'completed_failure', 'bot_died',
229 'expired', 'canceled'. Defaults to 'all'. 236 'expired', 'canceled'. Defaults to 'all'.
230 237
231 In particular, one of `name`, `tag` or `state` can be used 238 In particular, one of `name`, `tag` or `state` can be used
232 exclusively. 239 exclusively.
233 """ 240 """
234 EXPECTED = {'cursor', 'limit', 'name', 'sort', 'state', 'tag'} 241 EXPECTED = {'cursor', 'limit', 'name', 'sort', 'state', 'tag'}
235 242
236 @auth.require(acl.is_privileged_user) 243 @auth.require(acl.is_privileged_user)
237 def get(self): 244 def get(self):
245 logging.error('Unexpected old client')
238 extra = frozenset(self.request.GET) - self.EXPECTED 246 extra = frozenset(self.request.GET) - self.EXPECTED
239 if extra: 247 if extra:
240 self.abort_with_error( 248 self.abort_with_error(
241 400, 249 400,
242 error='Extraneous query parameters. Did you make a typo? %s' % 250 error='Extraneous query parameters. Did you make a typo? %s' %
243 ','.join(sorted(extra))) 251 ','.join(sorted(extra)))
244 252
245 # Use a similar query to /user/tasks. 253 # Use a similar query to /user/tasks.
246 name = self.request.get('name') 254 name = self.request.get('name')
247 tags = self.request.get_all('tag') 255 tags = self.request.get_all('tag')
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
290 'completed_success', 298 'completed_success',
291 'expired', 299 'expired',
292 'pending', 300 'pending',
293 'pending_running', 301 'pending_running',
294 'running', 302 'running',
295 'timed_out', 303 'timed_out',
296 } 304 }
297 305
298 @auth.require(acl.is_privileged_user) 306 @auth.require(acl.is_privileged_user)
299 def get(self): 307 def get(self):
308 logging.error('Unexpected old client')
300 extra = frozenset(self.request.GET) - self.EXPECTED 309 extra = frozenset(self.request.GET) - self.EXPECTED
301 if extra: 310 if extra:
302 self.abort_with_error( 311 self.abort_with_error(
303 400, 312 400,
304 error='Extraneous query parameters. Did you make a typo? %s' % 313 error='Extraneous query parameters. Did you make a typo? %s' %
305 ','.join(sorted(extra))) 314 ','.join(sorted(extra)))
306 315
307 interval = self.request.get('interval', 24 * 3600) 316 interval = self.request.get('interval', 24 * 3600)
308 state = self.request.get('state', 'all') 317 state = self.request.get('state', 'all')
309 tags = self.request.get_all('tag') 318 tags = self.request.get_all('tag')
(...skipping 25 matching lines...) Expand all
335 class ClientApiBots(auth.ApiHandler): 344 class ClientApiBots(auth.ApiHandler):
336 """Bots known to the server""" 345 """Bots known to the server"""
337 346
338 ACCEPTABLE_FILTERS = ( 347 ACCEPTABLE_FILTERS = (
339 'quarantined', 348 'quarantined',
340 'is_dead', 349 'is_dead',
341 ) 350 )
342 351
343 @auth.require(acl.is_privileged_user) 352 @auth.require(acl.is_privileged_user)
344 def get(self): 353 def get(self):
354 logging.error('Unexpected old client')
345 now = utils.utcnow() 355 now = utils.utcnow()
346 limit = int(self.request.get('limit', 1000)) 356 limit = int(self.request.get('limit', 1000))
347 filter_by = self.request.get('filter') 357 filter_by = self.request.get('filter')
348 if filter_by and filter_by not in self.ACCEPTABLE_FILTERS: 358 if filter_by and filter_by not in self.ACCEPTABLE_FILTERS:
349 self.abort_with_error(400, error='Invalid filter query parameter') 359 self.abort_with_error(400, error='Invalid filter query parameter')
350 360
351 q = bot_management.BotInfo.query() 361 q = bot_management.BotInfo.query()
352 362
353 if not filter_by: 363 if not filter_by:
354 q = q.order(bot_management.BotInfo.key) 364 q = q.order(bot_management.BotInfo.key)
(...skipping 23 matching lines...) Expand all
378 'now': now, 388 'now': now,
379 } 389 }
380 self.send_response(utils.to_json_encodable(data)) 390 self.send_response(utils.to_json_encodable(data))
381 391
382 392
383 class ClientApiBot(auth.ApiHandler): 393 class ClientApiBot(auth.ApiHandler):
384 """Bot's meta data""" 394 """Bot's meta data"""
385 395
386 @auth.require(acl.is_privileged_user) 396 @auth.require(acl.is_privileged_user)
387 def get(self, bot_id): 397 def get(self, bot_id):
398 logging.error('Unexpected old client')
388 bot = bot_management.get_info_key(bot_id).get() 399 bot = bot_management.get_info_key(bot_id).get()
389 if not bot: 400 if not bot:
390 self.abort_with_error(404, error='Bot not found') 401 self.abort_with_error(404, error='Bot not found')
391 now = utils.utcnow() 402 now = utils.utcnow()
392 self.send_response(utils.to_json_encodable(bot.to_dict_with_now(now))) 403 self.send_response(utils.to_json_encodable(bot.to_dict_with_now(now)))
393 404
394 @auth.require(acl.is_admin) 405 @auth.require(acl.is_admin)
395 def delete(self, bot_id): 406 def delete(self, bot_id):
396 # Only delete BotInfo, not BotRoot, BotEvent nor BotSettings. 407 # Only delete BotInfo, not BotRoot, BotEvent nor BotSettings.
397 bot_key = bot_management.get_info_key(bot_id) 408 bot_key = bot_management.get_info_key(bot_id)
398 found = False 409 found = False
399 if bot_key.get(): 410 if bot_key.get():
400 bot_key.delete() 411 bot_key.delete()
401 found = True 412 found = True
402 self.send_response({'deleted': bool(found)}) 413 self.send_response({'deleted': bool(found)})
403 414
404 415
405 class ClientApiBotTask(auth.ApiHandler): 416 class ClientApiBotTask(auth.ApiHandler):
406 """Tasks executed on a specific bot""" 417 """Tasks executed on a specific bot"""
407 418
408 @auth.require(acl.is_privileged_user) 419 @auth.require(acl.is_privileged_user)
409 def get(self, bot_id): 420 def get(self, bot_id):
421 logging.error('Unexpected old client')
410 limit = int(self.request.get('limit', 100)) 422 limit = int(self.request.get('limit', 100))
411 cursor = datastore_query.Cursor(urlsafe=self.request.get('cursor')) 423 cursor = datastore_query.Cursor(urlsafe=self.request.get('cursor'))
412 run_results, cursor, more = task_result.TaskRunResult.query( 424 run_results, cursor, more = task_result.TaskRunResult.query(
413 task_result.TaskRunResult.bot_id == bot_id).order( 425 task_result.TaskRunResult.bot_id == bot_id).order(
414 -task_result.TaskRunResult.started_ts).fetch_page( 426 -task_result.TaskRunResult.started_ts).fetch_page(
415 limit, start_cursor=cursor) 427 limit, start_cursor=cursor)
416 now = utils.utcnow() 428 now = utils.utcnow()
417 data = { 429 data = {
418 'cursor': cursor.urlsafe() if cursor and more else None, 430 'cursor': cursor.urlsafe() if cursor and more else None,
419 'items': run_results, 431 'items': run_results,
420 'limit': limit, 432 'limit': limit,
421 'now': now, 433 'now': now,
422 } 434 }
423 self.send_response(utils.to_json_encodable(data)) 435 self.send_response(utils.to_json_encodable(data))
424 436
425 437
426 class ClientApiServer(auth.ApiHandler): 438 class ClientApiServer(auth.ApiHandler):
427 """Server details""" 439 """Server details"""
428 440
429 @auth.require(acl.is_privileged_user) 441 @auth.require(acl.is_privileged_user)
430 def get(self): 442 def get(self):
443 logging.error('Unexpected old client')
431 data = { 444 data = {
432 'bot_version': bot_code.get_bot_version(self.request.host_url), 445 'bot_version': bot_code.get_bot_version(self.request.host_url),
433 } 446 }
434 self.send_response(utils.to_json_encodable(data)) 447 self.send_response(utils.to_json_encodable(data))
435 448
436 449
437 class ClientRequestHandler(auth.ApiHandler): 450 class ClientRequestHandler(auth.ApiHandler):
438 """Creates a new request, returns the task id. 451 """Creates a new request, returns the task id.
439 452
440 Argument: 453 Argument:
(...skipping 29 matching lines...) Expand all
470 # task itself, e.g. what to run. 483 # task itself, e.g. what to run.
471 _REQUIRED_PROPERTIES_KEYS= frozenset( 484 _REQUIRED_PROPERTIES_KEYS= frozenset(
472 ['commands', 'data', 'dimensions', 'env', 'execution_timeout_secs', 485 ['commands', 'data', 'dimensions', 'env', 'execution_timeout_secs',
473 'io_timeout_secs']) 486 'io_timeout_secs'])
474 _EXPECTED_PROPERTIES_KEYS = frozenset( 487 _EXPECTED_PROPERTIES_KEYS = frozenset(
475 ['commands', 'data', 'dimensions', 'env', 'execution_timeout_secs', 488 ['commands', 'data', 'dimensions', 'env', 'execution_timeout_secs',
476 'grace_period_secs', 'idempotent', 'io_timeout_secs']) 489 'grace_period_secs', 'idempotent', 'io_timeout_secs'])
477 490
478 @auth.require(acl.is_bot_or_user) 491 @auth.require(acl.is_bot_or_user)
479 def post(self): 492 def post(self):
493 logging.error('Unexpected old client')
480 data = self.parse_body() 494 data = self.parse_body()
481 msg = log_unexpected_subset_keys( 495 msg = log_unexpected_subset_keys(
482 self._EXPECTED_DATA_KEYS, self._REQUIRED_DATA_KEYS, data, self.request, 496 self._EXPECTED_DATA_KEYS, self._REQUIRED_DATA_KEYS, data, self.request,
483 'client', 'request keys') 497 'client', 'request keys')
484 if msg: 498 if msg:
485 self.abort_with_error(400, error=msg) 499 self.abort_with_error(400, error=msg)
486 data_properties = data['properties'] 500 data_properties = data['properties']
487 msg = log_unexpected_subset_keys( 501 msg = log_unexpected_subset_keys(
488 self._EXPECTED_PROPERTIES_KEYS, self._REQUIRED_PROPERTIES_KEYS, 502 self._EXPECTED_PROPERTIES_KEYS, self._REQUIRED_PROPERTIES_KEYS,
489 data_properties, self.request, 'client', 'request properties keys') 503 data_properties, self.request, 'client', 'request properties keys')
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
529 self.send_response(utils.to_json_encodable(data)) 543 self.send_response(utils.to_json_encodable(data))
530 544
531 545
532 class ClientCancelHandler(auth.ApiHandler): 546 class ClientCancelHandler(auth.ApiHandler):
533 """Cancels a task.""" 547 """Cancels a task."""
534 548
535 # TODO(maruel): Allow privileged users to cancel, and users to cancel their 549 # TODO(maruel): Allow privileged users to cancel, and users to cancel their
536 # own task. 550 # own task.
537 @auth.require(acl.is_admin) 551 @auth.require(acl.is_admin)
538 def post(self): 552 def post(self):
553 logging.error('Unexpected old client')
539 request = self.parse_body() 554 request = self.parse_body()
540 task_id = request.get('task_id') 555 task_id = request.get('task_id')
541 summary_key = task_pack.unpack_result_summary_key(task_id) 556 summary_key = task_pack.unpack_result_summary_key(task_id)
542 557
543 ok, was_running = task_scheduler.cancel_task(summary_key) 558 ok, was_running = task_scheduler.cancel_task(summary_key)
544 out = { 559 out = {
545 'ok': ok, 560 'ok': ok,
546 'was_running': was_running, 561 'was_running': was_running,
547 } 562 }
548 self.send_response(out) 563 self.send_response(out)
(...skipping 15 matching lines...) Expand all
564 ClientTaskResultRequestHandler), 579 ClientTaskResultRequestHandler),
565 ('/swarming/api/v1/client/task/<task_id:[0-9a-f]+>/output/' 580 ('/swarming/api/v1/client/task/<task_id:[0-9a-f]+>/output/'
566 '<command_index:[0-9]+>', 581 '<command_index:[0-9]+>',
567 ClientTaskResultOutputHandler), 582 ClientTaskResultOutputHandler),
568 ('/swarming/api/v1/client/task/<task_id:[0-9a-f]+>/output/all', 583 ('/swarming/api/v1/client/task/<task_id:[0-9a-f]+>/output/all',
569 ClientTaskResultOutputAllHandler), 584 ClientTaskResultOutputAllHandler),
570 ('/swarming/api/v1/client/tasks', ClientApiTasksHandler), 585 ('/swarming/api/v1/client/tasks', ClientApiTasksHandler),
571 ('/swarming/api/v1/client/tasks/count', ClientApiTasksCountHandler), 586 ('/swarming/api/v1/client/tasks/count', ClientApiTasksCountHandler),
572 ] 587 ]
573 return [webapp2.Route(*i) for i in routes] 588 return [webapp2.Route(*i) for i in routes]
OLDNEW
« no previous file with comments | « appengine/isolate/handlers_api.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698