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

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

Issue 1458553003: Delete old APIs on both Swarming and Isolate servers. (Closed) Base URL: git@github.com:luci/luci-py.git@1_warning
Patch Set: Rebasing on HEAD Created 4 years, 10 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
« no previous file with comments | « appengine/swarming/handlers_api.py ('k') | appengine/swarming/handlers_frontend.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # coding: utf-8
3 # Copyright 2014 The Swarming Authors. All rights reserved.
4 # Use of this source code is governed by the Apache v2.0 license that can be
5 # found in the LICENSE file.
6
7 import base64
8 import datetime
9 import logging
10 import os
11 import random
12 import sys
13 import unittest
14
15 # Setups environment.
16 import test_env_handlers
17
18 import webapp2
19 import webtest
20
21 import handlers_api
22 import handlers_bot
23 from components import ereporter2
24 from components import utils
25 from server import config
26 from server import bot_code
27 from server import bot_management
28 from server import task_result
29
30
31 class ClientApiTest(test_env_handlers.AppTestBase):
32 def setUp(self):
33 super(ClientApiTest, self).setUp()
34 # By default requests in tests are coming from bot with fake IP.
35 routes = handlers_bot.get_routes() + handlers_api.get_routes()
36 app = webapp2.WSGIApplication(routes, debug=True)
37 self.app = webtest.TestApp(
38 app,
39 extra_environ={
40 'REMOTE_ADDR': self.source_ip,
41 'SERVER_SOFTWARE': os.environ['SERVER_SOFTWARE'],
42 })
43 self.mock(
44 ereporter2, 'log_request',
45 lambda *args, **kwargs: self.fail('%s, %s' % (args, kwargs)))
46 # Client API test cases run by default as user.
47 self.set_as_user()
48
49 def get_client_token(self):
50 """Gets the XSRF token for client after handshake."""
51 headers = {'X-XSRF-Token-Request': '1'}
52 params = {}
53 response = self.app.post_json(
54 '/swarming/api/v1/client/handshake',
55 headers=headers,
56 params=params).json
57 return response['xsrf_token'].encode('ascii')
58
59 def test_list(self):
60 self.set_as_anonymous()
61 response = self.app.get('/swarming/api/v1/client/list').json
62 expected = {
63 u'bot/<bot_id:[^/]+>': u'Bot\'s meta data',
64 u'bot/<bot_id:[^/]+>/tasks': u'Tasks executed on a specific bot',
65 u'bots': u'Bots known to the server',
66 u'list': u'All query handlers',
67 u'server': u'Server details',
68 u'task/<task_id:[0-9a-f]+>': u'Task\'s result meta data',
69 u'task/<task_id:[0-9a-f]+>/output/<command_index:[0-9]+>':
70 u'Task\'s output for a single command',
71 u'task/<task_id:[0-9a-f]+>/output/all':
72 u'All output from all commands in a task',
73 u'task/<task_id:[0-9a-f]+>/request': u'Task\'s request details',
74 u'tasks': handlers_api.process_doc(handlers_api.ClientApiTasksHandler),
75 u'tasks/count': handlers_api.process_doc(
76 handlers_api.ClientApiTasksCountHandler),
77 }
78 self.assertEqual(expected, response)
79
80 def test_handshake(self):
81 # Bare minimum:
82 headers = {'X-XSRF-Token-Request': '1'}
83 params = {}
84 response = self.app.post_json(
85 '/swarming/api/v1/client/handshake',
86 headers=headers, params=params).json
87 self.assertEqual(
88 [u'server_version', u'xsrf_token'], sorted(response))
89 self.assertTrue(response['xsrf_token'])
90 self.assertEqual(u'v1a', response['server_version'])
91
92 def test_handshake_extra(self):
93 errors = []
94 def add_error(request, source, message):
95 self.assertTrue(request)
96 self.assertEqual('client', source)
97 errors.append(message)
98 self.mock(ereporter2, 'log_request', add_error)
99 headers = {'X-XSRF-Token-Request': '1'}
100 params = {
101 # Works with unknown items but logs an error. This permits catching typos.
102 'foo': 1,
103 }
104 response = self.app.post_json(
105 '/swarming/api/v1/client/handshake',
106 headers=headers, params=params).json
107 self.assertEqual(
108 [u'server_version', u'xsrf_token'], sorted(response))
109 self.assertTrue(response['xsrf_token'])
110 self.assertEqual(u'v1a', response['server_version'])
111 expected = [
112 'Unexpected keys superfluous: [u\'foo\']; did you make a typo?',
113 ]
114 self.assertEqual(expected, errors)
115
116 def test_request_invalid(self):
117 record = []
118 self.mock(
119 ereporter2, 'log_request',
120 lambda *args, **kwargs: record.append((args, kwargs)))
121 headers = {'X-XSRF-Token-Request': '1'}
122 response = self.app.post_json(
123 '/swarming/api/v1/client/handshake', headers=headers, params={}).json
124 params = {
125 'foo': 'bar',
126 'properties': {},
127 'scheduling_expiration_secs': 30,
128 'tags': [],
129 }
130 headers = {'X-XSRF-Token': str(response['xsrf_token'])}
131 response = self.app.post_json(
132 '/swarming/api/v1/client/request',
133 headers=headers, params=params, status=400).json
134 expected = {
135 u'error':
136 u'Unexpected request keys missing: '
137 u'[\'name\', \'priority\', \'user\'] superfluous: [u\'foo\']; '
138 u'did you make a typo?',
139 }
140 self.assertEqual(expected, response)
141
142 def test_request_invalid_lower_level(self):
143 headers = {'X-XSRF-Token-Request': '1'}
144 response = self.app.post_json(
145 '/swarming/api/v1/client/handshake', headers=headers, params={}).json
146 params = {
147 'name': 'job1',
148 'priority': 200,
149 'properties': {
150 'commands': [],
151 'data': [],
152 'dimensions': {},
153 'env': {},
154 'execution_timeout_secs': 10,
155 'io_timeout_secs': 10,
156 },
157 'scheduling_expiration_secs': 30,
158 'tags': ['foo:bar'],
159 'user': 'joe@localhost',
160 }
161 headers = {'X-XSRF-Token': str(response['xsrf_token'])}
162 response = self.app.post_json(
163 '/swarming/api/v1/client/request',
164 headers=headers, params=params, status=400).json
165 self.assertEqual({u'error': u'use one of command or inputs_ref'}, response)
166
167 def test_request(self):
168 self.mock(random, 'getrandbits', lambda _: 0x88)
169 now = datetime.datetime(2010, 1, 2, 3, 4, 5)
170 self.mock_now(now)
171 str_now = unicode(now.strftime(utils.DATETIME_FORMAT))
172 headers = {'X-XSRF-Token-Request': '1'}
173 response = self.app.post_json(
174 '/swarming/api/v1/client/handshake', headers=headers, params={}).json
175 params = {
176 'name': 'job1',
177 'priority': 200,
178 'properties': {
179 'commands': [['rm', '-rf', '/']],
180 'data': [],
181 'dimensions': {},
182 'env': {},
183 'execution_timeout_secs': 30,
184 'io_timeout_secs': 30,
185 },
186 'scheduling_expiration_secs': 30,
187 'tags': ['foo:bar'],
188 'user': 'joe@localhost',
189 }
190 headers = {'X-XSRF-Token': str(response['xsrf_token'])}
191 response = self.app.post_json(
192 '/swarming/api/v1/client/request',
193 headers=headers, params=params).json
194 expected = {
195 u'request': {
196 u'authenticated': [u'user', u'user@example.com'],
197 u'created_ts': str_now,
198 u'expiration_ts': unicode(
199 (now + datetime.timedelta(seconds=30)).strftime(
200 utils.DATETIME_FORMAT)),
201 u'name': u'job1',
202 u'parent_task_id': None,
203 u'priority': 200,
204 u'properties': {
205 u'commands': [[u'rm', u'-rf', u'/']],
206 u'data': [],
207 u'dimensions': {},
208 u'env': {},
209 u'execution_timeout_secs': 30,
210 u'extra_args': [],
211 u'grace_period_secs': 30,
212 u'idempotent': False,
213 u'inputs_ref': None,
214 u'io_timeout_secs': 30,
215 },
216 u'properties_hash': None,
217 u'pubsub_topic': None,
218 u'pubsub_userdata': None,
219 u'tags': [
220 u'foo:bar',
221 u'priority:200',
222 u'user:joe@localhost',
223 ],
224 u'user': u'joe@localhost',
225 },
226 u'task_id': u'5cee488008810',
227 }
228 self.assertEqual(expected, response)
229
230 def test_cancel(self):
231 self.mock(random, 'getrandbits', lambda _: 0x88)
232 now = datetime.datetime(2010, 1, 2, 3, 4, 5)
233 self.mock_now(now)
234 str_now = unicode(now.strftime(utils.DATETIME_FORMAT))
235 self.set_as_admin()
236 token = self.get_client_token()
237 _, task_id = self.client_create_task_raw()
238 params = {
239 'task_id': task_id,
240 }
241 response = self.post_with_token(
242 '/swarming/api/v1/client/cancel', params, token)
243 expected = {
244 u'ok': True,
245 u'was_running': False,
246 }
247 self.assertEqual(expected, response)
248 response = self.app.get(
249 '/swarming/api/v1/client/task/' + task_id).json
250 expected = {
251 u'abandoned_ts': str_now,
252 u'bot_dimensions': None,
253 u'bot_id': None,
254 u'bot_version': None,
255 u'children_task_ids': [],
256 u'completed_ts': None,
257 u'costs_usd': [],
258 u'cost_saved_usd': None,
259 u'created_ts': str_now,
260 u'deduped_from': None,
261 u'durations': [],
262 u'exit_codes': [],
263 u'failure': False,
264 u'id': task_id,
265 u'internal_failure': False,
266 u'modified_ts': str_now,
267 u'name': u'hi',
268 u'outputs_ref': None,
269 u'properties_hash': None,
270 u'server_versions': [],
271 u'started_ts': None,
272 u'state': task_result.State.CANCELED,
273 u'tags': [u'os:Amiga', u'priority:10', u'user:joe@localhost'],
274 u'try_number': None,
275 u'user': u'joe@localhost',
276 }
277 self.assertEqual(expected, response)
278
279 def test_get_task_metadata_unknown(self):
280 response = self.app.get(
281 '/swarming/api/v1/client/task/12300', status=404).json
282 self.assertEqual({u'error': u'Task not found'}, response)
283
284 def test_get_task_metadata(self):
285 self.mock(random, 'getrandbits', lambda _: 0x88)
286 now = datetime.datetime(2010, 1, 2, 3, 4, 5)
287 self.mock_now(now)
288 str_now = unicode(now.strftime(utils.DATETIME_FORMAT))
289 _, task_id = self.client_create_task_raw()
290 response = self.app.get(
291 '/swarming/api/v1/client/task/' + task_id).json
292 expected = {
293 u'abandoned_ts': None,
294 u'bot_dimensions': None,
295 u'bot_id': None,
296 u'bot_version': None,
297 u'children_task_ids': [],
298 u'completed_ts': None,
299 u'costs_usd': [],
300 u'cost_saved_usd': None,
301 u'created_ts': str_now,
302 u'deduped_from': None,
303 u'durations': [],
304 u'exit_codes': [],
305 u'failure': False,
306 u'id': u'5cee488008810',
307 u'internal_failure': False,
308 u'modified_ts': str_now,
309 u'name': u'hi',
310 u'outputs_ref': None,
311 u'properties_hash': None,
312 u'server_versions': [],
313 u'started_ts': None,
314 u'state': task_result.State.PENDING,
315 u'tags': [u'os:Amiga', u'priority:100', u'user:joe@localhost'],
316 u'try_number': None,
317 u'user': u'joe@localhost',
318 }
319 self.assertEqual(expected, response)
320 self.assertEqual('0', task_id[-1])
321
322 # No bot started yet.
323 run_id = task_id[:-1] + '1'
324 response = self.app.get(
325 '/swarming/api/v1/client/task/' + run_id, status=404).json
326 self.assertEqual({u'error': u'Task not found'}, response)
327
328 self.set_as_bot()
329 self.bot_poll('bot1')
330
331 self.set_as_user()
332 response = self.app.get(
333 '/swarming/api/v1/client/task/' + run_id).json
334 expected = {
335 u'abandoned_ts': None,
336 u'bot_dimensions': {u'id': [u'bot1'], u'os': [u'Amiga']},
337 u'bot_id': u'bot1',
338 u'bot_version': self.bot_version,
339 u'children_task_ids': [],
340 u'completed_ts': None,
341 u'cost_usd': 0.,
342 u'durations': [],
343 u'exit_codes': [],
344 u'failure': False,
345 u'id': u'5cee488008811',
346 u'internal_failure': False,
347 u'modified_ts': str_now,
348 u'outputs_ref': None,
349 u'server_versions': [u'v1a'],
350 u'started_ts': str_now,
351 u'state': task_result.State.RUNNING,
352 u'try_number': 1,
353 }
354 self.assertEqual(expected, response)
355
356 def test_get_task_metadata_denied(self):
357 # Asserts that a non-public task can not be seen by an anonymous user.
358 _, task_id = self.client_create_task_raw()
359
360 self.set_as_anonymous()
361 self.app.get('/swarming/api/v1/client/task/' + task_id, status=403)
362 self.assertEqual('0', task_id[-1])
363
364 def test_get_task_output(self):
365 self.client_create_task_raw()
366
367 self.set_as_bot()
368 task_id = self.bot_run_task()
369
370 self.set_as_privileged_user()
371 run_id = task_id[:-1] + '1'
372 response = self.app.get(
373 '/swarming/api/v1/client/task/%s/output/0' % task_id).json
374 self.assertEqual({'output': u'rÉsult string'}, response)
375 response = self.app.get(
376 '/swarming/api/v1/client/task/%s/output/0' % run_id).json
377 self.assertEqual({'output': u'rÉsult string'}, response)
378
379 response = self.app.get(
380 '/swarming/api/v1/client/task/%s/output/1' % task_id).json
381 self.assertEqual({'output': None}, response)
382 response = self.app.get(
383 '/swarming/api/v1/client/task/%s/output/1' % run_id).json
384 self.assertEqual({'output': None}, response)
385
386 def test_get_task_output_empty(self):
387 _, task_id = self.client_create_task_raw()
388 response = self.app.get(
389 '/swarming/api/v1/client/task/%s/output/0' % task_id).json
390 self.assertEqual({'output': None}, response)
391
392 run_id = task_id[:-1] + '1'
393 response = self.app.get(
394 '/swarming/api/v1/client/task/%s/output/0' % run_id, status=404).json
395 self.assertEqual({u'error': u'Task not found'}, response)
396
397 def test_task_deduped(self):
398 _, task_id_1 = self.client_create_task_raw(properties=dict(idempotent=True))
399
400 self.set_as_bot()
401 task_id_bot = self.bot_run_task()
402 self.assertEqual(task_id_1, task_id_bot[:-1] + '0')
403 self.assertEqual('1', task_id_bot[-1:])
404
405 # Create a second task. Results will be returned immediately without the bot
406 # running anything.
407 self.set_as_user()
408 _, task_id_2 = self.client_create_task_raw(
409 name='second', user='jack@localhost', properties=dict(idempotent=True))
410
411 self.set_as_bot()
412 resp = self.bot_poll()
413 self.assertEqual('sleep', resp['cmd'])
414
415 self.set_as_user()
416 # Look at the results. It's the same as the previous run, even if task_id_2
417 # was never executed.
418 response = self.app.get(
419 '/swarming/api/v1/client/task/%s/output/all' % task_id_2).json
420 self.assertEqual({'outputs': [u'rÉsult string']}, response)
421
422 def test_get_task_output_all(self):
423 self.client_create_task_raw()
424
425 self.set_as_bot()
426 token, _ = self.get_bot_token()
427 res = self.bot_poll()
428 task_id = res['manifest']['task_id']
429 params = {
430 'cost_usd': 0.1,
431 'duration': 0.1,
432 'exit_code': 0,
433 'id': 'bot1',
434 'output': base64.b64encode('result string'),
435 'output_chunk_start': 0,
436 'task_id': task_id,
437 }
438 response = self.post_with_token(
439 '/swarming/api/v1/bot/task_update', params, token)
440 self.assertEqual({u'ok': True}, response)
441
442 self.set_as_privileged_user()
443 run_id = task_id[:-1] + '1'
444 response = self.app.get(
445 '/swarming/api/v1/client/task/%s/output/all' % task_id).json
446 self.assertEqual({'outputs': [u'result string']}, response)
447 response = self.app.get(
448 '/swarming/api/v1/client/task/%s/output/all' % run_id).json
449 self.assertEqual({'outputs': [u'result string']}, response)
450
451 def test_get_task_output_all_empty(self):
452 _, task_id = self.client_create_task_raw()
453 response = self.app.get(
454 '/swarming/api/v1/client/task/%s/output/all' % task_id).json
455 self.assertEqual({'outputs': []}, response)
456
457 run_id = task_id[:-1] + '1'
458 response = self.app.get(
459 '/swarming/api/v1/client/task/%s/output/all' % run_id, status=404).json
460 self.assertEqual({u'error': u'Task not found'}, response)
461
462 def test_get_task_request(self):
463 now = datetime.datetime(2010, 1, 2, 3, 4, 5, 6)
464 self.mock_now(now)
465 _, task_id = self.client_create_task_raw()
466 response = self.app.get(
467 '/swarming/api/v1/client/task/%s/request' % task_id).json
468 expected = {
469 u'authenticated': [u'user', u'user@example.com'],
470 u'created_ts': unicode(now.strftime(utils.DATETIME_FORMAT)),
471 u'expiration_ts': unicode(
472 (now + datetime.timedelta(days=1)).strftime(utils.DATETIME_FORMAT)),
473 u'name': u'hi',
474 u'parent_task_id': None,
475 u'priority': 100,
476 u'properties': {
477 u'commands': [[u'python', u'run_test.py']],
478 u'data': [],
479 u'dimensions': {u'os': u'Amiga'},
480 u'env': {},
481 u'execution_timeout_secs': 3600,
482 u'extra_args': [],
483 u'grace_period_secs': 30,
484 u'idempotent': False,
485 u'inputs_ref': None,
486 u'io_timeout_secs': 1200,
487 },
488 u'properties_hash': None,
489 u'pubsub_topic': None,
490 u'pubsub_userdata': None,
491 u'tags': [u'os:Amiga', u'priority:100', u'user:joe@localhost'],
492 u'user': u'joe@localhost',
493 }
494 self.assertEqual(expected, response)
495
496 def test_tasks(self):
497 # Create two tasks, one deduped.
498 self.mock(random, 'getrandbits', lambda _: 0x66)
499 now = datetime.datetime(2010, 1, 2, 3, 4, 5, 6)
500 now_str = unicode(now.strftime(utils.DATETIME_FORMAT))
501 self.mock_now(now)
502 self.client_create_task_raw(
503 name='first', tags=['project:yay', 'commit:post', 'os:Win'],
504 properties=dict(idempotent=True))
505 self.set_as_bot()
506 self.bot_run_task()
507
508 self.set_as_user()
509 self.mock(random, 'getrandbits', lambda _: 0x88)
510 now_60 = self.mock_now(now, 60)
511 now_60_str = unicode(now_60.strftime(utils.DATETIME_FORMAT))
512 self.client_create_task_raw(
513 name='second', user='jack@localhost',
514 tags=['project:yay', 'commit:pre', 'os:Win'],
515 properties=dict(idempotent=True))
516
517 self.set_as_privileged_user()
518 expected_first = {
519 u'abandoned_ts': None,
520 u'bot_dimensions': {u'id': [u'bot1'], u'os': [u'Amiga']},
521 u'bot_id': u'bot1',
522 u'bot_version': self.bot_version,
523 u'children_task_ids': [],
524 u'completed_ts': now_str,
525 u'costs_usd': [0.1],
526 u'cost_saved_usd': None,
527 u'created_ts': now_str,
528 u'deduped_from': None,
529 u'durations': [0.1],
530 u'exit_codes': [0],
531 u'failure': False,
532 u'id': u'5cee488006610',
533 u'internal_failure': False,
534 u'modified_ts': now_str,
535 u'name': u'first',
536 u'outputs_ref': None,
537 u'properties_hash': u'8771754ee465a689f19c87f2d21ea0d9b8dd4f64',
538 u'server_versions': [u'v1a'],
539 u'started_ts': now_str,
540 u'state': task_result.State.COMPLETED,
541 u'tags': [
542 u'commit:post',
543 u'os:Amiga',
544 u'os:Win',
545 u'priority:100',
546 u'project:yay',
547 u'user:joe@localhost',
548 ],
549 u'try_number': 1,
550 u'user': u'joe@localhost',
551 }
552 expected_second = {
553 u'abandoned_ts': None,
554 u'bot_dimensions': {u'id': [u'bot1'], u'os': [u'Amiga']},
555 u'bot_id': u'bot1',
556 u'bot_version': self.bot_version,
557 u'children_task_ids': [],
558 u'completed_ts': now_str,
559 u'costs_usd': [],
560 u'cost_saved_usd': 0.1,
561 u'created_ts': now_60_str,
562 u'deduped_from': u'5cee488006611',
563 u'durations': [0.1],
564 u'exit_codes': [0],
565 u'failure': False,
566 u'id': u'5cfcee8008810',
567 u'internal_failure': False,
568 u'modified_ts': now_60_str,
569 u'name': u'second',
570 u'outputs_ref': None,
571 u'properties_hash': None,
572 u'server_versions': [u'v1a'],
573 u'started_ts': now_str,
574 u'state': task_result.State.COMPLETED,
575 u'tags': [
576 u'commit:pre',
577 u'os:Amiga',
578 u'os:Win',
579 u'priority:100',
580 u'project:yay',
581 u'user:jack@localhost',
582 ],
583 u'try_number': 0,
584 u'user': u'jack@localhost',
585 }
586
587 expected = {
588 u'cursor': None,
589 u'items': [expected_second, expected_first],
590 u'limit': 100,
591 u'sort': u'created_ts',
592 u'state': u'all',
593 }
594 resource = '/swarming/api/v1/client/tasks'
595 self.assertEqual(expected, self.app.get(resource).json)
596
597 # It has a cursor even if there's only one element because of Search API.
598 expected = {
599 u'items': [expected_second],
600 u'limit': 100,
601 u'sort': u'created_ts',
602 u'state': u'all',
603 }
604 actual = self.app.get(resource + '?name=second').json
605 self.assertTrue(actual.pop('cursor'))
606 self.assertEqual(expected, actual)
607
608 expected = {
609 u'cursor': None,
610 u'items': [],
611 u'limit': 100,
612 u'sort': u'created_ts',
613 u'state': u'all',
614 }
615 self.assertEqual(expected, self.app.get(resource + '?&tag=foo:bar').json)
616
617 expected = {
618 u'cursor': None,
619 u'items': [expected_second],
620 u'limit': 100,
621 u'sort': u'created_ts',
622 u'state': u'all',
623 }
624 actual = self.app.get(resource + '?tag=project:yay&tag=commit:pre').json
625 self.assertEqual(expected, actual)
626
627 # Test output from deduped task.
628 for task in expected['items']:
629 response = self.app.get(
630 '/swarming/api/v1/client/task/%s/output/all' % task['id']).json
631 self.assertEqual({u'outputs': [u'r\xc9sult string']}, response)
632
633 def test_tasks_fail(self):
634 self.app.get('/swarming/api/v1/client/tasks?tags=a:b', status=403)
635 self.set_as_privileged_user()
636 # It's 'tag', not 'tags'.
637 self.app.get('/swarming/api/v1/client/tasks?tags=a:b', status=400)
638
639 def test_count(self):
640 now = datetime.datetime(2010, 1, 2, 3, 4, 5, 6)
641
642 # Task in completed state.
643 self.set_as_user()
644 self.mock_now(now)
645 self.client_create_task_raw(
646 name='first', tags=['project:yay', 'commit:post', 'os:Win'],
647 properties=dict(idempotent=False))
648 self.set_as_bot()
649 self.bot_run_task()
650
651 # Task in pending state.
652 self.set_as_user()
653 self.mock_now(now, 60)
654 self.client_create_task_raw(
655 name='second', user='jack@localhost',
656 tags=['project:yay', 'commit:pre', 'os:Win'],
657 properties=dict(idempotent=False))
658
659 self.set_as_privileged_user()
660
661 # Default 24h cutoff interval.
662 result = self.app.get('/swarming/api/v1/client/tasks/count').json
663 self.assertEqual({'count': 2}, result)
664
665 # Test cutoff.
666 result = self.app.get(
667 '/swarming/api/v1/client/tasks/count?interval=30').json
668 self.assertEqual({'count': 1}, result)
669
670 # Test filter by state.
671 result = self.app.get(
672 '/swarming/api/v1/client/tasks/count?state=pending').json
673 self.assertEqual({'count': 1}, result)
674
675 # Test filter by tag.
676 result = self.app.get(
677 '/swarming/api/v1/client/tasks/count?'
678 'tag=project:yay&tag=commit:pre').json
679 self.assertEqual({'count': 1}, result)
680
681 def test_api_bots(self):
682 self.set_as_privileged_user()
683 self.mock_now(datetime.datetime(2010, 1, 2, 3, 4, 5, 6))
684 now_str = lambda: unicode(utils.utcnow().strftime(utils.DATETIME_FORMAT))
685 bot_management.bot_event(
686 event_type='bot_connected', bot_id='id1', external_ip='8.8.4.4',
687 dimensions={'foo': ['bar'], 'id': ['id1']}, state={'ram': 65},
688 version='123456789', quarantined=False, task_id=None, task_name=None)
689 bot1_dict = {
690 u'dimensions': {u'foo': [u'bar'], u'id': [u'id1']},
691 u'external_ip': u'8.8.4.4',
692 u'first_seen_ts': now_str(),
693 u'id': u'id1',
694 u'is_dead': False,
695 u'last_seen_ts': now_str(),
696 u'quarantined': False,
697 u'state': {u'ram': 65},
698 u'task_id': None,
699 u'task_name': None,
700 u'version': u'123456789',
701 }
702
703 actual = self.app.get('/swarming/api/v1/client/bots', status=200).json
704 expected = {
705 u'items': [bot1_dict],
706 u'cursor': None,
707 u'death_timeout': config.settings().bot_death_timeout_secs,
708 u'limit': 1000,
709 u'now': now_str(),
710 }
711 self.assertEqual(expected, actual)
712
713 # Test with limit.
714 actual = self.app.get(
715 '/swarming/api/v1/client/bots?limit=1', status=200).json
716 expected['limit'] = 1
717 self.assertEqual(expected, actual)
718
719 # Advance time to make bot1 dead to test filtering for dead bots.
720 self.mock_now(datetime.datetime(2011, 1, 2, 3, 4, 5, 6))
721 bot1_dict['is_dead'] = True
722 expected['now'] = now_str()
723
724 # Use quarantined bot to check filtering by 'quarantined' flag.
725 bot_management.bot_event(
726 event_type='bot_connected', bot_id='id2', external_ip='8.8.4.4',
727 dimensions={'foo': ['bar'], 'id': ['id2']}, state={'ram': 65},
728 version='123456789', quarantined=True, task_id=None, task_name=None)
729 bot2_dict = {
730 u'dimensions': {u'foo': [u'bar'], u'id': [u'id2']},
731 u'external_ip': u'8.8.4.4',
732 u'first_seen_ts': now_str(),
733 u'id': u'id2',
734 u'is_dead': False,
735 u'last_seen_ts': now_str(),
736 u'quarantined': True,
737 u'state': {u'ram': 65},
738 u'task_id': None,
739 u'task_name': None,
740 u'version': u'123456789',
741 }
742
743 # Test limit + cursor: start the query.
744 actual = self.app.get(
745 '/swarming/api/v1/client/bots?limit=1', status=200).json
746 expected['cursor'] = actual['cursor']
747 expected['items'] = [bot1_dict]
748 self.assertTrue(actual['cursor'])
749 self.assertEqual(expected, actual)
750
751 # Test limit + cursor: continue the query.
752 actual = self.app.get(
753 '/swarming/api/v1/client/bots?limit=1&cursor=%s' % actual['cursor'],
754 status=200).json
755 expected['cursor'] = None
756 expected['items'] = [bot2_dict]
757 self.assertEqual(expected, actual)
758
759 # Filtering by 'quarantined'.
760 actual = self.app.get(
761 '/swarming/api/v1/client/bots?filter=quarantined',
762 status=200).json
763 expected['limit'] = 1000
764 expected['cursor'] = None
765 expected['items'] = [bot2_dict]
766 self.assertEqual(expected, actual)
767
768 # Filtering by 'is_dead'.
769 actual = self.app.get(
770 '/swarming/api/v1/client/bots?filter=is_dead',
771 status=200).json
772 expected['limit'] = 1000
773 expected['cursor'] = None
774 expected['items'] = [bot1_dict]
775 self.assertEqual(expected, actual)
776
777 def test_api_bot(self):
778 self.set_as_privileged_user()
779 now = datetime.datetime(2010, 1, 2, 3, 4, 5, 6)
780 now_str = unicode(now.strftime(utils.DATETIME_FORMAT))
781 self.mock_now(now)
782 bot_management.bot_event(
783 event_type='bot_connected', bot_id='id1', external_ip='8.8.4.4',
784 dimensions={'foo': ['bar'], 'id': ['id1']}, state={'ram': 65},
785 version='123456789', quarantined=False, task_id=None, task_name=None)
786
787 actual = self.app.get('/swarming/api/v1/client/bot/id1', status=200).json
788 expected = {
789 u'dimensions': {u'foo': [u'bar'], u'id': [u'id1']},
790 u'external_ip': u'8.8.4.4',
791 u'first_seen_ts': now_str,
792 u'id': u'id1',
793 u'is_dead': False,
794 u'last_seen_ts': now_str,
795 u'quarantined': False,
796 u'state': {u'ram': 65},
797 u'task_id': None,
798 u'task_name': None,
799 u'version': u'123456789',
800 }
801 self.assertEqual(expected, actual)
802
803 def test_api_bot_delete(self):
804 self.set_as_admin()
805 now = datetime.datetime(2010, 1, 2, 3, 4, 5, 6)
806 self.mock_now(now)
807 state = {
808 'dict': {'random': 'values'},
809 'float': 0.,
810 'list': ['of', 'things'],
811 'str': u'uni',
812 }
813 bot_management.bot_event(
814 event_type='bot_connected', bot_id='id1', external_ip='8.8.4.4',
815 dimensions={'foo': ['bar'], 'id': ['id1']}, state=state,
816 version='123456789', quarantined=False, task_id=None, task_name=None)
817
818 token = self.get_client_token()
819 actual = self.app.delete(
820 '/swarming/api/v1/client/bot/id1',
821 status=200,
822 headers={'X-XSRF-Token': str(token)}).json
823 expected = {
824 u'deleted': True,
825 }
826 self.assertEqual(expected, actual)
827
828 actual = self.app.get('/swarming/api/v1/client/bot/id1', status=404).json
829 expected = {
830 u'error': u'Bot not found',
831 }
832 self.assertEqual(expected, actual)
833
834 def test_api_bot_tasks_empty(self):
835 self.set_as_privileged_user()
836 now = datetime.datetime(2010, 1, 2, 3, 4, 5, 6)
837 self.mock_now(now)
838 actual = self.app.get('/swarming/api/v1/client/bot/id1/tasks').json
839 expected = {
840 u'cursor': None,
841 u'limit': 100,
842 u'now': now.strftime(utils.DATETIME_FORMAT),
843 u'items': [],
844 }
845 self.assertEqual(expected, actual)
846
847 def test_api_bot_tasks(self):
848 self.mock(random, 'getrandbits', lambda _: 0x88)
849 now = datetime.datetime(2010, 1, 2, 3, 4, 5, 6)
850 now_str = unicode(now.strftime(utils.DATETIME_FORMAT))
851 self.mock_now(now)
852
853 self.set_as_bot()
854 self.client_create_task_raw()
855 token, _ = self.get_bot_token()
856 res = self.bot_poll()
857 self.bot_complete_task(token, task_id=res['manifest']['task_id'])
858
859 now_1 = self.mock_now(now, 1)
860 now_1_str = unicode(now_1.strftime(utils.DATETIME_FORMAT))
861 self.mock(random, 'getrandbits', lambda _: 0x55)
862 self.client_create_task_raw(name='ho')
863 token, _ = self.get_bot_token()
864 res = self.bot_poll()
865 self.bot_complete_task(
866 token, exit_code=1, task_id=res['manifest']['task_id'])
867
868 self.set_as_privileged_user()
869 actual = self.app.get('/swarming/api/v1/client/bot/bot1/tasks?limit=1').json
870 expected = {
871 u'limit': 1,
872 u'now': now_1_str,
873 u'items': [
874 {
875 u'abandoned_ts': None,
876 u'bot_dimensions': {u'id': [u'bot1'], u'os': [u'Amiga']},
877 u'bot_id': u'bot1',
878 u'bot_version': self.bot_version,
879 u'children_task_ids': [],
880 u'completed_ts': now_1_str,
881 u'cost_usd': 0.1,
882 u'durations': [0.1],
883 u'exit_codes': [1],
884 u'failure': True,
885 u'id': u'5cee870005511',
886 u'internal_failure': False,
887 u'modified_ts': now_1_str,
888 u'outputs_ref': None,
889 u'server_versions': [u'v1a'],
890 u'started_ts': now_1_str,
891 u'state': task_result.State.COMPLETED,
892 u'try_number': 1,
893 },
894 ],
895 }
896 cursor = actual.pop('cursor')
897 self.assertEqual(expected, actual)
898
899 actual = self.app.get(
900 '/swarming/api/v1/client/bot/bot1/tasks?limit=1&cursor=' + cursor).json
901 expected = {
902 u'cursor': None,
903 u'limit': 1,
904 u'now': now_1_str,
905 u'items': [
906 {
907 u'abandoned_ts': None,
908 u'bot_dimensions': {u'id': [u'bot1'], u'os': [u'Amiga']},
909 u'bot_id': u'bot1',
910 u'bot_version': self.bot_version,
911 u'children_task_ids': [],
912 u'completed_ts': now_str,
913 u'cost_usd': 0.1,
914 u'durations': [0.1],
915 u'exit_codes': [0],
916 u'failure': False,
917 u'id': u'5cee488008811',
918 u'internal_failure': False,
919 u'modified_ts': now_str,
920 u'outputs_ref': None,
921 u'server_versions': [u'v1a'],
922 u'started_ts': now_str,
923 u'state': task_result.State.COMPLETED,
924 u'try_number': 1,
925 },
926 ],
927 }
928 self.assertEqual(expected, actual)
929
930 def test_api_bot_missing(self):
931 self.set_as_privileged_user()
932 self.app.get('/swarming/api/v1/client/bot/unknown', status=404)
933
934 def test_api_server(self):
935 self.set_as_privileged_user()
936 actual = self.app.get('/swarming/api/v1/client/server').json
937 expected = {
938 'bot_version': bot_code.get_bot_version('http://localhost'),
939 }
940 self.assertEqual(expected, actual)
941
942
943 if __name__ == '__main__':
944 if '-v' in sys.argv:
945 unittest.TestCase.maxDiff = None
946 logging.basicConfig(
947 level=logging.DEBUG if '-v' in sys.argv else logging.CRITICAL,
948 format='%(levelname)-7s %(filename)s:%(lineno)3d %(message)s')
949 unittest.main()
OLDNEW
« no previous file with comments | « appengine/swarming/handlers_api.py ('k') | appengine/swarming/handlers_frontend.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698