OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2014 The LUCI Authors. All rights reserved. | 2 # Copyright 2014 The LUCI Authors. All rights reserved. |
3 # Use of this source code is governed under the Apache License, Version 2.0 | 3 # Use of this source code is governed under the Apache License, Version 2.0 |
4 # that can be found in the LICENSE file. | 4 # that can be found in the LICENSE file. |
5 | 5 |
6 """Integration test for the Swarming server, Swarming bot and Swarming client. | 6 """Integration test for the Swarming server, Swarming bot and Swarming client. |
7 | 7 |
8 It starts both a Swarming server and a Swarming bot and triggers tasks with the | 8 It starts both a Swarming server and a Swarming bot and triggers tasks with the |
9 Swarming client to ensure the system works end to end. | 9 Swarming client to ensure the system works end to end. |
10 """ | 10 """ |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
67 | 67 |
68 RESULT_HEY_OUTPUTS_REF = { | 68 RESULT_HEY_OUTPUTS_REF = { |
69 u'isolated': u'5c64883277eb00bceafe3659f182e194cffc5d96', | 69 u'isolated': u'5c64883277eb00bceafe3659f182e194cffc5d96', |
70 u'isolatedserver': u'http://localhost:10050', | 70 u'isolatedserver': u'http://localhost:10050', |
71 u'namespace': u'default-gzip', | 71 u'namespace': u'default-gzip', |
72 u'view_url': | 72 u'view_url': |
73 u'http://localhost:10050/browse?namespace=default-gzip' | 73 u'http://localhost:10050/browse?namespace=default-gzip' |
74 '&hash=5c64883277eb00bceafe3659f182e194cffc5d96', | 74 '&hash=5c64883277eb00bceafe3659f182e194cffc5d96', |
75 } | 75 } |
76 | 76 |
| 77 RESULT_HEY2_ISOLATED_OUT = { |
| 78 u'isolated': u'0616f86b24065a0595e58088925567ae54a8157c', |
| 79 u'isolatedserver': u'http://localhost:10050', |
| 80 u'namespace': u'default-gzip', |
| 81 u'view_url': |
| 82 u'http://localhost:10050/browse?namespace=default-gzip' |
| 83 '&hash=0616f86b24065a0595e58088925567ae54a8157c', |
| 84 } |
| 85 |
| 86 RESULT_HEY2_OUTPUTS_REF = { |
| 87 u'isolated': u'0616f86b24065a0595e58088925567ae54a8157c', |
| 88 u'isolatedserver': u'http://localhost:10050', |
| 89 u'namespace': u'default-gzip', |
| 90 u'view_url': |
| 91 u'http://localhost:10050/browse?namespace=default-gzip' |
| 92 '&hash=0616f86b24065a0595e58088925567ae54a8157c', |
| 93 } |
| 94 |
77 RESULT_SECRET_OUTPUT = { | 95 RESULT_SECRET_OUTPUT = { |
78 u'isolated': u'd2eca4d860e4f1728272f6a736fd1c9ac6e98c4f', | 96 u'isolated': u'd2eca4d860e4f1728272f6a736fd1c9ac6e98c4f', |
79 u'isolatedserver': u'http://localhost:10050', | 97 u'isolatedserver': u'http://localhost:10050', |
80 u'namespace': u'default-gzip', | 98 u'namespace': u'default-gzip', |
81 u'view_url': | 99 u'view_url': |
82 u'http://localhost:10050/browse?namespace=default-gzip' | 100 u'http://localhost:10050/browse?namespace=default-gzip' |
83 '&hash=d2eca4d860e4f1728272f6a736fd1c9ac6e98c4f', | 101 '&hash=d2eca4d860e4f1728272f6a736fd1c9ac6e98c4f', |
84 } | 102 } |
85 | 103 |
86 | 104 |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
129 h, tmp = tempfile.mkstemp(prefix='swarming_smoke_test', suffix='.json') | 147 h, tmp = tempfile.mkstemp(prefix='swarming_smoke_test', suffix='.json') |
130 os.close(h) | 148 os.close(h) |
131 try: | 149 try: |
132 cmd = [ | 150 cmd = [ |
133 '--user', 'joe@localhost', | 151 '--user', 'joe@localhost', |
134 '-d', 'pool', 'default', | 152 '-d', 'pool', 'default', |
135 '--dump-json', tmp, | 153 '--dump-json', tmp, |
136 '--task-name', name, | 154 '--task-name', name, |
137 '-I', self._isolate_server, | 155 '-I', self._isolate_server, |
138 '--namespace', 'default-gzip', | 156 '--namespace', 'default-gzip', |
139 isolated_hash, | 157 '-s', isolated_hash, |
140 ] | 158 ] |
141 if extra: | 159 if extra: |
142 cmd.extend(extra) | 160 cmd.extend(extra) |
143 assert not self._run('trigger', cmd) | 161 assert not self._run('trigger', cmd) |
144 with open(tmp, 'rb') as f: | 162 with open(tmp, 'rb') as f: |
145 data = json.load(f) | 163 data = json.load(f) |
146 task_id = data['tasks'].popitem()[1]['task_id'] | 164 task_id = data['tasks'].popitem()[1]['task_id'] |
147 logging.debug('task_id = %s', task_id) | 165 logging.debug('task_id = %s', task_id) |
148 return task_id | 166 return task_id |
149 finally: | 167 finally: |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
267 bot = None | 285 bot = None |
268 | 286 |
269 @classmethod | 287 @classmethod |
270 def setUpClass(cls): | 288 def setUpClass(cls): |
271 cls.dimensions = os_utilities.get_dimensions() | 289 cls.dimensions = os_utilities.get_dimensions() |
272 | 290 |
273 def setUp(self): | 291 def setUp(self): |
274 super(Test, self).setUp() | 292 super(Test, self).setUp() |
275 # Reset the bot's cache at the start of each task, so that the cache reuse | 293 # Reset the bot's cache at the start of each task, so that the cache reuse |
276 # data becomes deterministic. | 294 # data becomes deterministic. |
| 295 # Main caveat is 'isolated_upload' as the isolate server is not cleared. |
277 self.bot.wipe_cache() | 296 self.bot.wipe_cache() |
278 | 297 |
279 def gen_expected(self, **kwargs): | 298 def gen_expected(self, **kwargs): |
280 return gen_expected(bot_dimensions=self.dimensions, **kwargs) | 299 return gen_expected(bot_dimensions=self.dimensions, **kwargs) |
281 | 300 |
282 def test_raw_bytes(self): | 301 def test_raw_bytes(self): |
283 # A string of a letter 'A', UTF-8 BOM then UTF-16 BOM then UTF-EDBCDIC then | 302 # A string of a letter 'A', UTF-8 BOM then UTF-16 BOM then UTF-EDBCDIC then |
284 # invalid UTF-8 and the letter 'B'. It is double escaped so it can be passed | 303 # invalid UTF-8 and the letter 'B'. It is double escaped so it can be passed |
285 # down the shell. | 304 # down the shell. |
286 invalid_bytes = 'A\\xEF\\xBB\\xBF\\xFE\\xFF\\xDD\\x73\\x66\\x73\\xc3\\x28B' | 305 invalid_bytes = 'A\\xEF\\xBB\\xBF\\xFE\\xFF\\xDD\\x73\\x66\\x73\\xc3\\x28B' |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
401 'print(\'hi\')', | 420 'print(\'hi\')', |
402 'with open(os.path.join(sys.argv[1], \'result.txt\'), \'wb\') as f:', | 421 'with open(os.path.join(sys.argv[1], \'result.txt\'), \'wb\') as f:', |
403 ' f.write(\'hey\')')) | 422 ' f.write(\'hey\')')) |
404 expected_summary = self.gen_expected( | 423 expected_summary = self.gen_expected( |
405 name=u'isolated_task', | 424 name=u'isolated_task', |
406 isolated_out=RESULT_HEY_ISOLATED_OUT, | 425 isolated_out=RESULT_HEY_ISOLATED_OUT, |
407 performance_stats={ | 426 performance_stats={ |
408 u'isolated_download': { | 427 u'isolated_download': { |
409 u'initial_number_items': u'0', | 428 u'initial_number_items': u'0', |
410 u'initial_size': u'0', | 429 u'initial_size': u'0', |
411 u'items_cold': [112, 200], | 430 u'items_cold': sorted([len(hello_world), 200]), |
412 u'items_hot': [], | 431 u'items_hot': [], |
413 }, | 432 }, |
414 u'isolated_upload': { | 433 u'isolated_upload': { |
415 u'items_cold': [3, 118], | 434 u'items_cold': [3, 118], |
416 u'items_hot': [], | 435 u'items_hot': [], |
417 }, | 436 }, |
418 }, | 437 }, |
419 outputs=[u'hi\n'], | 438 outputs=[u'hi\n'], |
420 outputs_ref=RESULT_HEY_OUTPUTS_REF) | 439 outputs_ref=RESULT_HEY_OUTPUTS_REF) |
421 expected_files = {os.path.join('0', 'result.txt'): 'hey'} | 440 expected_files = {os.path.join('0', 'result.txt'): 'hey'} |
422 self._run_isolated( | 441 self._run_isolated( |
423 hello_world, 'isolated_task', ['--', '${ISOLATED_OUTDIR}'], | 442 hello_world, 'isolated_task', ['--', '${ISOLATED_OUTDIR}'], |
424 expected_summary, expected_files) | 443 expected_summary, expected_files) |
425 | 444 |
| 445 def test_isolated_command(self): |
| 446 # Command is specified in Swarming task, still with isolated file. |
| 447 hello_world = '\n'.join(( |
| 448 'import os', |
| 449 'import sys', |
| 450 'print(\'hi\')', |
| 451 'with open(os.path.join(sys.argv[1], \'result.txt\'), \'wb\') as f:', |
| 452 ' f.write(\'hey2\')')) |
| 453 expected_summary = self.gen_expected( |
| 454 name=u'separate_cmd', |
| 455 isolated_out=RESULT_HEY2_ISOLATED_OUT, |
| 456 performance_stats={ |
| 457 u'isolated_download': { |
| 458 u'initial_number_items': u'0', |
| 459 u'initial_size': u'0', |
| 460 u'items_cold': sorted([len(hello_world), 157]), |
| 461 u'items_hot': [], |
| 462 }, |
| 463 u'isolated_upload': { |
| 464 u'items_cold': [4, 118], |
| 465 u'items_hot': [], |
| 466 }, |
| 467 }, |
| 468 outputs=[u'hi\n'], |
| 469 outputs_ref=RESULT_HEY2_OUTPUTS_REF) |
| 470 expected_files = {os.path.join('0', 'result.txt'): 'hey2'} |
| 471 self._run_isolated( |
| 472 hello_world, 'separate_cmd', |
| 473 ['--raw-cmd', '--', 'python', 'hello_world.py', '${ISOLATED_OUTDIR}'], |
| 474 expected_summary, expected_files, |
| 475 isolated_content={'variables': {'files': ['hello_world.py']}}) |
| 476 |
426 def test_isolated_hard_timeout(self): | 477 def test_isolated_hard_timeout(self): |
427 # Make an isolated file, archive it, have it time out. Similar to | 478 # Make an isolated file, archive it, have it time out. Similar to |
428 # test_hard_timeout. The script doesn't handle signal so it failed the grace | 479 # test_hard_timeout. The script doesn't handle signal so it failed the grace |
429 # period. | 480 # period. |
430 hello_world = '\n'.join(( | 481 hello_world = '\n'.join(( |
431 'import os', | 482 'import os', |
432 'import sys', | 483 'import sys', |
433 'import time', | 484 'import time', |
434 'sys.stdout.write(\'hi\\n\')', | 485 'sys.stdout.write(\'hi\\n\')', |
435 'sys.stdout.flush()', | 486 'sys.stdout.flush()', |
436 'time.sleep(120)', | 487 'time.sleep(120)', |
437 'with open(os.path.join(sys.argv[1], \'result.txt\'), \'wb\') as f:', | 488 'with open(os.path.join(sys.argv[1], \'result.txt\'), \'wb\') as f:', |
438 ' f.write(\'hey\')')) | 489 ' f.write(\'hey\')')) |
439 expected_summary = self.gen_expected( | 490 expected_summary = self.gen_expected( |
440 name=u'isolated_hard_timeout', | 491 name=u'isolated_hard_timeout', |
441 exit_codes=[SIGNAL_TERM], | 492 exit_codes=[SIGNAL_TERM], |
442 failure=True, | 493 failure=True, |
443 performance_stats={ | 494 performance_stats={ |
444 u'isolated_download': { | 495 u'isolated_download': { |
445 u'initial_number_items': u'0', | 496 u'initial_number_items': u'0', |
446 u'initial_size': u'0', | 497 u'initial_size': u'0', |
447 u'items_cold': [172, 200], | 498 u'items_cold': sorted([len(hello_world), 200]), |
448 u'items_hot': [], | 499 u'items_hot': [], |
449 }, | 500 }, |
450 u'isolated_upload': { | 501 u'isolated_upload': { |
451 u'items_cold': [], | 502 u'items_cold': [], |
452 u'items_hot': [], | 503 u'items_hot': [], |
453 }, | 504 }, |
454 }, | 505 }, |
455 state=0x40) # task_result.State.TIMED_OUT | 506 state=0x40) # task_result.State.TIMED_OUT |
456 # Hard timeout is enforced by run_isolated, I/O timeout by task_runner. | 507 # Hard timeout is enforced by run_isolated, I/O timeout by task_runner. |
457 self._run_isolated( | 508 self._run_isolated( |
(...skipping 25 matching lines...) Expand all Loading... |
483 ' print(\'ioerror\')', | 534 ' print(\'ioerror\')', |
484 'with open(os.path.join(sys.argv[1], \'result.txt\'), \'wb\') as f:', | 535 'with open(os.path.join(sys.argv[1], \'result.txt\'), \'wb\') as f:', |
485 ' f.write(\'hey\')')) | 536 ' f.write(\'hey\')')) |
486 expected_summary = self.gen_expected( | 537 expected_summary = self.gen_expected( |
487 name=u'isolated_hard_timeout_grace', | 538 name=u'isolated_hard_timeout_grace', |
488 isolated_out=RESULT_HEY_ISOLATED_OUT, | 539 isolated_out=RESULT_HEY_ISOLATED_OUT, |
489 performance_stats={ | 540 performance_stats={ |
490 u'isolated_download': { | 541 u'isolated_download': { |
491 u'initial_number_items': u'0', | 542 u'initial_number_items': u'0', |
492 u'initial_size': u'0', | 543 u'initial_size': u'0', |
493 u'items_cold': [200, 407], | 544 u'items_cold': sorted([200, len(hello_world)]), |
494 u'items_hot': [], | 545 u'items_hot': [], |
495 }, | 546 }, |
496 u'isolated_upload': { | 547 u'isolated_upload': { |
497 u'items_cold': [], | 548 u'items_cold': [], |
498 u'items_hot': [3, 118], | 549 u'items_hot': [3, 118], |
499 }, | 550 }, |
500 }, | 551 }, |
501 outputs=[u'hi\ngot signal 15\n'], | 552 outputs=[u'hi\ngot signal 15\n'], |
502 outputs_ref=RESULT_HEY_OUTPUTS_REF, | 553 outputs_ref=RESULT_HEY_OUTPUTS_REF, |
503 failure=True, | 554 failure=True, |
504 state=0x40) # task_result.State.TIMED_OUT | 555 state=0x40) # task_result.State.TIMED_OUT |
505 expected_files = {os.path.join('0', 'result.txt'): 'hey'} | 556 expected_files = {os.path.join('0', 'result.txt'): 'hey'} |
506 # Hard timeout is enforced by run_isolated, I/O timeout by task_runner. | 557 # Hard timeout is enforced by run_isolated, I/O timeout by task_runner. |
507 self._run_isolated( | 558 self._run_isolated( |
508 hello_world, 'isolated_hard_timeout_grace', | 559 hello_world, 'isolated_hard_timeout_grace', |
509 ['--hard-timeout', '1', '--', '${ISOLATED_OUTDIR}'], | 560 ['--hard-timeout', '1', '--', '${ISOLATED_OUTDIR}'], |
510 expected_summary, expected_files) | 561 expected_summary, expected_files) |
511 | 562 |
512 def test_idempotent_reuse(self): | 563 def test_idempotent_reuse(self): |
513 hello_world = 'print "hi"\n' | 564 hello_world = 'print "hi"\n' |
514 expected_summary = self.gen_expected( | 565 expected_summary = self.gen_expected( |
515 name=u'idempotent_reuse', | 566 name=u'idempotent_reuse', |
516 performance_stats={ | 567 performance_stats={ |
517 u'isolated_download': { | 568 u'isolated_download': { |
518 u'initial_number_items': u'0', | 569 u'initial_number_items': u'0', |
519 u'initial_size': u'0', | 570 u'initial_size': u'0', |
520 u'items_cold': [11, 199], | 571 u'items_cold': sorted([len(hello_world), 199]), |
521 u'items_hot': [], | 572 u'items_hot': [], |
522 }, | 573 }, |
523 u'isolated_upload': { | 574 u'isolated_upload': { |
524 u'items_cold': [], | 575 u'items_cold': [], |
525 u'items_hot': [], | 576 u'items_hot': [], |
526 }, | 577 }, |
527 }, | 578 }, |
528 properties_hash = | 579 properties_hash = |
529 u'082928de84d0a65839d227dcea2f5a947898929c77c1602b68c46d7d4588c1f5', | 580 u'082928de84d0a65839d227dcea2f5a947898929c77c1602b68c46d7d4588c1f5', |
530 ) | 581 ) |
(...skipping 23 matching lines...) Expand all Loading... |
554 with open(os.path.join(sys.argv[1], 'sekret'), 'w') as f: | 605 with open(os.path.join(sys.argv[1], 'sekret'), 'w') as f: |
555 print >> f, data['swarming']['secret_bytes'].decode('base64') | 606 print >> f, data['swarming']['secret_bytes'].decode('base64') |
556 """) | 607 """) |
557 expected_summary = self.gen_expected( | 608 expected_summary = self.gen_expected( |
558 name=u'secret_bytes', | 609 name=u'secret_bytes', |
559 isolated_out=RESULT_SECRET_OUTPUT, | 610 isolated_out=RESULT_SECRET_OUTPUT, |
560 performance_stats={ | 611 performance_stats={ |
561 u'isolated_download': { | 612 u'isolated_download': { |
562 u'initial_number_items': u'0', | 613 u'initial_number_items': u'0', |
563 u'initial_size': u'0', | 614 u'initial_size': u'0', |
564 u'items_cold': [200, 241], | 615 u'items_cold': sorted([200, len(hello_world)]), |
565 u'items_hot': [], | 616 u'items_hot': [], |
566 }, | 617 }, |
567 u'isolated_upload': { | 618 u'isolated_upload': { |
568 u'items_cold': [7, 114], | 619 u'items_cold': [7, 114], |
569 u'items_hot': [], | 620 u'items_hot': [], |
570 }, | 621 }, |
571 }, | 622 }, |
572 outputs_ref=RESULT_SECRET_OUTPUT, | 623 outputs_ref=RESULT_SECRET_OUTPUT, |
573 ) | 624 ) |
574 h, tmp = tempfile.mkstemp(prefix='swarming_smoke_test_secret') | 625 h, tmp = tempfile.mkstemp(prefix='swarming_smoke_test_secret') |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
644 }, | 695 }, |
645 }, | 696 }, |
646 ) | 697 ) |
647 self._run_isolated( | 698 self._run_isolated( |
648 script, 'cache_second', | 699 script, 'cache_second', |
649 ['--named-cache', 'fuu', 'p/b', '--', '${ISOLATED_OUTDIR}/yo'], | 700 ['--named-cache', 'fuu', 'p/b', '--', '${ISOLATED_OUTDIR}/yo'], |
650 expected_summary, | 701 expected_summary, |
651 {'0/yo': 'Yo!'}) | 702 {'0/yo': 'Yo!'}) |
652 | 703 |
653 def _run_isolated(self, hello_world, name, args, expected_summary, | 704 def _run_isolated(self, hello_world, name, args, expected_summary, |
654 expected_files, deduped=False): | 705 expected_files, deduped=False, isolated_content=None): |
| 706 """Runs hello_world.py as an isolated file.""" |
655 # Shared code for all test_isolated_* test cases. | 707 # Shared code for all test_isolated_* test cases. |
656 tmpdir = tempfile.mkdtemp(prefix='swarming_smoke') | 708 tmpdir = tempfile.mkdtemp(prefix='swarming_smoke') |
657 try: | 709 try: |
658 isolate_path = os.path.join(tmpdir, 'i.isolate') | 710 isolate_path = os.path.join(tmpdir, 'i.isolate') |
659 isolated_path = os.path.join(tmpdir, 'i.isolated') | 711 isolated_path = os.path.join(tmpdir, 'i.isolated') |
660 with open(isolate_path, 'wb') as f: | 712 with open(isolate_path, 'wb') as f: |
661 json.dump(ISOLATE_HELLO_WORLD, f) | 713 json.dump(isolated_content or ISOLATE_HELLO_WORLD, f) |
662 with open(os.path.join(tmpdir, 'hello_world.py'), 'wb') as f: | 714 with open(os.path.join(tmpdir, 'hello_world.py'), 'wb') as f: |
663 f.write(hello_world) | 715 f.write(hello_world) |
664 isolated_hash = self.client.isolate(isolate_path, isolated_path) | 716 isolated_hash = self.client.isolate(isolate_path, isolated_path) |
665 task_id = self.client.task_trigger_isolated( | 717 task_id = self.client.task_trigger_isolated( |
666 name, isolated_hash, extra=args) | 718 name, isolated_hash, extra=args) |
667 actual_summary, actual_files = self.client.task_collect(task_id) | 719 actual_summary, actual_files = self.client.task_collect(task_id) |
668 self.assertResults(expected_summary, actual_summary, deduped=deduped) | 720 self.assertResults(expected_summary, actual_summary, deduped=deduped) |
669 actual_files.pop('summary.json') | 721 actual_files.pop('summary.json') |
670 self.assertEqual(expected_files, actual_files) | 722 self.assertEqual(expected_files, actual_files) |
671 return task_id | 723 return task_id |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
821 if bot is not None and bot.poll() is None: | 873 if bot is not None and bot.poll() is None: |
822 bot.kill() | 874 bot.kill() |
823 bot.wait() | 875 bot.wait() |
824 finally: | 876 finally: |
825 cleanup(bot, client, servers, failed or verbose, leak) | 877 cleanup(bot, client, servers, failed or verbose, leak) |
826 return int(failed) | 878 return int(failed) |
827 | 879 |
828 | 880 |
829 if __name__ == '__main__': | 881 if __name__ == '__main__': |
830 sys.exit(main()) | 882 sys.exit(main()) |
OLD | NEW |