OLD | NEW |
---|---|
1 # Copyright 2015 The LUCI Authors. All rights reserved. | 1 # Copyright 2015 The LUCI Authors. All rights reserved. |
2 # Use of this source code is governed under the Apache License, Version 2.0 | 2 # Use of this source code is governed under the Apache License, Version 2.0 |
3 # that can be found in the LICENSE file. | 3 # that can be found in the LICENSE file. |
4 | 4 |
5 """Internal bot API handlers.""" | 5 """Internal bot API handlers.""" |
6 | 6 |
7 import base64 | 7 import base64 |
8 import json | 8 import json |
9 import logging | 9 import logging |
10 import re | 10 import re |
(...skipping 557 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
568 | 568 |
569 class BotTaskUpdateHandler(_BotApiHandler): | 569 class BotTaskUpdateHandler(_BotApiHandler): |
570 """Receives updates from a Bot for a task. | 570 """Receives updates from a Bot for a task. |
571 | 571 |
572 The handler verifies packets are processed in order and will refuse | 572 The handler verifies packets are processed in order and will refuse |
573 out-of-order packets. | 573 out-of-order packets. |
574 """ | 574 """ |
575 ACCEPTED_KEYS = { | 575 ACCEPTED_KEYS = { |
576 u'bot_overhead', u'cipd_stats', u'cost_usd', u'duration', u'exit_code', | 576 u'bot_overhead', u'cipd_stats', u'cost_usd', u'duration', u'exit_code', |
577 u'hard_timeout', u'id', u'io_timeout', u'isolated_stats', u'output', | 577 u'hard_timeout', u'id', u'io_timeout', u'isolated_stats', u'output', |
578 u'output_chunk_start', u'outputs_ref', u'task_id', | 578 u'output_chunk_start', u'outputs_ref', u'cipd_pins', u'task_id', |
M-A Ruel
2016/08/24 02:13:44
keep ordered lists ordered
iannucci
2016/08/24 02:31:57
Done.
| |
579 } | 579 } |
580 REQUIRED_KEYS = {u'id', u'task_id'} | 580 REQUIRED_KEYS = {u'id', u'task_id'} |
581 | 581 |
582 @auth.public # auth happens in bot_auth.validate_bot_id_and_fetch_config() | 582 @auth.public # auth happens in bot_auth.validate_bot_id_and_fetch_config() |
583 def post(self, task_id=None): | 583 def post(self, task_id=None): |
584 # Unlike handshake and poll, we do not accept invalid keys here. This code | 584 # Unlike handshake and poll, we do not accept invalid keys here. This code |
585 # path is much more strict. | 585 # path is much more strict. |
586 request = self.parse_body() | 586 request = self.parse_body() |
587 msg = log_unexpected_subset_keys( | 587 msg = log_unexpected_subset_keys( |
588 self.ACCEPTED_KEYS, self.REQUIRED_KEYS, request, self.request, 'bot', | 588 self.ACCEPTED_KEYS, self.REQUIRED_KEYS, request, self.request, 'bot', |
(...skipping 12 matching lines...) Expand all Loading... | |
601 cost_usd = request.get('cost_usd', 0) | 601 cost_usd = request.get('cost_usd', 0) |
602 duration = request.get('duration') | 602 duration = request.get('duration') |
603 exit_code = request.get('exit_code') | 603 exit_code = request.get('exit_code') |
604 hard_timeout = request.get('hard_timeout') | 604 hard_timeout = request.get('hard_timeout') |
605 io_timeout = request.get('io_timeout') | 605 io_timeout = request.get('io_timeout') |
606 isolated_stats = request.get('isolated_stats') | 606 isolated_stats = request.get('isolated_stats') |
607 cipd_stats = request.get('cipd_stats') | 607 cipd_stats = request.get('cipd_stats') |
608 output = request.get('output') | 608 output = request.get('output') |
609 output_chunk_start = request.get('output_chunk_start') | 609 output_chunk_start = request.get('output_chunk_start') |
610 outputs_ref = request.get('outputs_ref') | 610 outputs_ref = request.get('outputs_ref') |
611 cipd_pins = request.get('cipd_pins') | |
611 | 612 |
612 if (isolated_stats or cipd_stats) and bot_overhead is None: | 613 if (isolated_stats or cipd_stats) and bot_overhead is None: |
613 ereporter2.log_request( | 614 ereporter2.log_request( |
614 request=self.request, | 615 request=self.request, |
615 source='server', | 616 source='server', |
616 category='task_failure', | 617 category='task_failure', |
617 message='Failed to update task: %s' % task_id) | 618 message='Failed to update task: %s' % task_id) |
618 self.abort_with_error( | 619 self.abort_with_error( |
619 400, | 620 400, |
620 error='isolated_stats and cipd_stats require bot_overhead to be set' | 621 error='isolated_stats and cipd_stats require bot_overhead to be set' |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
654 logging.error('Failed to decode output\n%s\n%r', e, output) | 655 logging.error('Failed to decode output\n%s\n%r', e, output) |
655 output = output.encode('ascii', 'replace') | 656 output = output.encode('ascii', 'replace') |
656 except TypeError as e: | 657 except TypeError as e: |
657 # Save the output as-is instead. The error will be logged in ereporter2 | 658 # Save the output as-is instead. The error will be logged in ereporter2 |
658 # and returning a HTTP 500 would only force the bot to stay in a retry | 659 # and returning a HTTP 500 would only force the bot to stay in a retry |
659 # loop. | 660 # loop. |
660 logging.error('Failed to decode output\n%s\n%r', e, output) | 661 logging.error('Failed to decode output\n%s\n%r', e, output) |
661 if outputs_ref: | 662 if outputs_ref: |
662 outputs_ref = task_request.FilesRef(**outputs_ref) | 663 outputs_ref = task_request.FilesRef(**outputs_ref) |
663 | 664 |
665 if cipd_pins: | |
666 cipd_pins = [task_request.CipdPackage(**pin) for pin in cipd_pins] | |
667 | |
664 try: | 668 try: |
665 state = task_scheduler.bot_update_task( | 669 state = task_scheduler.bot_update_task( |
666 run_result_key=run_result_key, | 670 run_result_key=run_result_key, |
667 bot_id=bot_id, | 671 bot_id=bot_id, |
668 output=output, | 672 output=output, |
669 output_chunk_start=output_chunk_start, | 673 output_chunk_start=output_chunk_start, |
670 exit_code=exit_code, | 674 exit_code=exit_code, |
671 duration=duration, | 675 duration=duration, |
672 hard_timeout=hard_timeout, | 676 hard_timeout=hard_timeout, |
673 io_timeout=io_timeout, | 677 io_timeout=io_timeout, |
674 cost_usd=cost_usd, | 678 cost_usd=cost_usd, |
675 outputs_ref=outputs_ref, | 679 outputs_ref=outputs_ref, |
680 cipd_pins=cipd_pins, | |
676 performance_stats=performance_stats) | 681 performance_stats=performance_stats) |
677 if not state: | 682 if not state: |
678 logging.info('Failed to update, please retry') | 683 logging.info('Failed to update, please retry') |
679 self.abort_with_error(500, error='Failed to update, please retry') | 684 self.abort_with_error(500, error='Failed to update, please retry') |
680 | 685 |
681 if state in (task_result.State.COMPLETED, task_result.State.TIMED_OUT): | 686 if state in (task_result.State.COMPLETED, task_result.State.TIMED_OUT): |
682 action = 'task_completed' | 687 action = 'task_completed' |
683 elif state == task_result.State.CANCELED: | 688 elif state == task_result.State.CANCELED: |
684 action = 'task_canceled' | 689 action = 'task_canceled' |
685 else: | 690 else: |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
781 ('/swarming/api/v1/bot/poll', BotPollHandler), | 786 ('/swarming/api/v1/bot/poll', BotPollHandler), |
782 ('/swarming/api/v1/bot/server_ping', ServerPingHandler), | 787 ('/swarming/api/v1/bot/server_ping', ServerPingHandler), |
783 ('/swarming/api/v1/bot/task_update', BotTaskUpdateHandler), | 788 ('/swarming/api/v1/bot/task_update', BotTaskUpdateHandler), |
784 ('/swarming/api/v1/bot/task_update/<task_id:[a-f0-9]+>', | 789 ('/swarming/api/v1/bot/task_update/<task_id:[a-f0-9]+>', |
785 BotTaskUpdateHandler), | 790 BotTaskUpdateHandler), |
786 ('/swarming/api/v1/bot/task_error', BotTaskErrorHandler), | 791 ('/swarming/api/v1/bot/task_error', BotTaskErrorHandler), |
787 ('/swarming/api/v1/bot/task_error/<task_id:[a-f0-9]+>', | 792 ('/swarming/api/v1/bot/task_error/<task_id:[a-f0-9]+>', |
788 BotTaskErrorHandler), | 793 BotTaskErrorHandler), |
789 ] | 794 ] |
790 return [webapp2.Route(*i) for i in routes] | 795 return [webapp2.Route(*i) for i in routes] |
OLD | NEW |